构建搜索语句

搜索查询力争做到简单明了,和目前最流行的搜索引擎做法极为相似,并不需要过多额外的修饰或语法。

您只要把您打算搜索的短语或关键词列出来即可,建议用空格连接多个词;在此基础上还支持字段搜索、布尔搜索、精确搜索、相邻位置搜索等。1. 典型搜索语句正如上面所讲,仅仅用空格把搜索词、名子连接起来的方式最为常用。内部默认采用 AND 规则,也就是说要求列出来的关键词(分词之后)全部匹配,如果您通过 XSSearch::setFuzzy 开启了模糊匹配,那么相当于采用 OR 规则,只要其中一个关键词匹配即可。

刚刚说的这种搜索的是索引数据库的“混合区”,即类型为 body 的字段以及索引方式为 mixedboth 的字段。如果某个字段的索引方式为 selfboth,那么还可以指定为字段检索,也就是要求这个字段匹配指定的关键词,用法为格式为 “字段名:关键词”。

Note: 搜索语句最大支持长度为 80 字节(每一个汉字占 3 字节),此规则同时适用于 XSSearch::searchXSSearch::count$query 参数。

搜索语句举例如下:

  1. $search->search('上海人民公园'); // 检索 body 型字段及混合区
  2. $search->search('上海 人民公园'); // 用空格连接 2 个关键词, 这种情况比上面的用法更明确
  3. $search->search('subject:上海 人民公园'); // 特别要求 subject 字段包含 "上海"
  4. $search->setFuzzy()->search('上海公园'); // 开启模糊搜索,搜索 "上海" 或 "公园"

Tip: 从 1.3.0 版本起,允许使用空搜索条件进行搜索了,其含义相当于全部匹配

2. 布尔搜索出于高级搜索的需求,我们的搜索语句还全面支持布尔操作,并且像数学运算一样支持括号操作以明确关系。

多个词语之间的“并且”、“或者”、“异或”关系

默认情况,系统采用“并且”关系,即您输入的多个词语(空格分开或系统自动切分以后)必须全部包含。您也可以通过 AND 将这些关键词显式的强制要求全部出现。相应的,如果您需要只包括其中一个词即可,那么用 OR 将它们连接起来。更有趣的是,如果有些情况您只想要其中的一个词,并且不要两者同时出现,那么就用 XOR 连接。参看下面的例子:

Note: 表达布尔关系的关键字必须全部大写,即:XOR, OR, AND

  1. $search->search('杭州 西湖');
  2. $search->search('杭州 AND 西湖'); // 这两者效果是相同的
  3.  
  4. $search->setFuzzy()->search('杭州 西湖');
  5. $search->search('杭州 OR 西湖'); // 这两者效果相同,都是只要求匹配其中一个
  6.  
  7. $search->search('杭州 XOR 西湖'); // 表示必须包含其中一个词,并且不允许同时包含这两个词。

要求搜索结果中不含特定关键词

如果您发现搜索结果中,有某一类信息是您不希望看见的,而且这些信息都包含特定的关键词,那么请用减号或 NOT 语法,就可以去除所有这些含有特定关键词的网页。例如,搜索 “神雕侠侣”,希望是关于武侠小说方面的内容,却发现很多关于电视剧方面的网页。这时希望排除所有包含“电视剧”的搜索结果。

Note: 前一个关键词和减号之间必须有空格分开,否则会被当连字符处理,减号和后一个关键词之间不能有空格。

  1. $search->search('神雕侠侣 -电视剧'); // 正确用法1
  2. $search->search('神雕侠侣 NOT 电视剧'); // 正确用法2
  3.  
  4. $search->search('神雕侠侣-电视剧'); // 不符合预期目的
  5. $search->search('神雕侠侣 - 电视剧'); // 不符合预期目的

组合关系的括号表达式

就像数学表达式中,括号具有显式的优先级,如果您需要非常复杂的搜索条件,就采用括号表达式来组合上述的条件。

  1. // 要求结果包含 “杭州+西湖” 或 “杭州 + 西溪湿地”,并且不能包含 “汽车 火车”
  2. $search->search('((杭州 AND 西湖) OR (杭州 AND 西溪湿地)) NOT (汽车 火车)');

Tip: 在组合关键中允许嵌入字段搜索的语法。如:subject:上海 OR subject:公园

3. 精确搜索在项目字段设计中有一个选项 phrase 如果设为 yes 将在索引数据库中保存关键词的位置信息,因而我们可以在搜索时利用这些位置信息实现精确搜索。

Info: 项目中类型为 titlebody 的字段默认已开启此项,此外这个选项也仅针对使用内置分词器的字段有效。 对于 phrase 设为 no 或用其它分词器的字段来说这项功能无效。

使用双引号进行精确匹配

如果输入的查询词较长,在经过系统词法分析后,给出的搜索结果中的查询词,可能是拆分的。如果您对这种情况不满意,可以尝试给查询短句加上双引号,搜索结果就会要求词按顺序匹配。

  1. $search->search('上海科技大学');
  2. // 这时内部把搜索语句切割为:上海+科技+大学,要求搜索结果同时包含这三个词即可,
  3. // 但不要求它们的出现顺序,有可能先出现”科技“再出现”上海“,也有可能这三个词离得很远。
  4.  
  5. $search->search('"上海科技大学"');
  6. // 加上引号后,要求搜索结果必须依次出现”上海+科技+大学“,要显得精准很多。

索引词之间的距离查询

如果您希望输入的多个关键词之间的在匹配结果中的位置距离不要太远,那么可以使用 NEAR 语法来进行搜索,默认要求它们的距离不超过 10。您还可以在 NEAR 后加上 /n 表示具体的距离要求,n 是整数,这儿的距离指的是分词后的序号距离

  1. $search->search('杭州 NEAR 西湖'); // 要求结果中出现 "杭州" 和 "西湖",并且距离不超过 10 个词
  2. $search->search('杭州 NEAR/5 西湖'); // 要求它们距离不超过 5个词

如果您还希望检索结果中词汇出现的顺序与您的输入顺序一致,只要将 NEAR 换成 ADJ 即可,注意这些语法词和布尔搜索一样,均必须全部大写。

  1. $search->search('杭州 ADJ 西湖'); // 要求结果中先出现 "杭州" 再出现 "西湖",并且距离不超过 10 个词
  2. $search->search('杭州 ADJ/3 西湖'); // 要求它们距离不超过 3个词

4. 其它搜索语句修饰前面还有提到一些附加的搜索语句修饰,这些操作不能直接写进 Query 语句,只能过过相应的 API 来完成。

字段值区间搜索

通过 XSSearch::addRange 我们可以在搜索中,针对某个字段的值增加区间条件过滤。该方法接受三个参数,第一参数 $field 为字段名称,后面两个参数依次是 $from 起始值,$to 结束值。假设该字段的值为 $value,那么要求 $value > $from && $value <= $to 才符合条件,其中如果有一个设为 null,则会自动将区间转换为单向比较。

以前面提到的 demo 项目中的 chrono 字段为例,参看下面代码及注释:

  1. $from = strtotime("2011-1-1"); // 起始时间 2011年1月1日
  2. $to = strtotime("2011-9-1"); // 结束时间 2011年9月1日
  3.  
  4. // 1. <= $to: 要求结果的时间必须在 2011-9-1 之前,并且包含 2011-9-1
  5. $docs = $search->setQuery('杭州')->addRange('chrono', null, $to)->search();
  6.  
  7. // 2. >= $from: 要求结果的时间必须在 2011-1-1 之后,并且包含 2011-1-1
  8. $docs = $search->setQuery('杭州')->addRange('chrono', $from, null)->search();
  9.  
  10. // 3. > $from && <= $to: 要求结果的时间必须在 2011-1-1 之后 并且 2011-9-1 之前
  11. $docs = $search->setQuery('杭州')->addRange('chrono', $from, $to)->search();

按字段索引词加权

通过 XSSearch::addWeight 我们可以针对某个字段添加权重索引词。该字段是否包含这个词都不影响搜索匹配结果,但如果包含会参与计算结果权重,使结果的相关度更高。常用于针对某一类数据提升搜索结果排序位置。

  1. // 搜索包含 "杭州" 的结果,并且提升 subject 字段包含 "西湖" 的数据的排序
  2. $docs = $search->setQuery('杭州')->addWeight('subject', '西湖')->search();

Note: 这些修饰不能应用到快捷搜索中,也就是说不能直接把 Query 语句传递给 XSSearch::search。 您必须用 XSSearch::setQuery 设置搜索语句,再用不带参数的调用 searchcount 方法。

区间搜索默认用的是字节序比较,如果您希望将字段值以数字方式比较大小,请将字段的类型设置为 numeric

5. 查看搜索语句的分析结果通过 XSSearch::setQuery 设置了搜索语句后,您可以调用 XSSearch::getQuery 查看您的搜索语句在内部的分析结果,常用于 DEBUG。此外这两个方法也可以通过魔术属性 XSSearch::query 来访问。

  1. // 设置搜索语句方法1:
  2. $search->setQuery('神雕侠侣 -电视剧');
  3.  
  4. // 设置搜索语句方法2:
  5. $search->query = '神雕侠侣 -电视剧';
  6.  
  7. // 查看搜索语句解析结果方法1:
  8. // 不带参数,输出:Xapian::Query((神雕侠侣:(pos=1) AND_NOT 电视剧:(pos=2)))
  9. echo $search->getQuery();
  10.  
  11. // 带参数,则解析新语句,输出:Xapian::Query((杭州:(pos=1) AND 西湖:(pos=2)))
  12. echo $search->getQuery('杭州 西湖');
  13.  
  14. // 查看搜索语句解析结果方法2:
  15. // 相当于不带参数的调用 getQuery()
  16. echo $search->query;