web-dev-qa-db-fra.com

Comment obtenir une agrégation Elasticsearch avec plusieurs champs

J'essaie de trouver des tags associés à celui actuellement affiché. Chaque document de notre index est balisé. Chaque balise est formée de deux parties - un ID et un nom de texte:

{
    ...
    meta: {
        ...
        tags: [
            {
                id: 123,
                name: 'Biscuits'
            },
            {
                id: 456,
                name: 'Cakes'
            },
            {
                id: 789,
                name: 'Breads'
            }
        ]
    }
}

Pour récupérer les balises associées, je recherche simplement les documents et j'obtiens un agrégat de leurs balises:

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "item.meta.tags.id": "123"
                    }
                },
                {
                    ...
                }
            ]
        }
    },
    "aggs": {
        "baked_goods": {
            "terms": {
                "field": "item.meta.tags.id",
                "min_doc_count": 2
            }
        }
    }
}

Cela fonctionne parfaitement, j'obtiens les résultats que je veux. Cependant, j'ai besoin à la fois du tag ID et nom pour faire quoi que ce soit d'utile. J'ai exploré comment y parvenir, les solutions semblent être:

  1. Combinez les champs lors de l'indexation
  2. Un script pour fusionner les champs
  3. Une agrégation imbriquée

Les options un et deux ne sont pas disponibles pour moi, donc je suis allé avec 3 mais il ne répond pas de la manière attendue. Étant donné la requête suivante (toujours à la recherche de documents également étiquetés avec "Biscuits"):

{
    ...
    "aggs": {
        "baked_goods": {
            "terms": {
                "field": "item.meta.tags.id",
                "min_doc_count": 2
            },
            "aggs": {
                "name": {
                    "terms": {
                        "field": "item.meta.tags.name"
                    }
                }
            }
        }
    }
}

J'obtiendrai ce résultat:

{
    ...
    "aggregations": {
        "baked_goods": {
            "buckets": [
                {
                    "key": "456",
                    "doc_count": 11,
                    "name": {
                        "buckets": [
                            {
                                "key": "Biscuits",
                                "doc_count": 11
                            },
                            {
                                "key": "Cakes",
                                "doc_count": 11
                            }
                        ]
                    }
                }
            ]
        }
    }
}

L'agrégation imbriquée comprend à la fois le terme de recherche et la balise que je recherche (renvoyée par ordre alphabétique).

J'ai essayé d'atténuer cela en ajoutant un exclude à l'agrégation imbriquée, mais cela a trop ralenti la requête (environ 100 fois pour 500 000 documents). Jusqu'à présent, la solution la plus rapide consiste à déduper le résultat manuellement.

Quelle est la meilleure façon d'obtenir une agrégation de balises avec à la fois l'ID de balise et le nom de balise dans la réponse?

Merci d'être arrivé si loin!

37
i_like_robots

De par son apparence, votre tags n'est pas nested. Pour que cette agrégation fonctionne, vous en avez besoin nested pour qu'il y ait une association entre un id et un name. Sans nested la liste des ids est juste un tableau et la liste des names est un autre tableau:

    "item": {
      "properties": {
        "meta": {
          "properties": {
            "tags": {
              "type": "nested",           <-- nested field
              "include_in_parent": true,  <-- to, also, keep the flat array-like structure
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }

Notez également que j'ai ajouté au mappage cette ligne "include_in_parent": true ce qui signifie que vos balises nested se comporteront également comme une structure de type tableau "plat".

Ainsi, tout ce que vous aviez jusqu'à présent dans vos requêtes fonctionnera toujours sans aucune modification des requêtes.

Mais, pour votre requête particulière, l'agrégation doit changer en quelque chose comme ceci:

{
  "aggs": {
    "baked_goods": {
      "nested": {
        "path": "item.meta.tags"
      },
      "aggs": {
        "name": {
          "terms": {
            "field": "item.meta.tags.id"
          },
          "aggs": {
            "name": {
              "terms": {
                "field": "item.meta.tags.name"
              }
            }
          }
        }
      }
    }
  }
}

Et le résultat est comme ceci:

   "aggregations": {
      "baked_goods": {
         "doc_count": 9,
         "name": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
               {
                  "key": 123,
                  "doc_count": 3,
                  "name": {
                     "doc_count_error_upper_bound": 0,
                     "sum_other_doc_count": 0,
                     "buckets": [
                        {
                           "key": "biscuits",
                           "doc_count": 3
                        }
                     ]
                  }
               },
               {
                  "key": 456,
                  "doc_count": 2,
                  "name": {
                     "doc_count_error_upper_bound": 0,
                     "sum_other_doc_count": 0,
                     "buckets": [
                        {
                           "key": "cakes",
                           "doc_count": 2
                        }
                     ]
                  }
               },
               .....
48
Andrei Stefan