我都知道lucene是不区分数组类型的,而elasticsearch中没有专用的数组类型,对 Array datatype支持不需要专用类型(理解为动态的字符串或object类型)。默认情况下,任何字段都可以包含零个或多个值,但数组中的所有值都必须具有相同的数据类型。本文基于es 6.2,比如:  

  • "one""two" ]
  •  [ 12 ]
  •  [ { "name": "Mary", "age": 12 }{ "name": "John", "age": 10 }],这类叫嵌套对象

需求1:查找多个精确值,一个电影属于多个类别,根据多个类别查找电影

{
     "title" : "流浪地铁",
     "actor" : "吴京",
     "tags" : [
         "科幻",
         "灾难",
         "冒险"
     ]
 }
 {
     "title" : "警察故事",
     "actor" : "成龙",
     "tags" : [
         "动作",
         "冒险"
     ]
 }

首先,需要对tags进行mapping,否则它默认会被自动判断为text类型(分词啥的,我们不需要)

http://192.168.1.42:9200/movies/movie/_mapping {
     "movie": {
         "properties": {
             "tags": {
                 "type": "keyword"
             }
         }
     }
 }

然后对上面模型的数据进行插入,之后如何判断它是否是有效的呢?可以通 _termvectors 查询。

http://192.168.1.42:9200/movies/movie/2/_termvectors?fields=tags

{  "_index": "movies",
     "_type": "movie",
     "_id": "2",
     "_version": 1,
     "found": true,
     "took": 63,
     "term_vectors": {
         "tags": {
             "field_statistics": {
                 "sum_doc_freq": 2,
                 "doc_count": 1,
                 "sum_ttf": -1
             },
             "terms": {
                 "动作": {
                     "term_freq": 1,
                     "tokens": [
                         {
                             "position": 2,
                             "start_offset": 10,
                             "end_offset": 12
                         }
                     ]
                 },
                 "冒险": {
                     "term_freq": 1,
                     "tokens": [
                         {
                             "position": 1,
                             "start_offset": 7,
                             "end_offset": 9
                         }
                     ]
                 }
             }
         }
     }
 }

确实已经可以了,那就可以通过term查询了,但这有什么问题?精确查找不需要计算评分的,需要去掉的

http://192.168.1.42:9200/movies/movie/_search {
  

"query": {
         "term": {
             "tags": "冒险"
         }
     }
 }
 http://192.168.1.42:9200/movies/movie/_search
 {
     "query": {
         "constant_score": {  //不计算评分
             "filter": {
                 "term": {
                     "tags": "冒险"
                 }
             },
             "boost": 1
         }
     }
 }

http://192.168.1.42:9200/movies/movie/_search {

"query": {
         "constant_score" : {
             "filter" : {
                  "bool" : {//整个字段完全相等
                     "must" : [
                         { "term" : { "tags" : "冒险" } }, 
                         { "term" : { "tags" : "动作" } } 
                     ]
                 }
             }
         }
     }
 }

这里有什么问题呢?由于倒排索引表自身的特性,整个字段是否相等会难以计算,正因如此, term 和 terms 是 必须包含(must contain) 操作,而不是 必须精确相等(must equal exactly) 。如果一定期望整个字段完全相等,最好的方式是增加并索引另一个字段,使用bool-must语法。

那如果是嵌套数组该怎么玩?我们有这样的模型:

{
          "title": "流浪地铁",
          "actor": "吴京",
          "tags": [{
             "id": 1,
             "type": "科幻"
         }, {
             "id": 2,
             "type": "灾难"
         }, {
             "id": 3,
             "type": "冒险"
         }]
 }


还是按之前的套路,首先建mapping

http://192.168.1.42:9200/movies/movie/_mapping {
  

"movie": {
         "properties": {
             "tags.type": {
                 "type": "keyword"
             }
         }
     }
 }

需求2:我们需要对tags( type=科幻 and id=1)进行交叉查询

满足需求1没有什么问题,执行下面的语句我们发现了一个现象:交叉查询(id+type)会出现匹配错误的,查询出来结果了(应该是没有才对)!

http://192.168.1.42:9200/movies/movie/_search {

"query": {
         "constant_score" : {
             "filter" : {
                  "bool" : {
                     "must" : [
                         { "term" : { "tags.id" : 3 } }, //正确的应该是1
                         { "term" : { "tags.type" : "科幻" } } 
                     ]
                 }
             }
         }
     }
 }

要了解事情的真相需要明白这一点,es对嵌套对象文档会处理成扁平式键值对的结构:

{
     "title": "流浪地铁",
     "actor": "吴京",
     "tags.id": [1, 2, 3],
     "tags.type": ["科幻", "灾难", "冒险"] 
 }

扁平化之后也就无法对嵌套对象进行扁平化查询,要查询就需要借助父子文档查询。