在工作中,我们更多操作的是一个表的对象,所以我们对SOQL的使用很多。但是有时候,我们需要对几个表进行查询操作,类似salesforce的全局搜索功能,这时,使用SOQL没法满足功能了,我们就需要使用SOSL.其实不只是多个表检索可以使用SOSL,如果针对某个字段进行高级的检索也可以使用SOSL。

背景:Account表中的Name字段,存储了以下的数据
1.上海电信
2.上海-电信
3.上海(电信)
4.电信-上海
5.电信上海
6.电信(上海)
7.上海xx电信
8.海上信电
9.海信电上。

当用户搜索上海电信时,需要将1,2,3,4,5,6,7检索出来,8,9排除。使用正常的SOQL语句实现起来难度较大,这种情况可以考虑使用SOSL,尽管SOSL不一定将所有的结果返回,但是可以返回大部分情况。

一.SOSL简单介绍

SOSL全称为Salesforce Object Search Language。SOSL查询可以在以下环境使用:
Search()的调用/apex语句/Visualforce的Controller和getter方法里面/Eclipse的Schema Explorer(没有测试成功)

SOSL支持对多个objects同时查询text/email/phone类型字段的数据,SOSL可以查询标准的对象以及自定义的对象。当然SOSL不是所有的对象或者字段都支持搜索,以下情况下是不允许搜索的:

1.sObject不允许搜索:创建sObject或者自带标准sObject,只有允许搜索的sObject才可以使用SOSL,判断一个sObject是否可以搜索,可以使用Schema的DescribeSObjectResult类来判断,如果希望一个自定义对象允许搜索,只需要把allow search勾选即可。

2.Number, date, or checkbox 这几种类型是不支持使用SOSL的,如果需要搜索这几种,需要使用SOQL。

3.Textarea 类型,除非SearchGroup选择的是ALL FIELDS,否则不支持搜索。

4.关联到对象上的Attachment数据不允许搜索。

除了上述的使用限制以外,其实SOSL还有一些其他的限制,比如SOSL语句长度不能超过20000个字符,超过的话会报error。其他的限制详看开发文档。

SOSL在apex中调用时,search query使用的是单引号'',在search调用中使用的是{},下面的demo以及代码均以apex写法为主。

二.SOSL的语法

SOSL的语法如下:

1.FIND:搜索指定的文本,如果searchQuery超过10000,则无结果返回,如果超过4000,所有的逻辑都将移除。SearchQuery除了纯文本以外,还可以使用*或者?的通配符进行匹配,*代表后面的所有位为任意内容,?代表后面的一位为任意内容。
        比如FIND 'z*o'会将所有zero,zoo数据查询出来,但是'z?o'会过滤掉zero对应的数据,只会查出zoo对应的数据。
        searchQuery也可以使用与或等操作,详情查看SOQL与SOSL开发文档。

2.IN:设置查询组--即查询的类型,SearchGroup包含四种固定的类型:ALL FIELDS/EMAIL FIELDS/NAME FIELDS/PHONE FIELDS。如果想要在Name或者Email/Phone类型中进行搜索,则可以设置指定的类型,否则设置ALL FIELDS,默认查询组为ALL FIELDS。

3.RETURNING:此部分作为搜索返回结果的处理部分,显得尤为重要,RETURNING可以返回一个对象,也可以返回多个对象,多个对象通过逗号分隔;对象中可以返回多个字段,也可以在返回的结果中添加自定义的逻辑。比如我们希望搜索Opportunity和Account的Name中包含zero中的数据,其中,要求Opportunity中的数据按照创建日期正序排列,只查询十条,并且只搜索Name和StageName字段,Account要求Name除了含有zero以外还需要包含zhang,并且最多只查询1条,这种情况下RETURNING就可以发挥神奇的作用了。
        eg:FIND 'zero' IN ALL FIELDS RETURNING account(where Name like '%zhang%' limit 1),Opportunity(Name,StageName order by createddate asc limit 10)
        备注(如果使用order by,object的field不能为空,如果上述内容修改成以下写法便是错误的)
        FIND 'zero' IN ALL FIELDS RETURNING account(where Name like '%zhang%' limit 1),Opportunity(order by createddate asc limit 10)

其他部分自行查看开发文档。

三.SOSL应用

封装了一个SOSL工具类,用户可以根据需要查询的关键字,设置返回的结果的格式来返回需要的数据,如果不设置returning的field的内容,则默认返回所有可以访问的字段,否则返回指定字段:

 public with sharing class SOSLController {

     public class RetrieveWrapper {
//keyword:used to retrieve this
public String retrieveKeyword{get;set;}
//search group: values :(ALL FIELDS/EMAIL FIELDS/NAME FIELDS/PHONE FIELDS/SIDEBAR FIELDS)
public String searchGroup{get;set;}
//obj api name -> field eg: account->[Name,Site]
public Map<String,List<String>> objName2FieldsMap{get;set;}
//obj api name -> condition eg : account -> where name like 'test%' order by name asc limit 10 offset 1
public Map<String,String> objName2QueryConditionMap{get;set;}
} public class SearchResultWrapper {
//sobject api name eg:Account
public String objName{get;set;}
//sObject Field Name => Value
public Map<String,Object> objFieldName2Value{get;set;}
} public static List<SearchResultWrapper> search(RetrieveWrapper wrapper) {
String retrieveSQL = buildRetrieveSQL(wrapper);
List<SearchResultWrapper> searchResultWrappers = new List<SearchResultWrapper>();
if(retrieveSQL == null || (!retrieveSQL.contains('FIND'))) {
return null;
}
System.debug(LoggingLevel.INFO, '*** retrieveSQL: ' + retrieveSQL);
List<List<sObject>> searchResults = search.query(retrieveSQL);
if(searchResults.size() > 0) {
for(List<sObject> objs : searchResults) {
String objName;
if(objs.size() > 0) {
String objId = objs.get(0).Id;
objName = getAPINameByObjId(objId);
}
List<String> retrieveFields;
if(objName != null) { if(wrapper.objName2FieldsMap != null && wrapper.objName2FieldsMap.get(objName) != null) {
retrieveFields = wrapper.objName2FieldsMap.get(objName);
} else {
retrieveFields = getAvailableFields(objName);
}
} for(sObject obj : objs) {
SearchResultWrapper resultWrapper = new SearchResultWrapper();
resultWrapper.objName = objName;
Map<String,Object> fieldValueMap = new Map<String,Object>();
for(String field : retrieveFields){
if(obj.get(field) != null) {
fieldValueMap.put(field, obj.get(field));
}
}
resultWrapper.objFieldName2Value = fieldValueMap;
searchResultWrappers.add(resultWrapper);
}
}
}
return searchResultWrappers;
} private static String buildRetrieveSQL(RetrieveWrapper wrapper) {
String fetchSQL;
if(wrapper.retrieveKeyword != null && wrapper.retrieveKeyword.trim() != '') {
String keyword = '\'' + wrapper.retrieveKeyword + '\'';
fetchSQL = 'FIND ' + keyword; if(wrapper.searchGroup != null) {
fetchSQL += ' IN ' + wrapper.searchGroup;
} else {
fetchSQL += ' IN ALL FIELDS';
} if(wrapper.objName2FieldsMap != null) {
List<String> objToFieldsList = new List<String>();
for(String key : wrapper.objName2FieldsMap.keySet()) {
String objName = key;
String fieldStr;
if(wrapper.objName2FieldsMap != null && wrapper.objName2FieldsMap.get(objName) != null) {
fieldStr = String.join(wrapper.objName2FieldsMap.get(objName),',');
} else {
fieldStr = String.join(getAvailableFields(objName), ',');
}
String filterStr;
if(wrapper.objName2QueryConditionMap != null) {
filterStr = wrapper.objName2QueryConditionMap.get(objName);
} if(String.isNotEmpty(filterStr)){
fieldStr = '(' + fieldStr + ' WHERE ' + filterStr +')';
}
else{
if(fieldStr != null) {
fieldStr = '(' + fieldStr +')';
}
}
if(fieldStr != null) {
objName = key + fieldStr;
} else {
objName = key;
} objToFieldsList.add(objName);
} if(objToFieldsList.size() > 0) {
fetchSQL += ' RETURNING ' + String.join(objToFieldsList, ',');
}
}
}
return fetchSQL;
} private static String getAPINameByObjId(String objId) {
String objPrefix = objId.left(3);
return objId2APIName.get(objPrefix);
} private static Map<String,String> objId2APIName {
get {
if(objId2APIName == null) {
objId2APIName = new Map<String,String>();
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
for(String objectName : gd.keySet()) {
Schema.SObjectType objectType = gd.get(objectName);
String prefix = objectType.getDescribe().getKeyPrefix();
if(prefix != null) {
objId2APIName.put(prefix, objectName);
}
}
}
return objId2APIName;
}
set;
} private static List<String> getAvailableFields(String objName) {
List<String> availableFields = new List<String>();
List<Schema.DescribeSObjectResult> objDescribes = Schema.describeSObjects(new List<String>{objName});
Schema.DescribeSObjectResult objDescribe;
if(objDescribes != null && objDescribes.size() > 0) {
objDescribe = objDescribes.get(0);
} else {
return null;
}
Map<String,SObjectField> sObjectFieldMaps = objDescribe.fields.getMap();
for(String objField : sObjectFieldMaps.keySet()) {
SObjectField field = sObjectFieldMaps.get(objField);
DescribeFieldResult fieldResult = field.getDescribe();
if(fieldResult.isAccessible()) {
availableFields.add(fieldResult.getName());
}
}
return availableFields;
}
}

调用代码如下:

SOSLController.RetrieveWrapper wrapper = new SOSLController.RetrieveWrapper();
wrapper.retrieveKeyword = '上海电信';
wrapper.objName2FieldsMap = new Map<String,List<String>>();
List<String> userFieldsList = new List<String>();
userFieldsList.add('Name');
wrapper.objName2FieldsMap.put('account',userFieldsList);
//wrapper.objName2FieldsMap.put('account',null);
List<SOSLController.SearchResultWrapper> searchResults = SOSLController.search(wrapper);
System.debug(LoggingLevel.INFO, '*** searchResults: \n' + JSON.serializePretty(searchResults));

结果:

总结:本篇只是描述一下SOSL的基本使用,还有很多细节使用以及限制没有涉及。本篇只起到抛砖引玉效果,如果项目中需要使用SOSL或者想要研究的,最好先自行查看文档。

salesforce零基础学习(七十五)浅谈SOSL(Salesforce Object Search Language)的更多相关文章

  1. salesforce 零基础学习(十九)Permission sets 讲解及设置

    Permission sets以及Profile是常见的设置访问权限的方式. Profile规则为'who see what'.通过Profile可以将一类的用户设置相同的访问权限.对于有着相同Pro ...

  2. salesforce 零基础学习(十八)WorkFlow介绍及用法

    说起workflow大家肯定都不陌生,这里简单介绍一下salesforce中什么情况下使用workflow. 当你分配许多任务,定期发送电子邮件,记录修改时,可以通过自动配置workflow来完成以上 ...

  3. salesforce 零基础学习(十六)Validation Rules & Date/time

    上一篇介绍的内容为Formula,其中的Date/time部分未指出,此篇主要介绍Date/time部分以及Validation rules. 本篇参考PDF: Date/time:https://r ...

  4. salesforce lightning零基础学习(八) Aura Js 浅谈一: Component篇

    我们在开发lightning的时候,常常会在controller.js中写 component.get('v.label'), component.set('v.label','xxValue'); ...

  5. salesforce零基础学习(九十五)lightning out

    随着salesforce对lightning的推进,越来越多的项目基于lightning开发,导致很多小伙伴可能都并不了解classic或者认为不需要用到classic直接就开始了lightning的 ...

  6. salesforce lightning零基础学习(九) Aura Js 浅谈二: Event篇

    上一篇介绍了Aura Framework中 Component类的部分方法,本篇将要介绍Event常用的方法. 1. setParam (String key , Object value):设置事件 ...

  7. salesforce零基础学习(九十九)Git 在salesforce项目中的应用(vs code篇)

    本篇参考: https://code.visualstudio.com/docs/editor/versioncontrol https://git-scm.com/doc https://git-s ...

  8. salesforce零基础学习(一百一十)list button实现的一些有趣事情

    本篇参考: salesforce零基础学习(九十五)lightning out https://developer.salesforce.com/docs/component-library/docu ...

  9. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  10. salesforce零基础学习(八十)使用autoComplete 输入内容自动联想结果以及去重实现

    项目中,我们有时候会需要实现自动联想功能,比如我们想输入用户或者联系人名称,去联想出系统中有的相关的用户和联系人,当点击以后获取相关的邮箱或者其他信息等等.这种情况下可以使用jquery ui中的au ...

随机推荐

  1. 数据库数据对比自动生成sql

    1.故事背景 有一次迭代步入尾声,提交给用户测试,系统管理员在测试环境中初始了一些数据,然后在上线的时候系统管理员再去正式环境初始这一些数据,然而这次数据太多了,说了一次:”为什么要初始化两次?“ 你 ...

  2. python通过excel对数据库插入数据

    1.需要有两个包文件xlrd及MySQLdb(其他数据库可以另外找) 2.读取excel文件信息 book = xlrd.open_workbook(文件地址) 3.建立MySQL链接 databas ...

  3. 惊心动魄的SAP S4客户额度调整运动

    今天一大早,收到商务部发来的消息,说某一个客户的额度超额了,但实际上并未超额.从SAP系统中反馈的额度来看,显示超了2万多.后来经过在BP画面检查信用段发现,这个客户额度占用里面,某一个未清订单已经部 ...

  4. MVC 5限制所有HTTP请求必须是POST

    今天有位同事,提出了这样一个问题,他想限制所有MVC接收到的HTTP请求必须是POST方式. 接下来在下面的内容中,将我想到的方式分享给大家,如果大家有其它的方式,请留言. 一.HttpPostAtt ...

  5. ASP.NET MVC5(四):数据注解和验证

    前言 用户输入验证的工作,不仅要在客户端浏览器中执行,还要在服务端执行.主要原因是客户端验证会对输入数据给出即时反馈,提高用户体验:服务器端验证,主要是因为不能完全信任用户提供的数据.ASP.NET ...

  6. string services

    string通用字符串操作: re,正则表达式 difflib,比较序列 stringIO:以文件的方式来读和写字符串 CstringIO:更快捷的stringIO版本 textwrap:文本包装和填 ...

  7. HashMap如何工作 - Java

    大多数人应该会同意HashMap是现在面试最喜欢问的主题之一.我和同事常常进行讨论,并很有帮助.现在,我继续和大家讨论. 我假设你对HashMap的内部工作原理感兴趣,并且你已经知道了基本的HashM ...

  8. php面试问题

    问题:请用最简单的语言告诉我PHP是什么? 回答:PHP全称:Hypertext Preprocessor,是一种用来开发动态网站的服务器脚本语言. 问题:什么是MVC? 回答:MVC由Model(模 ...

  9. bootstrap-table前台和后台分页对json格式的要求

    Bootstrap是一款前端非常流行的框架,其中的表格更为大家经常使用.大家都知道表格的分页分为前台和后台分页,也就是表格配置中sidePagination属性,当sidePagination: &q ...

  10. linux下vim 查找命令

    在命令模式下输入/word 这个是查找文件中“word”这个单词,是从文件上面到下面查找?word 这个是查找文件中“word”这个单词,是从文件下上面到面查找