2017-05-11 3 views
0

Je suis nouveau chez ElasticSearch. Auparavant, je ne l'ai utilisé qu'avec Django-Haystack, de façon très limitée, et je n'ai jamais parlé directement à ES.Termes de la commande Elasticsearch agrégés par score

Actuellement, j'ai un index ElasticSearch (5.x si cela compte) avec quelques documents. J'utilise Python + elasticsearch-dsl + django-elasticsearch-dsl donc j'indexe les modèles de base de données, mais cela ne devrait pas vraiment importer. Je vais essayer de laisser cette question agnostique à la bibliothèque.

Conceptuellement, je stocke les utilisateurs et leurs publications dans le même index. Les documents pour les utilisateurs et pour les publications ont une chose en commun: un champ user_id.

Les utilisateurs ressemblent à ceci:

{ 
    "_id": 1, 
    "_type": "user_document", 
    "username": "jdoe", 
    "user_id": 1, 
    "title": "Test user" 
} 

Et les messages sont comme ceci:

{ 
    "_id": 1, 
    "_doc": "post_document", 
    "user_id": 1, 
    "title": "Hello world!", 
    "text": "Lorem ipsum test test test..." 
} 

Ce que je veux que mon application à mettre en œuvre est un champ de recherche unique entrée qui fait la recherche en texte intégral sur les utilisateurs et leurs messages (dans le monde réel, il y a plus de «types» de documents - je simplifie les choses ici, juste à titre d'exemple). Et je veux regrouper par user_id pour montrer juste une liste des utilisateurs distincts qui ont correspondu.

Actuellement, je fais requête comme ceci:

{ 
    "query": { 
     "multi_match": { 
      "query": "test", 
      "fields": ["username^3", "title^2", "text"] 
     } 
    }, 
    "aggs": { 
     "user_ids": {"terms": {"field": "user_id"}} 
    } 
} 

Ensuite, en utilisant la réponse de aggregations.user_ids.buckets.key pour obtenir une liste d'utilisateurs correspondant. Cependant, cette liste semble être simplement classée par nombre de documents (donc si l'utilisateur a une paire de messages avec le mot "test" ils semblent gagner sur l'utilisateur nommé "test"), et je veux expérimenter avec commander. Mon idée actuelle consiste à utiliser une correspondance de document moyenne (ou valeur médiane) _score.

Remarque: en situation réelle, il y a plus que deux types de document, prendre un raccourci et interroger juste un _type spécifique ne fonctionnera pas.

Comment est-ce que je peux faire ceci? Je suis en train de lire le chapitre "Sorting by a Metric", mais les idées sont un peu perdues pour moi. J'ai fait quelques tentatives mais elles étaient fondamentalement absurdes. Quelqu'un peut-il s'il vous plaît montrer un exemple de requête concrète (très préférablement, avec une explication comment il a été construit), afin que je puisse en tirer des leçons?

Voici le Gist with an example dataset, la requête de recherche ci-dessus, et les résultats exacts que je reçois. Ce que je veux (en test_query_01_results.json) est d'avoir user_id 1 sur 2 être hiérarchisés, avec la logique que 2,0794415> (0,78306973 + 0,45315093)/2.

Une autre chose que je sens que je fais est que mal je ne » t utiliser hits du tout - je n'en ai pas besoin - seulement les valeurs agrégées user_id. Si cela est correct, existe-t-il un moyen de les "désactiver" et de ne renvoyer que les agrégations?

Répondre

3

Utiliser requête suivante

{ 
"size": 0 ,     ==> to return no hits 
"query": {      ==> query similar to yours 
    "multi_match": { 
     "query": "test", 
     "fields": ["username^3", "title^2", "text"] 
    } 
}, 
"aggs": { 
    "user_ids": { 
     "terms": { 
      "field": "user_id", 
      "order": {"avg_score": "desc"} 
     }, 
     "aggs": { 
      "avg_score": { 
       "avg": {"script": "_score"} 
       } 
      } 
     } 
    } 
    } 
+0

merci. ceci est sauvé mon temps. – Arafath

0

Je pense avoir trouvé une solution pour trier l'agrégation. J'ai dû créer une sous-agrégation, alors tout a fonctionné. Je me suis trompé, en essayant d'utiliser "order": {"_score: "desc"} et des bêtises similaires quand il n'y avait pas _score là (c'est une collection de documents, pas un document, donc aucun score là-bas).

{ 
    "query": { 
     "multi_match": { 
      "query": "test", 
      "fields": ["username^3", "title^2", "text"] 
     } 
    }, 
    "aggs": { 
     "user_ids": { 
      "terms": { 
       "field": "user_id", 
       "order": {"avg_score": "desc"} 
      }, 
      "aggs": { 
       "avg_score": { 
        "avg": {"script": "_score"} 
       } 
      } 
     } 
    } 
} 

Avec ce mon aggregations regarde exactement comme je voulais:

... 
"aggregations": { 
    "user_ids": { 
     "buckets": [ 
      { 
       "avg_score": {"value": 2.079441547393799}, 
       "doc_count": 1, 
       "key": 1 
      }, 
      { 
       "avg_score": {"value": 0.618110328912735}, 
       "doc_count": 2, 
       "key": 2 
      } 
     ], 
     "doc_count_error_upper_bound": 0, 
     "sum_other_doc_count": 0 
    } 
}, 
... 

Cependant, la question d'avoir hits (que je ne l'utilise) tient toujours.