我都知道lucene是不区分数组类型的,而elasticsearch中没有专用的数组类型,对 Array datatype支持不需要专用类型(理解为动态的字符串或object类型)。默认情况下,任何字段都可以包含零个或多个值,但数组中的所有值都必须具有相同的数据类型。本文基于es 6.2,比如:
"one"
, "two"
]1
, 2
]{ "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": "冒险"
}]
}
还是按之前的套路,首先建mappinghttp://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": ["科幻", "灾难", "冒险"]
}
扁平化之后也就无法对嵌套对象进行扁平化查询,要查询就需要借助父子文档查询。