web-dev-qa-db-fra.com

Filtrer les éléments dont le tableau contient l'une des valeurs données

J'ai un ensemble de documents comme

{
    tags:['a','b','c']
    // ... a bunch properties
}

Comme indiqué dans le titre: Existe-t-il un moyen de filtrer tous les documents contenant l’une des balises données à l’aide de Nest?

Par exemple, l'enregistrement ci-dessus correspondrait à ['c', 'd']

Ou devrais-je créer plusieurs "OU" manuellement?

53
Olivier

Edit: Le contenu de bits ci-dessous est peut-être une lecture intéressante, mais la réponse elle-même est un peu datée. Certaines de ces fonctionnalités évoluent dans les versions 2.x. Slawek fait également remarquer dans une autre réponse que la requête terms est un moyen facile de DRY), dans ce cas, la recherche est rétablie. Refactorisé à la fin pour les meilleures pratiques actuelles. —Nz

Vous voudrez probablement une requête Bool (ou plus probablement Filtre à côté d'une autre requête), avec une clause should.

La requête bool a trois propriétés principales: must, should et must_not. Chacune de celles-ci accepte une autre requête ou un autre tableau de requêtes. Les noms des clauses s’expliquent assez bien; dans votre cas, la clause should peut spécifier une liste de filtres, une correspondance avec l'un quelconque des deux retournera le document que vous recherchez.

De la docs:

Dans une requête booléenne sans clause must, une ou plusieurs clauses should doivent correspondre à un document. Le nombre minimal de clauses should à faire correspondre peut être défini à l'aide de la commande minimum_should_match paramètre.

Voici un exemple de ce à quoi cette requête Bool pourrait ressembler:

{
  "bool": {
    "should": [
      { "term": { "tag": "c" }},
      { "term": { "tag": "d" }}
    ]
  }
}

Et voici un autre exemple de cette requête Bool en tant que filtre dans un contexte plus général Requête filtrée :

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "bool": {
        "should": [
          { "term": { "tag": "c" }},
          { "term": { "tag": "d" }}
        ]
      }
    }
  }
}

Que vous utilisiez Bool en tant que requête (par exemple, pour influencer le score de correspondances) ou comme filtre (par exemple, pour réduire les hits qui sont ensuite marqués ou filtrés ultérieurement) est subjectif, en fonction de vos besoins.

Il est généralement préférable d'utiliser Bool en faveur de ou Filter , sauf si vous avez une raison d'utiliser And/Or/Not (de telles raisons existent). Le blog Elasticsearch contient plus d'informations sur les différentes implémentations de chacune, ainsi que de bons exemples de situations dans lesquelles vous pourriez préférer Bool à And/Or/Not et vice-versa.

Blog Elasticsearch: Tout sur les paquets de filtres Elasticsearch

Mise à jour avec une requête refactorisée ...

Maintenant, avec tous que à l'écart, la requête terms est une version plus sèche de tout ce qui précède. Il fait le bon choix en ce qui concerne le type de requête sous le capot, il se comporte comme le bool + should en utilisant le minimum_should_match _ options, et dans l’ensemble, c’est un peu plus concis.

Voici cette dernière requête un peu remaniée:

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "terms": {
        "tag": [ "c", "d" ],
        "minimum_should_match": 1
      }
    }
  }
}
43
Nick Zadrozny

Il y a aussi termes de requête qui devraient vous épargner du travail. Voici un exemple de docs:

{
  "terms" : {
      "tags" : [ "blue", "pill" ],
      "minimum_should_match" : 1
  }
}

Sous le capot, il construit booléen devrait. Donc, c'est fondamentalement la même chose que ci-dessus mais plus courte.

Il existe également un filtre de termes correspondant .

Donc, pour résumer votre requête pourrait ressembler à ceci:

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "terms": {
        "tags": ["c", "d"]
      }
    }
  }
}

Avec un plus grand nombre de tags, cela pourrait faire toute une différence de longueur.

53
slawek

Alors que c’était une vieille question, j’ai moi-même rencontré ce problème récemment et certaines des réponses fournies ici sont maintenant obsolètes (comme le soulignent les commentaires). Donc, pour le bénéfice des autres personnes qui ont trébuché ici:

Une requête term peut être utilisée pour trouver le terme exact spécifié dans l'index inverse:

{
  "query": {
   "term" : { "tags" : "a" }
} 

De la documentation https://www.elastic.co/guide/fr/elasticsearch/reference/current/query-dsl-term-query.html

Vous pouvez également utiliser une requête terms, qui fera correspondre tous les documents avec l'un des éléments spécifiés dans le tableau donné:

{
  "query": {
   "terms" : { "tags" : ["a", "c"]}
} 

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html

Il faut savoir ce qui m’a retenu: la définition du document fait également une différence. Si le champ dans lequel vous effectuez la recherche a été indexé en tant que type de texte, Elasticsearch effectuera une recherche en texte intégral (c'est-à-dire en utilisant une chaîne analyzed.).

Si vous avez indexé le champ en tant que mot-clé, une recherche par mot-clé utilisant une chaîne "non analysée" est effectuée. Cela peut avoir un impact pratique considérable car les chaînes analysées sont prétraitées (minuscules, ponctuation, etc.). Voir ( https://www.elastic.co/guide/fr/elasticsearch/guide/master/term- vs-full-text.html )

Pour éviter ces problèmes, le champ de chaîne a été divisé en deux nouveaux types: text, à utiliser pour la recherche en texte intégral, et mot-clé, à utiliser pour la recherche par mot-clé. ( https://www.elastic.co/blog/strings-are-dead-long-live-strings )

6
mdmjsh