函数查询
函数查询允许用一个或多个数值字段生成一个相关度的分数,标准查询解析器、DisMax、eDisMax都支持函数查询
函数查询使用函数,函数可以是一个常数(数字或字符串),一个字段,另一个函数,或参数替换参数(不懂...),可以用函数来修改结果的排名(Ranking),可以用于对于根据用户的位置,或其他的考虑,来改变结果的排名
使用函数查询
函数表现为函数调用,例如 sum(a,b)
而不是 a+b
在 solr 查询里,有几种使用函数查询的方法
- 通过一个明确的、期待函数参数的 QParser,如
func
或frange
,例如q={!func}div(popularity,price)&fq={!frange l=1000}customer_ratings
- 在一个排序表达式中,例如
sort=div(popularity,price) desc, score desc
- 将函数结果值作为查询结果文档的伪字段(区别与真实字段),例如
&fl=sum(x, y),id,a,b,c,score
- 明确需要函数的参数,如 eDisMax 的
boost
参数,或 DisMax 的bf
参数(注意 bf 参数接收多个以空格分隔的函数查询,每个函数查询有一个可选的 boost 因子,确保在单个函数内的空格被排除),例如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)"
只推荐可快速随机存取的函数(?)
可用的函数
函数 | 说明 | 语法示例 |
---|---|---|
abs | 绝对值 | abs(x) abs(-5) |
and | 仅当所有操作数都为 true 时返回 true | and(not(exists(popularity)),exists(price)) 任何文档,字段 price 有值,且字段 popularity 没有值时返回 true |
"constant" | 浮点常数 | 1.5 |
def | default 的简写,若值 1 不存在则返回值 2 | def(rating,5) :如果 rating 存在则返回之,否则返回 5 def(myfield, 1.0) :如果字段 myfield 存在返回其值,否则返回 1.0 |
div | 除法,div(x,y) 表示 x 除以 y | div(1,y) div(sum(x,100),max(y,1)) |
dist | 返回 n 维空间里 2 个矢量的距离 | dist(2, x, y, 0, 0) :(0,0) 和 (x,y) 之间的欧氏距离dist(1, x, y, 0, 0) : (0,0) 和 (x,y) 之间的曼哈顿距离dist(2, x,y,z,0,0,0) :(0,0,0) 和 (x,y,z) 之间的欧氏距离dist(1,x,y,z,e,f,g) : (x,y,z) 和 (e,f,g) 之间的曼哈顿距离,每个字符是一个字段名 |
docfreq(field,val) | 返回这个字段里包含这个值的文档数量,这是个常数 | docfreq(text,'solr') defType=func&q=docfreq(text,$myterm)&myterm=solr |
exists | 如果任意字段存在就返回 true | exists(author) 任何文档在 author 字段有值就返回 trueexists(query(price:5.00)) price 匹配 5.00 就返回 true |
field | 返回字段的 docValues 或 indexed 值(数量?),对于 docValues,可以添加可选参数 min 或 max,返回 0 表示文档木有该字段 | 如下示例是等价的myFloatFieldName field(myFloatFieldName) field("myFloatFieldName") 如果字段名是非典型的(例如包含了空格)用第三种写法 对于多值的 docValues 字段,示例如下 field(myMultiValuedFloatField,min) field(myMultiValuedFloatField,max) |
hsin | ||
idf | ||
if | 条件判断,语法为if(test,value1,value2) test 是个逻辑值或逻辑表达式value1 test = true 时返回values test = false 时返回表达式可以是任意返回逻辑值的函数,或是返回数值的函数,此时 0 表示 false,或是 string,此时空串表示 false |
if(termfreq(cat,'electronics'),popularity,42) 该函数检查每一文档,字段 cat 是否包含词条 "electronics",如果包含,返回 popularity 字段的值,否则返回 42 |
linear | m*x+c 的函数形式,m、c 为常数,x 为变量等价于 sum(product(m,x),c) 但是性能好些,因为只有一次函数调用 |
linear(x,m,c) linear(x,2,4) 返回 2*x+4 |
log | 对数,以 10 为底数 | |
map | 根据输入是否落在某个范围来返回值,范围参数 min,max 必须是常数。如果输入 x 不在 min 和 max 之间,返回值为 x 或 default(如果指定了该参数) | map(x,min,max,target) map(x,0,0,1) - 将 0 改为 1,处理默认值 0 时有用map(x,min,max,target,default) map(x,0,100,1,-1) - 将 0~100 之间的值改为 1,其他值 改为 -1 |
max | 返回最大值 max(x,y,...) |
max(myfield,myotherfield,0) |
maxdoc | 返回索引的文档数量,包括已标记为删除但还没有物理删除的文档,这是个常数,对于索引里的任何文档都是同样的值 | maxdoc() |
min | 返回最小值 | |
ms | 返回参数之间的时间差异,以毫秒为单位,参数可以是索引的 TrieDateField 字段,或NOW,或基于日期常数的日期计算ms() 等价于 ms(NOW) 当前时间与 epoch 之间的毫秒差epoch 1970-1-1 00:00:00.000 ms(a) a 与 epoch 之间的毫秒差ms(a,b) : 时间 a 与 b 之间的毫秒差,注意 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) |
norm(field) | ||
not | 逻辑非 | |
numdocs | 返回索引的文档数量,不包括已标记为删除但还没有物理删除的文档,这是个常数,对于索引里的任何文档都是同样的值 | numdocs() |
or | 逻辑或 | or(value1,value2) value1 和 value2 都为 true 时返回 true |
ord | 返回索引字段的值的序号,从 1 开始。排序是依字典顺序,该字段为单值字段。若字段没有值返回 0 | |
pow | 幂值,pow(x,y) =x^y |
pow(x,y) pow(x,log(y)) pow(x,0.5) 等价于 sqrt |
product | 乘积,参数为逗号分隔的值或函数,等价于 mul(...) |
product(x,y,...) |
query | 返回指定的子查询的分数,如果子查询未匹配到文档的话返回一个默认值 | query(subquery, default) q=product(popularity, query({!dismaxv='solr rocks'}) 返回 popularity 和 DisMax 查询的分数的乘积q=product(popularity, query($qq))&qq={!dismax}solr rocks : 等价于上面的查询,使用了参数引用q=product(popularity, query($qq,0.1))&qq={!dismax}solr rocks : 指定了不匹配 DisMax 查询的文档的默认的分数为 0.1 |
recip | recip(x,m,a,b) = a/(m*x+b) m,a,b 为常数, x 是个函数 |
recip(myfield,m,a,b) recip(rord(creationDate),1,1000,1000) |
rord | ord 的反转 | rord(myDateField) |
scale | 将输入 x 缩放到 minTarget 和 maxTarget 之间,当前的实现会遍历所有的 x 值来确定 min 和 max 的值 如果文档被删除或者没有值,则会视同为0 |
scale(x,minTarget,maxTarget) scale(x,1,2): scales the values of x such that all values will be between 1 and 2 inclusive. |
sqedist | 欧氏距离的平方, | sqedist(x_td, y_td, 0, 0) |
sqrt | 平方根 | sqrt(x)sqrt(100)sqrt(sum(x,100)) |
strdist | 计算 2 个字符串的距离,格式为 strdist(string1, string2, distance measure) |
strdist("SOLR",id,edit) distance measure 取值为 jw : Jaro-Winkleredit : Levenstein or Edit distancengram : NGramDistance, 可以指定一个可选的 ngram size 参数,默认为 2FQN : 实现了 StringDistance 接口的类名,该类必须有一个无参的构造函数 |
sub | 减法,sub(x,y) 即为 x - y | sub(myfield,myfield2) |
sum | 求和,add() 可以作为别名使用 | sum(x,y,...) |
sumtotaltermfreq | 返回 totaltermfreq 之和 |
sttf() |
termfreq | 词条在字段里出现的次数 | termfreq(text,'memory') |
tf | term frequence,返回一个给定词条的词条频率因子 | tf(text,'solr') |
top | 貌似是在顶层索引里取值,同一个值在某个段的序号和其在整个索引里的序号是不一样的 | ord() ,rord() 实际是隐含了 top() 调用,即 ord() 等价于 top(rod()) |
totaltermfreq | 整个索引里,词条在字段里出现的次数 | ttf(text,'memory') |
xor() | 逻辑异或 | xor(field1,field2) field1 ,field2 都为 true 则返回 false,否则返回 true |
有 2 个文档,doc1,doc2,都有字段 fieldX,其中
- doc1.fieldX = [A B C]
- doc2.fieldX = [A A A A]
则
docFreq(fieldX:A) = 2
A 在 2 个文档里出现freq(doc2, fieldX:A) = 4
A 在 doc2 里出现 4 次totalTermFreq(fieldX:A) = 5
A 在所有文档里出现 5 次sumTotalTermFreq(fieldX) = 7
对于 fieldX,5 个 A,1 个 B,1 个 C
函数查询示例
假设一个索引,字段名为 boxnames
,保存了一些名字随意,尺寸(x,y,z)以米为单位的盒子,你要搜索名字匹配 findbox
且按体积排序的盒子,查询参数如下
q=boxname:findbox _val_:"product(x,y,z)"
这个查询将结果按盒子的体积排序,如果想要知道体积,你需要请求分数(score,包含了体积)
&fl=*,score
假设还有一个字段保存着重量,weight
,要按盒子的密度排序且返回密度大小,可以提交下面的查询
http://localhost:8983/solr/collection_name/select?q=boxname:findbox _val_:"div(weight,product(x,y,z))"&fl=boxname x y z weight score
用函数排序
可以用函数的输出对查询结果排序,例如,要按距离排序
http://localhost:8983/solr/collection_name/select?q=*:*&sort=dist(2, point1, point2) desc
函数排序也支持伪字段:字段是动态生成且像一个正常字段那样返回结果,例如
&fl=id,sum(x, y),score
将会返回
<str name="id">foo</str>
<float name="sum(x,y)">40</float>
<float name="score">0.343</float>