2013-01-24 96 views
6

我在爲我的Java應用程序使用ElasticSearch時遇到了麻煩。 我解釋一下,我有一個映射,這是一樣的東西: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 
       } 
      } 
     }, 
... 

所以,你可以看到,我收到的產品,這是由位置的列表。在我的模型中,這些位置是所有類別的產品。這意味着一個產品可以在一個或多個類別中。在這個類別的每個類別中,產品都有一個訂單,這是客戶想要向他們展示的訂單。例如,鑽石產品可以在珠寶首飾中佔第一位,但在女性中佔第三位(我的例子不是那麼邏輯^^)。 所以,當我點擊珠寶首飾時,我想展示這個產品,按照這個特定類別的字段locations.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。

非常感謝你事先! 問候, 奧利維爾。

回答

9

首先對術語進行修正:在Elasticsearch中,「父/子」是指完全獨立的文檔,其中子文檔指向父文檔。父母和孩子存儲在同一個分片上,但它們可以獨立更新。

用上面的示例,您可以使用nested文檔完成您試圖實現的內容。

當前,您的locations字段爲type:"object"。這意味着,在每個位置的值獲得夷爲平地,是這個樣子:

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

換句話說,「分」字段將會展開到多值字段,這是沒有用的,你的,因爲有在category: 5322606order: 1之間沒有相關性。

但是,如果你改變locationstype:"nested"然後內部它將索引每個位置作爲一個單獨的文檔,這意味着每個位置可以獨立使用專用nestedqueryfilter進行查詢,。

默認情況下,nested查詢將返回根據每個位置的匹配程度如何的_score,但在你的情況下,你想從任何匹配的兒童返回order領域的最高值。爲此,您需要使用custom_score查詢。

因此,讓我們通過與適當的映射創建索引開始:

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" 
      } 
     } 
     } 
    } 
} 
' 

在我們索引你的榜樣DOC:

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從結果來看,你可以看到它已經從匹配location使用的order值:

{ 
    "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 
} 
+0

你好DrTech, 非常感謝你的回答,這正是我一直在尋找。我修改了我的映射,它工作正常。 但是,我不知道如何使用java API執行custom_score請求,並且沒有提及它。 你能幫我解決嗎? 再次感謝您!非常感謝。 祝您週末愉快。 Olivier –

+0

然後您應該接受答案:) – DrTech

+0

我等待知道您是否對java API有一些反饋。 我剛剛接受了答案,因爲它很好地解釋了我的理解現在好多了。 你對java API有一些瞭解嗎? 非常感謝您事先。 –