ElasticSearch - Renvoyer des valeurs uniques

122

Comment puis-je obtenir les valeurs de tous les à languagespartir des enregistrements et les rendre uniques.

Registres

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

Requete

GET items/_search
{ ... }

# => Expected Response
[10, 11]

Toute aide est la bienvenue.

ChuckJHardy
la source
1
fields: [languages]ne donnera que les valeurs du champ donné, mais les rendre uniques est probablement plus facile à faire dans le code. Cependant, il existe peut-être une agrégation pratique qui peut le faire pour vous.
Ashalynd
1
Pour ceux qui recherchent ce sujet, il y a aussi une discussion utile ici: Trouvez des valeurs distinctes, pas des
nombres

Réponses:

165

Vous pouvez utiliser les termes agrégation .

{
"size": 0,
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  "size" : 500 }
    }
}}

Une recherche renverra quelque chose comme:

{
"took" : 16,
"timed_out" : false,
"_shards" : {
  "total" : 2,
  "successful" : 2,
  "failed" : 0
},
"hits" : {
"total" : 1000000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
  "langs" : {
    "buckets" : [ {
      "key" : "10",
      "doc_count" : 244812
    }, {
      "key" : "11",
      "doc_count" : 136794

    }, {
      "key" : "12",
      "doc_count" : 32312
       } ]
    }
  }
}

Le sizeparamètre dans l'agrégation spécifie le nombre maximal de termes à inclure dans le résultat de l'agrégation. Si vous avez besoin de tous les résultats, définissez cette valeur sur une valeur supérieure au nombre de termes uniques dans vos données.

Anton
la source
2
"fields" : ["language"]ramène le même résultat. Pouvez-vous développer votre réponse pour voir si le cadre d'agrégation peut renvoyer uniquement les valeurs de langue? #=> [10, 11, 10]
ChuckJHardy
1
@CharlesJHardy, ça n'a pas le même résultat. Les données que vous recherchez se trouvent sous la clé "agrégations". J'ai édité ma réponse avec un exemple de résultat. Vous pouvez / devez également définir "taille": 0, afin de ne pas inclure aucun des documents, uniquement les résultats agrégés que vous vouliez.
Anton
1
Notez que si vous avez de nombreuses valeurs possibles pour languagevous pouvez ajouter size=0et shard_size=0, pour vous assurer d'obtenir toutes les valeurs. Voir elasticsearch.org/guide/en/elasticsearch/reference/current/…
Dror
3
Je pense que cette réponse ne concerne pas le PO. La question originale veut que des valeurs distinctes ne comptent pas. Est-ce que je manque quelque chose?
bhurlow
4
@BHBH, La réponse fournit les valeurs distinctes. Ce sont les valeurs «clés», c'est-à-dire «10», «11» et «12». (agrégations> langs> buckets> key ...)
Anton
9

Elasticsearch 1.1+ a l' agrégation de cardinalité qui vous donnera un décompte unique

Notez qu'il s'agit en fait d'une approximation et que la précision peut diminuer avec les ensembles de données à cardinalité élevée, mais elle est généralement assez précise dans mes tests.

Vous pouvez également régler la précision avec le precision_thresholdparamètre. Le compromis, ou bien sûr, est l'utilisation de la mémoire.

Ce graphique de la documentation montre comment une valeur plus élevée precision_thresholdconduit à des résultats beaucoup plus précis.


Erreur relative vs seuil

Bradvido
la source
2
L' agrégation de cardinalité garantit-elle que si un terme existe, il apparaîtra dans les résultats (avec un nombre> = 1)? Ou pourrait-il manquer certains termes qui n'apparaissent qu'une seule fois dans un grand ensemble de données?
marque le
2
@mark cela dépend du seuil de précision que vous définissez. Plus le seuil est élevé, moins il risque de manquer. Notez qu'il existe une limite de 40 000 dans le réglage du seuil de précision. Ce qui signifie qu'un ensemble de données plus élevé que cela, il y aura une estimation et donc la valeur unique peut être manquée
Sundar
12
Je pense que cette réponse est fausse. L'agrégation de cardinalité est un excellent outil. Cependant, la tâche consistait à récupérer les termes eux-mêmes, et non à estimer le nombre de termes différents.
Anton
4

si vous souhaitez obtenir le premier document pour chaque languagevaleur unique de champ, vous pouvez le faire:

{
 "query": {
    "match_all": {
    }
  },
  "collapse": {
    "field": "language.keyword",
    "inner_hits": {
    "name": "latest",
      "size": 1
    }
  }
}
MajidJafari
la source
3

Je recherche également ce genre de solution pour moi-même. J'ai trouvé des références en termes d'agrégation .

Donc, selon ce qui suit est la solution appropriée.

{
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  
                    "size" : 500 }
    }
}}

Mais si vous rencontrez l'erreur suivante:

"error": {
        "root_cause": [
            {
                "type": "illegal_argument_exception",
                "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [fastest_method] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
            }
        ]}

Dans ce cas, vous devez ajouter " KEYWORD " dans la requête, comme suit:

   {
    "aggs" : {
        "langs" : {
            "terms" : { "field" : "language.keyword",  
                        "size" : 500 }
        }
    }}
MAULIK MODI
la source
1

Si vous souhaitez obtenir toutes les valeurs uniques sans aucune approximation ni définir un nombre magique ( size: 500), utilisez AGRÉGATION COMPOSITE (ES 6.5+) .

De la documentation officielle :

"Si vous souhaitez récupérer tous les termes ou toutes les combinaisons de termes dans une agrégation de termes imbriqués, vous devez utiliser l'AGREGATION COMPOSITE qui permet de paginer sur tous les termes possibles plutôt que de définir une taille supérieure à la cardinalité du champ dans l'agrégation des termes. L'agrégation de termes est destinée à renvoyer les termes principaux et ne permet pas la pagination. "

Exemple d'implémentation en JavaScript:

const ITEMS_PER_PAGE = 1000;

const body =  {
    "size": 0, // Returning only aggregation results: https://www.elastic.co/guide/en/elasticsearch/reference/current/returning-only-agg-results.html
    "aggs" : {
        "langs": {
            "composite" : {
                "size": ITEMS_PER_PAGE,
                "sources" : [
                    { "language": { "terms" : { "field": "language" } } }
                ]
            }
        }
     }
};

const uniqueLanguages = [];

while (true) {
  const result = await es.search(body);

  const currentUniqueLangs = result.aggregations.langs.buckets.map(bucket => bucket.key);

  uniqueLanguages.push(...currentUniqueLangs);

  const after = result.aggregations.langs.after_key;

  if (after) {
      // continue paginating unique items
      body.aggs.langs.composite.after = after;
  } else {
      break;
  }
}

console.log(uniqueLanguages);

Ilarion Halushka
la source