2017-08-03 150 views
0

我是ES新手,正在搜索100k数据的记录集。 这里是我的映射和设置JSON与我所收录我的数据:ElasticSearch查询优化 - Java API

setings.json

{ 
    "index": { 
     "analysis": { 
      "tokenizer": { 
       "ngram_tokenizer": { 
        "type": "ngram", 
        "min_gram": 3, 
        "max_gram": 10 
       } 
      }, 
      "analyzer": { 
       "ngram_tokenizer_analyzer": { 
        "type": "custom", 
        "tokenizer": "ngram_tokenizer" 
       } 
      } 
     } 
    } 
} 

mappings.json

{ 
    "product": { 
     "properties": { 
      "name": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "description": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "vendorModelNumber": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "brand": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "specifications": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "upc": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "storeSkuId": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      }, 
      "modelNumber": { 
       "type": "string", 
       "analyzer": "ngram_tokenizer_analyzer", 
       "store": true 
      } 
     } 
    } 
} 

我需要查询基于所有域的文档根据一些优先级提到。这是我的查询来搜索所有记录。

BoolQueryBuilder query = QueryBuilders.boolQuery(); 
int boost = 7; 

for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("name", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("description", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("modelNumber", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("vendorModelNumber", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("storeSkuId", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("upc", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
boost--; 
for (String str : dataSplit) { 
    query.should(QueryBuilders.wildcardQuery("brand", "*" + str.toLowerCase() + "*").boost(boost)); 
} 
client.prepareSearch(index).setQuery(query).setSize(200).setExplain(true).execute().actionGet(); 

查询确实帮助我在搜索数据和工作正常,但我的问题是,它需要大量的时间,因为我使用通配符查询。 有人可以帮助优化此查询或指导我为我的搜索找到最合适的查询吗? TIA。

+0

为什么你首先使用通配符查询?拥有3+的ngram标记器,普通匹配查询应该能够处理超过2个字符的输入。或者ngram tokenizer的原因是什么?一个旁注;使用此分析器(如定义),您的查询将区分大小写。可能有意,但很不寻常。 – Slomo

+0

谢谢@Slomo你是对的。我不应该用ngram使用通配符。我可以让它不区分大小写吗?和ngram我应该查询与术语查询或匹配这是更优化的方式?对不起,如果这不是一个明智的问题:) – DivyaMenon

回答

1

首先,让我先回答一个简单的问题:处理大小写敏感。如果您定义自定义分析器,则可以添加不同的筛选器,这些筛选器将在输入已由标记器处理后应用于每个标记

{ 
"index": { 
    "analysis": { 
     "tokenizer": { 
      "ngram_tokenizer": { 
       "type": "ngram", 
       "min_gram": 3, 
       "max_gram": 10 
      } 
     }, 
     "analyzer": { 
      "ngram_tokenizer_analyzer": { 
       "type": "custom", 
       "tokenizer": "ngram_tokenizer", 
       "filter": [ 
        "lowercase", 
        ... 
       ] 
      } 
     } 
    } 
} 

正如你看到的,还有现有小写过滤器,这将根本改变所有标记为小写。我强烈建议参考documentation。这些令牌过滤器有lot


现在比较复杂的部分:NGram标记器。再次,为了更深入的理解,您可能需要阅读docs。但提到你的问题,你的分词器将主要创建长度为3的方面10.这意味着文本

I am an example TEXT. 

基本上都会创造大量的记号。只是为了显示几个:

  • 大小3: “我是”, “上午”, “我”,..., “TEX”, “EXT”
  • 大小4: “我是”, “am”,“am a”,...,“TEX”,“TEXT”。
  • 尺寸10: “我是一名前”,...

你的想法。 (小写标记过滤器会将这些标记小写)

匹配和术语查询之间的区别:匹配查询被分析,而术语查询不是。事实上,这意味着您的匹配查询可以匹配多个条款。例如:你匹配exam"

这实际上会匹配3个术语:exa,xamexam

这会影响比赛的比分。比赛越多,得分越高。在某些情况下,这是期望的,在其他情况下则不是。

不分析术语查询,这意味着exam可以匹配,但只有一个术语(当然是exam)。但是,由于没有进行分析,因此它也不是小写,这意味着您必须自己编写代码。 Exam永远不会匹配,因为如果您使用小写的tokenfilter,则索引中没有大写字母。

不确定您的使用情况。但我有一种感觉,你可以(或甚至想要)确实使用术语查询。 但是请注意,在您的索引中有大于10的大小。因为这就是您的ngram-tokenizer所做的。

/编辑:

东西值得指出的关于比赛的查询,以及为什么你可能想使用方面的原因:一些比赛查询,如Simple也将从example匹配mple

+0

tnx很多@Slomo的详细解释。将改进我的代码,以及通过文档以及。 :) – DivyaMenon

+0

所以假设我需要搜索多个字段上的多个值,布尔与匹配查询将是一个很好的选择权? – DivyaMenon

+1

@DivyaMenon你可以。或者,也许你也可以使用[multiMatch](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html),你也应该能够重量领域。也许具有预期结果的具体查询例子有助于回答你的问题。 – Slomo