有的时候,我们一开始不可能准确地知道搜索的关键字在 Solr 中查询出的结果是什么,因此,Solr 还提供了几种类型的模糊查询
Solr从数据库中读取数据并创建索引速度
一次性创建索引
在JVM内存配置为256M时,建立索引至1572865时出现Java heap异常;增加JVM内存配置至512M,设置系统环境变量:JAVA_OPTS -Xms256m -Xmx512m,能成功建立2112890条(花费2m 46s)。
平均索引创建速度为:12728/s(两个string字段,长度大约为20字符)。
增量创建索引
注意:近实时增量索引需要写数据库服务的时间与搜索引擎服务器时间同步(数据库服务时间先于搜索引擎服务器时间才行)。使用默认的DIH创建增量索引速度较慢(50/s~400/s),不如全索引(1W/s),因为需要从数据库中读取多遍(1、要更新的IDs;2、每1ID去数据库中重取所有列)。故需要更改DIH增量索引程序,以全索引的方式读数据;或采取全读出的方式,一次全读出所有列。
Solr创建索引效率
速度
Solr创建索引速度与Solr机器CPU正相关,一般情况下CPU占用率能达到接近100%,内存占用率在默认情况下需达到接近100%,网络、磁盘占用率均小。因此创建索引的效率瓶颈在CPU及内存。当内存占用率维持在接近100%,索引大小达到物理内存大小时,插入新的数据容易出现OOM错误,这时需要使用ulimit –v unlimited命令更改virtual memory配置为unlimited再启动Solr便不会出现OOM错误。在64位机器系统上,官方推荐使用MMapDirectory。
大小
1亿索引大小约为13-16GB,2亿索引大小约为30GB
Solr搜索方式
交集:{name:亿度 AND address:海淀} {text:海淀 AND 亿度}。
联集:{name:亿度 OR address:海淀} {text:海淀 OR 亿度}。
排除:{text:海淀 -亿度}。
通配符:{bank:中国*银}。
范围:{num:[30 TO60]}。
分页:start rows
排序:sort
Group 权重中文分词 …
可用的函数
序列 | 函数 | 说明 |
---|---|---|
1 | abs | 绝对值 |
2 | and | true/false, 例and(not(exists(popularity)),exists(price)) |
3 | “constant” | 浮点数 |
4 | def | 返回字段值, def(myfield, 1.0): 等价于 if(exists(myfield),myfield,1.0) |
5 | div | div(1,y) |
6 | dist | 返回两个n维向量(点)间的距离;dist(2, x, y, 0, 0): 计算(0,0)和(x,y)之间的欧式距离; dist(1, x, y, 0, 0)计算Manhattan距离。 |
序列 | 函数 | 说明 |
---|---|---|
7 | docfreq(field, val) | 返回字段包含val的文档数;docfreq(text,’solr’) |
8 | exists | 返回true如果字段存在;exists(author);exists(query(price:5.00)) |
9 | field | 返回字段的数值docValues或索引值。等价的三种形式:myFloatFieldName;field(myFloatFieldName);field(“myFloatFieldName”) ;对multivalued docValues字段:field(myMultiValuedFloatField,min)field(myMultiValuedFloatField,max) |
11 | idf | 反文档频率;idf(fieldName,’solr’) |
12 | if | 条件查询函数:if(test,value1,value2); if(termfreq(cat,’electronics’),popularity,42) |
13 | linear | 实现m*x+c,m和c是常量,等价于sum(product(m,x),c)但更高效; linear(x,m,c) |
序列 | 函数 | 说明 |
---|---|---|
14 | log | 10为底数的log |
15 | map | map(x,min,max,target), min和max之间的值转为target,map(x,min,max,target,default), min和max之间的值转为target, 其他为default |
16 | max | 选择最大值;max(myfield,myotherfield,0) |
17 | min | 返回最小值;min(myfield,myotherfield,0) |
18 | ms | 返回参数毫秒数;ms()等价ms(NOW); ms(a); ms(a,b) 为a-b的毫秒数, 例: ms(NOW/DAY) ;ms(2000-01-01T00:00:00Z) ;ms(mydatefield) ;ms(NOW,mydatefield) ;ms(mydatefield,2000-01-01T00:00:00Z);ms(datefield1,datefield2) ; |
29 | norm(field) | 返回索引字段存储的”norm”, 这是索引时boost和长度规范化因子的product(点积?) |
20 | maxdoc | 返回索引文档数,包括被标记为删除但还未被purged |
21 | numdocs | 返回索引文档数,不包括被标记为删除但还未被purged |
序列 | 函数 | 说明 |
---|---|---|
22 | not | not(exists(author)): true/false |
23 | or | or(value1,value2): true/false |
24 | ord | 返回字段索引值在Lucene索引顺序中的序号(ordinal) |
25 | rord | 返回ord的逆序 |
26 | pow | 幂 |
27 | product | 点积(或mul(…)); product(x,y,…) |
28 | query | 返回给定子查询的分数。query(subquery,default)列,product(popularity, query({!dismax v=’solr rocks’}); 等价于 q=product(popularity,query($qq))&qq={!dismax}solr rocks |
序列 | 函数 | 说明 |
---|---|---|
29 | recip | 倒数(reciprocal); recip(x,m,a,b) = a/(m*x+b),当x为rord(datefield)时,这是一个理想的函数用于boosting更多最新的文档。 |
30 | scale | 将x值按比例放入[minTarget, maxTarget]范围内。 scale(x,1,2) |
31 | sqedist | 欧式距离平方。sqedist(x_td, y_td, 0, 0) |
32 | sqrt | 平方根 |
33 | strdist | 字符串距离,使用Lucene的StringDistance接口。(string1, string2, distance measure); strdist(“SOLR”,id,edit), distance measure: jw(Jaro-Winkler), edit(Levenstein或编辑距离),ngram(默认2) |
34 | sub | sub(x,y) = x – y |
35 | sum | 求和(或add(…)); |
序列 | 函数 | 说明 |
---|---|---|
36 | sumtotaltermfreq | 或sttf, 返回字段索引中所有terms的总频次。 |
37 | termfreq | 返回文档中term出现的次数。 termfreq(text, ‘memory’) |
38 | tf | 返回指定字段在Similarity中使用的tf因子。 termfreq(text, ‘solr’) |
39 | top | top(query)使查询从整个索引获取数据。如一个segment中的值顺序与在整个索引中的顺序不一样。ord()和rord()隐身的使用top(); 即ord()=top(ord()) |
40 | totaltermfreq | 或ttf,返回term在字段中的频次。 ttf(text, ‘memory’) |
41 | xor | 异或 |
搜索方式
精确搜索
如果你想要通过 Solr 的索引查询公司中所有员工的档案。一种方法是枚举出公司中所有可能的职位:
查询语句:”chief executive officer” OR “chief financial officer” OR “chief marketing officer” OR “chief technology officer” OR … 当然,这种查询的前提是你需要知道公司中所有可能的职位,这当然不现实。另外的一种解决方案是单独搜索每个关键字: 查询语句: chief AND officer 这将会匹配所有可能的用例,但是同时也会匹配所有包含了这两个关键字的文档。例如:One chief concern arising from the incident was the safety of the police officer on duty。这个文档明显不符合我们的要求,但是如果使用上面的查询语句,那么将会返回这个文档。
模糊搜索
对于很多搜索应用来说,很重要的功能是不仅仅需要精确匹配用户的文本内容。而且还允许一些灵活的变化,比如一些用户的拼写错误或相同单词的其它变体。Solr 通过基于 Damerau-Levenshtein 距离的编辑距离测量来支持这个功能,它将容忍 80% 以上的拼写错误。
Solr 提供的模糊编辑距离查询需要用到波浪符号(~):
查询语句: administrator~ 匹配: adminstrator, administrater, administratior,等
这个查询不仅匹配原始的关键字(administrator),还有其它与原始关键字有 2 个编辑距离的关键字。一个编辑距离表示增加,删除,取代或交换一个任意字符。关键字 adminstrator (在第六个字母出少了字符“i”)和原始关键字之间相差一个编辑距离,因为它删除了一个字符。同样 sadministrator 和原始关键字之间也是相差一个编辑距离,因为它在前面添加了一个字符。administratro 也与原始关键字有一个编辑距离,因为它将最后两个字符交换了顺序。
在编辑距离查询中也可以精确指定编辑距离:
查询语句:administrator~1 匹配一个编辑距离以内的内容。
查询语句:administrator~2 匹配两个编辑距离以内的内容(如果没有提供编辑距离的话,这个就是默认值)。
查询语句:administrator~N 匹配 N 个编辑距离以内的内容。
注意,任何编辑距离大于 2 的查询将会使查询速度变得很慢。如果编辑距离在 2 以内,那么将会使用很高效率的 Levenshtein 自动机(Levenshtein automaton),但是如果编辑距离大于 2,将会退回到更慢的编辑距离实现。
范围搜索
在Solr查询中,有几种方式可以使用函数(functions):
* 通过QParser显示的传递function参数,如func或frange.
q={!func}div(popularity,price)&fq={!frange l=1000}customer_ratings
* 在Sort表达式中。
sort=div(popularity,price) desc, score desc
* 将function结果作为伪字段添加到查询结果文档中。
&fl=sum(x, y),id,a,b,c,score
* 使用用于指定函数的参数,如EDisMax的boost参数,或DisMax查询的bf(boost function)参数.
q=dismax&bf="ord(popularity)^0.5 recip(rord(price),1,1000,1000)^0.3"
* 在lucene QParser中通过_val_关键字使用内联函数。
q=_val_:mynumericfield _val_:"recip(rord(myfield),1,2,3)"
结论
范围越大,结果数据越多,搜索花费时间越长。
第一次搜索较慢,后来时间花费较少。