la requête booléenne elasticsearch combine must avec OR

181

J'essaye actuellement de migrer une application basée sur solr vers elasticsearch.

J'ai cette question lucene

(( 
    name:(+foo +bar) 
    OR info:(+foo +bar) 
)) AND state:(1) AND (has_image:(0) OR has_image:(1)^100)

Pour autant que je sache, il s'agit d'une combinaison de clauses MUST combinées avec booléen OR:

"Obtenez tous les documents contenant (foo AND bar in name) OR (foo AND bar in info). Après cela, filtrez les résultats par état de la condition = 1 et améliorez les documents qui ont une image."

J'ai essayé d'utiliser une requête booléenne avec MUST mais je n'arrive pas à obtenir boolean OR dans les clauses must. Voici ce que j'ai:

GET /test/object/_search
{
  "from": 0,
  "size": 20,
  "sort": {
    "_score": "desc"
  },
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "foo"
          }
        },
        {
          "match": {
            "name": "bar"
          }
        }
      ],
      "must_not": [],
      "should": [
        {
          "match": {
            "has_image": {
              "query": 1,
              "boost": 100
            }
          }
        }
      ]
    }
  }
}

Comme vous pouvez le voir, les conditions MUST pour "info" sont manquantes.

Quelqu'un a-t-il une solution?

Merci beaucoup.

** METTRE À JOUR **

J'ai mis à jour ma requête elasticsearch et supprimé ce score de fonction. Mon problème de base existe toujours.

Jesse
la source
1
Il existe une bonne documentation sur la combinaison des requêtes ElasticSearch ici: élastique.co
guide/en

Réponses:

426
  • OR est orthographié devrait
  • AND est orthographié must
  • NOR est orthographié should_not

Exemple:

Vous voulez voir tous les éléments qui sont (ronds ET (rouge OU bleu)):

{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {"shape": "round"}
                },
                {
                    "bool": {
                        "should": [
                            {"term": {"color": "red"}},
                            {"term": {"color": "blue"}}
                        ]
                    }
                }
            ]
        }
    }
}

Vous pouvez également faire des versions plus complexes de OR, par exemple si vous voulez faire correspondre au moins 3 sur 5, vous pouvez spécifier 5 options sous "devrait" et définir un "minimum_should" de 3.

Merci à Glen Thompson et Sebastialonso d'avoir trouvé où ma nidification n'était pas tout à fait juste avant.

Merci également à Fatmajk pour avoir signalé que «terme» devient «match» dans ElasticSearch 6.

Daniel Fackrell
la source
2
Est-ce que tirer le shoulddans le niveau supérieur bool, et y compris un minimum_should_match: 1travail?
Sid
18
Quand j'essaye cet exemple, je reviens [term] malformed query, expected [END_OBJECT] but found [FIELD_NAME]. Cela dépend-il en quelque sorte de la version?
DanneJ
26
Pourquoi n'ajoutent-ils pas un exemple et une explication aussi simples dans la documentation! L'exemple de la documentation est très déroutant.
Nikhil Owalekar
21
Après 6 mois, en lisant toute la documentation Elastic, c'est la première fois que je comprends complètement comment implémenter la logique booléenne. La documentation officielle manque de clarté à mon avis.
Sebastialonso
3
@Amir Quelles inexactitudes puis-je nettoyer pour vous? Dans le contexte illustré ci-dessus, la valeur par défaut minimum_shouldest 1, et l'encapsulation de ce boolrésultat dans ce groupe est true si au moins un élément correspond, false si aucun ne correspond. Ma motivation pour créer cette réponse était que je résolvais exactement ce genre de problème, et la documentation disponible et même les réponses que je pouvais trouver sur des sites comme celui-ci n'étaient au mieux d'aucune utilité, alors j'ai continué à chercher jusqu'à ce que je sente que j'avais une assez bonne compréhension de ce qui se passait. J'accueille volontiers tous les conseils constructifs sur la façon dont je peux encore améliorer la réponse.
Daniel Fackrell
69

J'ai finalement réussi à créer une requête qui fait exactement ce que je voulais:

Une requête booléenne imbriquée filtrée. Je ne sais pas pourquoi cela n'est pas documenté. Peut-être que quelqu'un ici peut me le dire?

Voici la requête:

GET /test/object/_search
{
  "from": 0,
  "size": 20,
  "sort": {
    "_score": "desc"
  },
  "query": {
    "filtered": {
      "filter": {
        "bool": {
          "must": [
            {
              "term": {
                "state": 1
              }
            }
          ]
        }
      },
      "query": {
        "bool": {
          "should": [
            {
              "bool": {
                "must": [
                  {
                    "match": {
                      "name": "foo"
                    }
                  },
                  {
                    "match": {
                      "name": "bar"
                    }
                  }
                ],
                "should": [
                  {
                    "match": {
                      "has_image": {
                        "query": 1,
                        "boost": 100
                      }
                    }
                  }
                ]
              }
            },
            {
              "bool": {
                "must": [
                  {
                    "match": {
                      "info": "foo"
                    }
                  },
                  {
                    "match": {
                      "info": "bar"
                    }
                  }
                ],
                "should": [
                  {
                    "match": {
                      "has_image": {
                        "query": 1,
                        "boost": 100
                      }
                    }
                  }
                ]
              }
            }
          ],
          "minimum_should_match": 1
        }
      }    
    }
  }
}

En pseudo-SQL:

SELECT * FROM /test/object
WHERE 
    ((name=foo AND name=bar) OR (info=foo AND info=bar))
AND state=1

Veuillez garder à l'esprit que cela dépend de l'analyse de votre champ de document et des mappages de la manière dont name = foo est géré en interne. Cela peut varier d'un comportement flou à strict.

"minimum_should_match": 1 dit qu'au moins une des instructions should doit être vraie.

Cela signifie que chaque fois qu'il y a un document dans le jeu de résultats qui contient has_image: 1, il est amplifié par un facteur 100. Cela change l'ordre des résultats.

"should": [
  {
    "match": {
      "has_image": {
        "query": 1,
        "boost": 100
      }
    }
   }
 ]

Amusez-vous les gars :)

Jesse
la source
28
Putain de merde. Quelqu'un at-il une meilleure solution? Merci de poster ceci, mais c'est vraiment trop de complexité pour obtenir un OU logique dans une requête.
nackjicholson
thnx, tu as sauvé ma journée)
cubbiu
3
Non seulement cette requête est inutilement longue, mais elle utilise une syntaxe obsolète. La réponse @ daniel-fackrell doit être celle acceptée.
Eric Alford
4
@EricAlford Cette réponse de 2015 est basée sur une version antérieure d'ES. N'hésitez pas à proposer une meilleure solution.
Jesse
1
Idée: reprendre / fork ElasticSearch, le réécrire de manière conviviale, y ajouter un langage de requête simple, GAGNER! Nous avons juste besoin de financement. J'en suis! Qui d'autre ?
Sliq
16

C'est ainsi que vous pouvez imbriquer plusieurs requêtes booléennes dans une seule requête booléenne externe en utilisant Kibana,

booléen indique que nous utilisons boolean

le must est pour ET

devrait être pour OU

GET my_inedx/my_type/_search
{
    "query" : {
       "bool": {             //bool indicates we are using boolean operator
            "must" : [       //must is for **AND**
                 {
                   "match" : {
                         "description" : "some text"  
                     }
                 },
                 {
                    "match" :{
                          "type" : "some Type"
                     }
                 },
                 {
                    "bool" : {          //here its a nested boolean query
                          "should" : [  //should is for **OR**
                                 {
                                   "match" : {
                                       //ur query
                                  }
                                 },
                                 { 
                                    "match" : {} 
                                 }     
                               ]
                          }
                 }
             ]
        }
    }
}

Voici comment vous pouvez imbriquer une requête dans ES

Il y a plus de types dans "bool" comme -

  1. Filtre

  2. ne doit pas

niranjan harpale
la source
Votre réponse est tout à fait juste, mais c'est un peu maladroit, c'est une petite suggestion pour vous si vous le souhaitez - vous devez l'éditer correctement. Probablement cela vous donne plus comme sur cette réponse :) Bonne journée.
Dhwanil Patel le
6

J'ai récemment dû résoudre ce problème aussi, et après beaucoup d'essais et d'erreurs, je suis venu avec ceci (en PHP, mais mappé directement sur le DSL):

'query' => [
    'bool' => [
        'should' => [
            ['prefix' => ['name_first' => $query]],
            ['prefix' => ['name_last' => $query]],
            ['prefix' => ['phone' => $query]],
            ['prefix' => ['email' => $query]],
            [
                'multi_match' => [
                    'query' => $query,
                    'type' => 'cross_fields',
                    'operator' => 'and',
                    'fields' => ['name_first', 'name_last']
                ]
            ]
        ],
        'minimum_should_match' => 1,
        'filter' => [
            ['term' => ['state' => 'active']],
            ['term' => ['company_id' => $companyId]]
        ]
    ]
]

Qui correspond à quelque chose comme ça dans SQL:

SELECT * from <index> 
WHERE (
    name_first LIKE '<query>%' OR
    name_last LIKE '<query>%' OR
    phone LIKE  '<query>%' OR
    email LIKE '<query>%'
)
AND state = 'active'
AND company_id = <query>

La clé de tout cela est le minimum_should_matchcadre. Sans cela, le filterremplace totalement le should.

J'espère que cela aide quelqu'un!

Benjamin Dowson
la source
0
$filterQuery = $this->queryFactory->create(QueryInterface::TYPE_BOOL, ['must' => $queries,'should'=>$queriesGeo]);

Dans mustvous devez ajouter le tableau de conditions de requête avec lequel vous souhaitez travailler ANDet dans shouldvous devez ajouter la condition de requête avec laquelle vous souhaitez travaillerOR .

Vous pouvez vérifier ceci: https://github.com/Smile-SA/elasticsuite/issues/972

Alakh Kumar
la source
0

Si vous utilisiez l'analyseur de requête par défaut de Solr ou Lucene, vous pouvez à peu près toujours le mettre dans une requête de chaîne de requête:

POST test/_search
{
  "query": {
    "query_string": {
      "query": "(( name:(+foo +bar) OR info:(+foo +bar)  )) AND state:(1) AND (has_image:(0) OR has_image:(1)^100)"
    }
  }
}

Cela dit, vous pouvez utiliser une requête booléenne , comme celle que vous avez déjà publiée, ou même une combinaison des deux.

Radu Gheorghe
la source