2016-01-19 121 views
3

我必須在Elasticsearch中構建相當不重要的(因爲它似乎是現在)查詢。 假設我有幾個實體,每個具有一個數組元素,由串:Elasticsearch中的數組元素匹配

1). ['A', 'B'] 
2). ['A', 'C'] 
3). ['A', 'E'] 
4). ['A'] 

映射爲數組元素如下(使用動態模板):

{ 
    "my_array_of_strings": { 
    "path_match": "stringArray*", 
    "mapping": { 
     "type": "string", 
     "index": "not_analyzed" 
    } 
    } 
} 

的Json實體的表示看起來像這樣:

{ 
    "stringArray": [ 
    "A", 
    "B" 
    ] 
} 

然後,我有用戶輸入: [ 'A', 'B', 'C']。

我想要實現的是找到僅包含輸入中指定元素的實體 - 預期結果爲: ['A','B'],['A','C'],['A' ]而不是['A','E'](因爲'E'不存在於用戶輸入中)。

這個場景可以用Elasticsearch實現嗎?

UPDATE: 除了使用腳本的解決方案 - 這應該很好地工作,但最有可能的情況下,大大減慢查詢時也有相匹配的多條記錄 - 我設計了一個又一個。下面我將嘗試解釋它的主要思想,沒有代碼實現。

我沒有提及的一個相當重要的條件是,數組由枚舉元素組成,即數組中有數量有限的元素。這允許將這樣的數組扁平化爲實體的單獨字段。假設有5種可能的值:'A','B','C','D','E'。這些值中的每一個都是布爾型字段 - 如果它是空的(即,數組版本將包含此元素)則爲true,否則爲false。

1). 
A = true 
B = true 
C = false 
D = false 
E = false 

2). 
A = true 
B = false 
C = true 
D = false 
E = false 

3). 
A = true 
B = false 
C = false 
D = false 
E = true 

4). 
A = true 
B = false 
C = false 
D = false 
E = false 

隨着[「A」,「B」,「C」]所有我需要做的用戶輸入是: 一個) 然後如下各實體可以改寫取所有可能的值(['A','B','C','D','E'])並從它們中減去用戶輸入 - >結果將是['D','E']; b)查找記錄,其中每個結果元素是假的,即'D =假AND E =假'。

這將給記錄1,2和4,如預期的那樣。我仍在試驗這種方法的代碼實現,但到目前爲止它看起來很有前途。它還有待測試,但我認爲這可能比在查詢中使用腳本更快,並且資源要求更低。

若要進一步優化這一點,可能不會提供完全爲'false'的字段,並將以前的查詢修改爲'D = not exists AND E = not exists' - result should be一樣。

+0

您可以在映射過程中創建自定義分析器,然後分別搜索每個單詞(逗號前的字符)。 –

+1

您能否提供文檔的映射?對細節提供反饋更容易(基於:是上面的字符串值'[A,E]'還是字符串數組['A','E']?)。我假設['A','E'],但在問題中指定它允許更清晰... – Calle

+0

@Calle,謝謝你指出這一點。我會更新問題 –

回答

0

在我,我也做了後續步驟相同的情況:所有我已經刪除了重新定義analyzer/settingssense plugin指數

第一。

DELETE my_index 

然後,我已經定義的自定義分析for my_index

PUT my_index 
{ 
    "index" : { 
    "analysis" : { 
     "tokenizer" : { 
      "comma" : { 
       "type" : "pattern", 
       "pattern" : "," 
      } 
     }, 
     "analyzer" : { 
      "comma" : { 
       "type" : "custom", 
       "tokenizer" : "comma" 
      } 
     } 
    } 
    } 
} 

我的代碼裏面然後我定義映射屬性,但你也可以做到這一點的意義。他們都是一樣的。

PUT /my_index/_mapping/my_type 
{ 
     "properties" : { 
      "conduct_days" : { 
       "type" : "string", 
       "analyzer" : "comma" 
      } 
     } 
} 

然後測試做這些波紋管步驟:

PUT /my_index/my_type/1 
{ 
    "coduct_days" : "1,2,3" 
} 

PUT /my_index/my_type/2 
{ 
    "conduct_days" : "3,4" 
} 

PUT /my_index/my_type/3 
{ 
    "conduct_days" : "1,6" 
} 

GET /my_index/_search 
{ 
    "query": {"match_all": {}} 
} 

GET /my_index/_search 
{ 
    "filter": { 
     "or" : [ 
      { 
      "term": { 
       "coduct_days": "6" 
      } 
      }, 
      { 
      "term": { 
       "coduct_days": "3" 
      } 
      } 
     ] 
    } 
} 
+0

有趣的...在你的情況下做這個工作嗎?使用您指定的查詢,不能找到記錄。但更令人驚訝的是,它沒有發現任何使用「conduct_days」的任何組合 - 我在單個查詢中嘗試了「1」,「2」和「3」(它肯定會返回第1條記錄),而且什麼都沒有。 –

+0

是的,在我的情況下,它正在工作。 'conduct_days'具有'1,2,3,4,5'值,輸入數據可以包含'1'或類似'1,2,4',我可以分別搜索每個輸入值。 –

4

您可以scripting實現這一目標,這是它的外觀

{ 
    "query": { 
    "filtered": { 
     "filter": { 
     "bool": { 
      "must": [ 
      { 
       "terms": { 
       "name": [ 
        "A", 
        "B", 
        "C" 
       ] 
       } 
      }, 
      { 
       "script": { 
       "script": "if(user_input.containsAll(doc['name'].values)){return true;}", 
       "params": { 
        "user_input": [ 
        "A", 
        "B", 
        "C" 
        ] 
       } 
       } 
      } 
      ] 
     } 
     } 
    } 
    } 
} 

groovy script如果列表中包含任何被檢查除['A', 'B', 'C']之外,如果它返回false,則不返回['A', 'E']。它只是檢查子列表的匹配。該腳本可能需要幾秒鐘的時間。您需要啓用dynamic scripting,對於ES 2.x,語法也可能不同,請告訴我它是否無效。

編輯1

我已經把兩個條件中僅filter。首先只返回具有A,B或C的文檔,然後腳本僅應用於這些文檔,因此這會比前一個更快。更多關於filter ordering

希望這有助於!

+0

非常感謝這個想法,我一定會試一試!不過,我已經讀過這個腳本,可以大大減緩查詢的速度。在我的情況下,可能會返回多達10k個結果,因此對每個結果運行腳本可能會很慢。我已經設計了另一個解決方案,我將嘗試實現 - 我將更新我的文章 –

+0

是的,你說得對,腳本確實減慢了查詢速度,但它不應該超過幾秒鐘(最多4-5秒,我是猜測),但也讓我們知道您的解決方案,同時我也會考慮其他事情,也許我們可以在分析階段做些事情。 – ChintanShah25

+0

我編輯了我的答案,使它比前一個更快。請看看。 – ChintanShah25