ElasticSearch:按嵌套文档的值排序

时间:2013-01-24 14:54:06

标签: java sorting elasticsearch

我在为我的java应用程序使用ElasticSearch时遇到了麻烦。 我自己解释一下,我有一个映射,类似于:

{
"products": {
    "properties": {
        "id": {
            "type": "long",
                   "ignore_malformed": false
        },
        "locations": {
            "properties": {
                "category": {
                    "type": "long",
                   "ignore_malformed": false
                },
                "subCategory": {
                    "type": "long",
                   "ignore_malformed": false
                },
                "order": {
                    "type": "long",
                   "ignore_malformed": false
                }
            }
        },
...

因此,正如您所看到的,我收到了一个由位置组成的产品列表。在我的模型中,这些位置是所有类别的产品。这意味着产品可以是1个或更多类别。在此类别的每个类别中,产品都有一个订单,即客户想要显示的订单。

例如,钻石产品在珠宝首饰中排名第一,但在女士中排名第三(我的例子不是那么逻辑^^)。 所以,当我点击珠宝时,我想展示这个产品,按照特定类别的字段location.order排序。

目前,当我搜索特定类别的所有产品时,我收到的ElasticSearch的响应类似于:

{"id":5331880,"locations":[{"category":5322606,"order":1},
{"category":5883712,"subCategory":null,"order":3},
{"category":5322605,"subCategory":6032961,"order":2},.......

是否可以按我所搜索的特定类别的元素locations.order对此产品进行排序?例如,如果我查询类别5322606,我希望获得此产品的订单1.

事先非常感谢! 问候, 奥利弗。

2 个答案:

答案 0 :(得分:9)

首先修正术语:在Elasticsearch中,“父/子”指的是完全独立的文档,其中子文档指向父文档。父级和子级存储在同一个分片上,但可以单独更新。

通过上面的示例,您可以使用nested文档完成您要实现的目标。

目前,您的locations字段属于type:"object"。这意味着每个位置的值变平,看起来像这样:

{ 
    "locations.category": [5322606, 5883712, 5322605],
    "locations.subCategory": [6032961],
    "locations.order": [1, 3, 2]
}

换句话说,“子”字段会变平为多值字段,这对您没用,因为category: 5322606order: 1之间没有相关性。

但是,如果您将locations更改为type:"nested",那么它会在内部将每个位置编入索引作为单独的文档,这意味着可以使用专用的nested {独立查询每个位置{3}}和query

默认情况下,nested查询会根据每个位置的匹配程度返回_score,但在您的情况下,您希望从任意位置返回order字段的最高值匹配孩子。为此,您需要使用filter查询。

让我们首先创建具有适当映射的索引:

curl -XPUT 'http://127.0.0.1:9200/test/?pretty=1'  -d '
{
   "mappings" : {
      "products" : {
         "properties" : {
            "locations" : {
               "type" : "nested",
               "properties" : {
                  "order" : {
                     "type" : "long"
                  },
                  "subCategory" : {
                     "type" : "long"
                  },
                  "category" : {
                     "type" : "long"
                  }
               }
            },
            "id" : {
               "type" : "long"
            }
         }
      }
   }
}
'

我们将您的示例文档编入索引:

curl -XPOST 'http://127.0.0.1:9200/test/products?pretty=1'  -d '
{
   "locations" : [
      {
         "order" : 1,
         "category" : 5322606
      },
      {
         "order" : 3,
         "subCategory" : null,
         "category" : 5883712
      },
      {
         "order" : 2,
         "subCategory" : 6032961,
         "category" : 5322605
      }
   ],
   "id" : 5331880
}
'

现在我们可以使用上面讨论过的查询来搜索它:

curl -XGET 'http://127.0.0.1:9200/test/products/_search?pretty=1'  -d '
{
   "query" : {
      "nested" : {
         "query" : {
            "custom_score" : {
               "script" : "doc[\u0027locations.order\u0027].value",
               "query" : {
                  "constant_score" : {
                     "filter" : {
                        "and" : [
                           {
                              "term" : {
                                 "category" : 5322605
                              }
                           },
                           {
                              "term" : {
                                 "subCategory" : 6032961
                              }
                           }
                        ]
                     }
                  }
               }
            }
         },
         "score_mode" : "max",
         "path" : "locations"
      }
   }
}
'

注意:脚本中的单引号已转义为\u0027以绕过shell引用。该脚本实际上如下所示:"doc['locations.order'].value"

如果您从结果中查看_score,则可以看到它已使用匹配的order中的location值:

{
   "hits" : {
      "hits" : [
         {
            "_source" : {
               "locations" : [
                  {
                     "order" : 1,
                     "category" : 5322606
                  },
                  {
                     "order" : 3,
                     "subCategory" : null,
                     "category" : 5883712
                  },
                  {
                     "order" : 2,
                     "subCategory" : 6032961,
                     "category" : 5322605
                  }
               ],
               "id" : 5331880
            },
            "_score" : 2,
            "_index" : "test",
            "_id" : "cXTFUHlGTKi0hKAgUJFcBw",
            "_type" : "products"
         }
      ],
      "max_score" : 2,
      "total" : 1
   },
   "timed_out" : false,
   "_shards" : {
      "failed" : 0,
      "successful" : 5,
      "total" : 5
   },
   "took" : 9
}

答案 1 :(得分:0)

只需添加与子字段排序父项相关的更新版本。 我们可以查询按子字段排序的父文档类型('count',例如),类似如下。

https://gist.github.com/robinloxley1/7ea7c4f37a3413b1ca16