2017-08-03 3 views
0

Je suis novice à ES et je recherche sur un ensemble d'enregistrements de 100k données. Voici ma cartographie et la mise en JSON avec lequel j'ai indexé mes données:Optimisation de requête ElasticSearch - API Java

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

documents que je dois interroger sur la base de tous les champs mentionné selon une priorité. Voici ma requête pour rechercher tous les enregistrements.

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(); 

La requête ne me aide dans la recherche de données et fonctionne très bien, mais mon problème est que cela prend beaucoup de temps depuis que je suis en utilisant la requête générique. Est-ce que quelqu'un peut aider à optimiser cette requête ou me guider dans la recherche de la requête la mieux adaptée à ma recherche? TIA.

+0

Pourquoi utilisez-vous les requêtes génériques en premier lieu? Ayant un tokenizer ngram avec 3+, une requête de correspondance normale devrait fonctionner avec des entrées de plus de 2 caractères. Ou quelle est la raison du tokenizer ngram de toute façon? Un sidenote; Avec cet analyseur (tel que défini), vos requêtes seront sensibles à la casse. Peut-être prévu, mais assez inhabituel. – Slomo

+0

Merci @Slomo vous avez raison. Je n'aurais pas dû utiliser des caractères génériques avec ngram. Puis-je le rendre insensible à la casse? et avec ngram je devrais être interroger avec une requête à long terme ou match qui est le moyen le plus optimal? désolé si ce n'est pas une question sensible :) – DivyaMenon

Répondre

1

Tout d'abord, laissez-moi d'abord répondre à la question simple: gérer la sensibilité à la casse. Si vous définissez un analyseur personnalisé, vous pouvez ajouter différents filtres, qui sont appliqués à chaque jeton après l'entrée a été traitée par le tokenizer.

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

Comme vous le voyez, il y a un filtre minuscule existant, qui transformera simplement tous les jetons en minuscules. Je recommande fortement de se référer au documentation. Il existe un lot de ces filtres de jetons.


Maintenant, la partie la plus compliquée: les tokenizers NGram. Encore une fois, pour une meilleure compréhension, vous pouvez lire docs. Mais se référant à votre problème, votre tokenizer va essentiellement créer des termes de longueur 3 à 10. Ce qui signifie que le texte

I am an example TEXT. 

sera essentiellement créer beaucoup de jetons. Juste pour montrer quelques-unes:

  • Taille 3: "Je un", "am", "suis", ..., "TEX", "EXT"
  • Taille 4: "Je suis", "suis", "suis un", ..., "TEX", "TEXTE".
  • Taille 10: "Je suis un ex", ...

Vous voyez l'idée. (Le minuscule filtre de jeton mettrait maintenant ces jetons en minuscules)

Différence entre Correspondance et Terme Requête: Les requêtes de correspondance sont analysées, tandis que les requêtes de termes ne le sont pas. En fait, cela signifie que vos requêtes de correspondance peuvent correspondre à plusieurs termes. Exemple: vous correspondez à exam".

Cela correspondrait à 3 termes: exa, xam et exam.

Ceci a une influence sur le score des correspondances. Plus il y a de matches, plus le score est élevé. Dans certains cas, c'est souhaité, dans d'autres cas non.

Une requête de terme n'est pas analysée, ce qui signifie exam correspondrait, mais seulement un terme (exam bien sûr). Cependant, comme il n'est pas analysé, il n'est pas non plus en minuscules, ce qui signifie que vous devez le faire vous-même dans le code. Exam ne correspondra jamais, car il n'y a pas de terme avec des majuscules dans votre index, si vous utilisez le filtre de jeton minuscule.

Vous n'êtes pas sûr de votre cas d'utilisation. Mais j'ai le sentiment que vous pourriez (ou même vouloir) utiliser le terme query. Mais soyez conscient, il n'y a aucun termes dans votre index avec une taille supérieure à 10. Parce que c'est ce que fait votre ngram-tokenizer.

/EDIT:

Quelque chose convient de signaler en ce qui concerne les requêtes de match, et la raison pour laquelle vous voudrez peut-être utiliser des termes: Certaines requêtes de match comme Simple également correspondre mple de example.

+0

tnx beaucoup @Slomo pour l'explication détaillée. raffinera mon code un dwill passer par le doc aussi bien. :) – DivyaMenon

+0

alors supposons que j'ai besoin de rechercher plusieurs valeurs sur plusieurs champs, bool avec requête de correspondance serait une bonne option non? – DivyaMenon

+1

@DivyaMenon Vous pouvez. Ou peut-être vous pouvez également utiliser le [multiMatch] (https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html) où vous devriez également être en mesure de champs de poids. Peut-être qu'un exemple de requête concret avec des résultats attendus aiderait à répondre à votre question. – Slomo