elasticsearch 数组的注意事项

我都知道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": ["科幻", "灾难", "冒险"] 
}

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

你可能感兴趣的:(最新,elasticsearch,搜索技术)