Elasticsearch学习之深入搜索四 --- cross-fields搜索
1. cross-fields搜索
一个唯一标识,跨了多个field。比如一个人,标识,是姓名;一个建筑,它的标识是地址。姓名可以散落在多个field中,比如first_name和last_name中,地址可以散落在country,province,city中。跨多个field搜索一个标识,比如搜索一个人名,或者一个地址,就是cross-fields搜索。初步来说,如果要实现,可能用most_fields比较合适。因为best_fields是优先搜索单个field最匹配的结果,cross-fields本身就不是一个field的问题了。
增添字段:
POST /forum/article/_bulk { "update": { "_id": "1"} } { "doc" : {"author_first_name" : "Peter", "author_last_name" : "Smith"} } { "update": { "_id": "2"} } { "doc" : {"author_first_name" : "Smith", "author_last_name" : "Williams"} } { "update": { "_id": "3"} } { "doc" : {"author_first_name" : "Jack", "author_last_name" : "Ma"} } { "update": { "_id": "4"} } { "doc" : {"author_first_name" : "Robbin", "author_last_name" : "Li"} } { "update": { "_id": "5"} } { "doc" : {"author_first_name" : "Tonny", "author_last_name" : "Peter Smith"} }
查询first_name和last_name中包含Peter Smith的doc
GET /forum/article/_search { "query": { "multi_match": { "query": "Peter Smith", "type": "most_fields", "fields": [ "author_first_name", "author_last_name" ] } } }
Peter Smith,匹配author_first_name,匹配到了Smith,这时候它的分数很高,为什么?
因为IDF分数高,IDF分数要高,那么这个匹配到的term(Smith),在所有doc中的出现频率要低,author_first_name field中,Smith就出现过1次
Peter Smith这个人,在doc 1,Smith在author_last_name中,但是 author_last_name 出现了两次 Smith,所以导致doc 1的IDF分数较低,这里就存在以下三个问题:
问题1:只是找到尽可能多的field匹配的doc,而不是某个field完全匹配的doc
解决,最匹配的document被最先返回
问题2:most_fields,没办法用minimum_should_match去掉长尾数据,就是匹配的特别少的结果
解决,可以使用minimum_should_match去掉长尾数据
问题3:TF/IDF算法,比如Peter Smith和Smith Williams,搜索Peter Smith的时候,由于first_name中很少有Smith的,所以query在所有document中的频率很低,得到的分数很高,可能Smith Williams反而会排在Peter Smith前面
解决,Smith和Peter在一个field了,所以在所有document中出现的次数是均匀的,不会有极端的偏差,计算IDF的时候,将每个query在每个field中的IDF都取出来,取最小值,就不会出现极端情况下的极大值了
第一个办法:用copy_to,将多个field组合成一个field
问题其实就出在有多个field,有多个field以后,就很尴尬,我们只要想办法将一个标识跨在多个field的情况,合并成一个field即可。比如说,一个人名,本来是first_name,last_name,现在合并成一个full_name
PUT /forum/_mapping/article { "properties": { "new_author_first_name": { "type": "string", "copy_to": "new_author_full_name" }, "new_author_last_name": { "type": "string", "copy_to": "new_author_full_name" }, "new_author_full_name": { "type": "string" } } }
用了这个copy_to语法之后,就可以将多个字段的值拷贝到一个字段中,并建立倒排索引
POST /forum/article/_bulk { "update": { "_id": "1"} } { "doc" : {"new_author_first_name" : "Peter", "new_author_last_name" : "Smith"} } --> Peter Smith { "update": { "_id": "2"} } { "doc" : {"new_author_first_name" : "Smith", "new_author_last_name" : "Williams"} } --> Smith Williams { "update": { "_id": "3"} } { "doc" : {"new_author_first_name" : "Jack", "new_author_last_name" : "Ma"} } --> Jack Ma { "update": { "_id": "4"} } { "doc" : {"new_author_first_name" : "Robbin", "new_author_last_name" : "Li"} } --> Robbin Li { "update": { "_id": "5"} } { "doc" : {"new_author_first_name" : "Tonny", "new_author_last_name" : "Peter Smith"} } --> Tonny Peter Smith
然后查询:
GET /forum/article/_search { "query": { "match": { "new_author_full_name": "Peter Smith" } } }