Faire un "Or" : Différence entre Queries et Filter

600 views
Skip to first unread message

Thibaut Tropardy

unread,
Feb 18, 2013, 8:05:27 AM2/18/13
to elastics...@googlegroups.com
Bonjour à tous,

Je cherche à faire un or lors d'une réponse sur un index. Par exemple, je veux les produits commençant par "TV..." ou "Magnéto..."

Au début, j'ai regardé dans les queries, sans parvenir à trouvé le Or. J'ai aussi pensé à l'utilisation d'un boolQuery avec 2 should sans must. Mais grâce à la conférence de David Pilato, j'ai compris le vrai sens du should, qui ne fait plus de "ou logique", dès qu'on ajoute un autre critère quelconque.

David a confirmé l'existence d'un "ou", et je viens de voir que le "ou" existe bien, non pas dans les queries, mais dans les filters.

Pour faire mon "ou", il faudrait donc que je fasse un matchAll, pour ensuite appliquer un filtre "ou"?

Je n'arrive pas à bien comprendre la différence entre les queries et filters.
  • En lisant la doc officielle Elastic Search, il est question d'une notion de cache?
  • En regardant ce qui se dit pour Lucene , il est question d'une différence dans la méthode de calcul et le score appliqué, mais j'y comprend pas grand chose.
J'ai lu aussi ceci "The difference between filter and query is mostly that filter is exact ". Du coup, mon "ou logique" avec des termes "Commencant par" ne me parait pas adaptés.

Quelqu'un aurait une explication plus claire sur la différence entre ces 2 notions?

D'avance merci.


Jérôme Mainaud

unread,
Feb 18, 2013, 8:45:59 AM2/18/13
to elastics...@googlegroups.com
Bonjour,

Un filtre est une requête de type booléenne : le résultat est vrai (le document est inclus dans le résultat) ou faux (le document n'est pas présent dans le résultat.)

Une query est une requête de type probabiliste : chaque document qui répond partiellement à la requête se voit attribuer un score en fonction de l'écart qu'il présente avec la requête.
Si tu ne précises pas de tri, les documents sont présentés avec les éléments qui ont le meilleur score en premier.
Ainsi, un boolQuery avec un should représente un "ou" où les documents qui possèdent les deux cas seront présentés en premier. S'il y a d'autres critères, un document peut être ramené qui ne possède aucun des deux critères du "should".

La combinaison d'un filtre et d'une requête revient à faire une requête (query) sur un sous-ensemble de documents déterminé par le filtre. (Sauf pour les facettes où le filtre n'est pas pris en compte.)

Le calcul du score ayant un cout et les filtres pouvant être mis en cache de façon très efficace, il est souvent conseillé d'écrire la partie booléenne d'une requête dans un filtre. Si ta requête est entièrement booléenne, alors, oui, on utilise un match_all avec un filtre. Si tu as des facettes, tu utiliseras un "filtered" avec un match_all et un filtre.

J'espère ne pas t'avoir perdu dans mes explications.

--
Jérôme Mainaud
jer...@mainaud.com


--
--
---
Vous pouvez également poster et consulter les réponses en anglais sur le groupe Elasticsearch https://groups.google.com/group/elasticsearch
 
Si vous avez également posté votre question sur la mailing list elasti...@googlegroups.com, merci d'indiquer ici le lien vers cette discussion pour faciliter le suivi.
 
Twitter : @ElasticsearchFR https://twitter.com/#!/ElasticsearchFR
Site web (English) : http://www.elasticsearch.org/
 
---
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes Elasticsearch FR.
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse elasticsearch-...@googlegroups.com.
Pour plus d'options, visitez le site https://groups.google.com/groups/opt_out .
 
 

Thibaut Tropardy

unread,
Feb 18, 2013, 9:04:37 AM2/18/13
to elastics...@googlegroups.com
On ne peux pas faire plus clair je pense.

J'avais bien vu la notion de "filtered" pour pouvoir utiliser les facets avec les filtres.

Je vais tester tout ça, pour voir si ca marche.

Merci.

Thibaut Tropardy

unread,
Feb 19, 2013, 3:29:17 AM2/19/13
to elastics...@googlegroups.com
J'ai fait ma requête avec l'Api Java, je reviens vers vous, pour avoir votre avis, et si l'exemple est bon, le partager pour que d'autre ait la réponse immédiatement

L'idée est d'avoir les produits commençant par une liste de recherche et dont le numéro de série contient l'un des éléments de la recherche

en SQL: on aurait quelque chose du genre 
(produit LIKE '<MA_RECHERCHE1>%'  OR produit LIKE '<MA_RECHERCHE2>%'  produit LIKE '<MA_RECHERCHE3>%' )
AND (serialNumber LIKE '%<MA_RECHERCHE>%' OR serialNumber LIKE '%<MA_RECHERCHE>%')

Pour faire ça avec l'API Java de Elastic search, il faut utiliser donc Filtres pour avoir le "Ou".

AndFilterBuilder afb = new AndFilterBuilder();
OrFilterBuilder ofb = new OrFilterBuilder();
ofb.add(FilterBuilders.prefixFilter("produit", StringUtils.lowerCase("TV")));//En lower, car il n'y a pas d'analyser pour prefix
ofb.add(FilterBuilders.prefixFilter("produit", StringUtils.lowerCase("Magnéto")));//En lower, car il n'y a pas d'analyser pour prefix
afb.add(ofb);

OrFilterBuilder ofb = new OrFilterBuilder();
ofb.add(FilterBuilders.queryFilter(QueryBuilders.wildcardQuery("serialNumber", "*123*")));//On construit un filter basé sur un querie pour utiliser les wildcards pour faire un contient
ofb.add(FilterBuilders.queryFilter(QueryBuilders.wildcardQuery("serialNumber", "*345*")));
afb.add(ofb);


Je veux aussi que mes facets dépendent de ces filtres.

TermsFacetBuilder facet = new TermsFacetBuilder("type_produit").field("type_produit").facetFilter(afb); //Je précise donc le filtre 


SearchRequestBuilder searchRequestBuilder = client.prepareSearch("gmot");
searchRequestBuilder.setFilter(afb);
searchRequestBuilder.addFacet(facet);
SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();


Mon exemple marche en l'état. Après, vous parait-il cohérent ou non? Suis-je susceptible d'avoir des problèmes de perf avec l'utilisation du wilcard. Existe-il une solution full-filter?

D'avance merci.

Thibaut Tropardy

unread,
Feb 19, 2013, 3:40:43 AM2/19/13
to elastics...@googlegroups.com
Voici le filtre Json généré:

"filter" : {
    "and" : {
      "filters" : [ {
        "term" : {
          "siteId" : 1
        }
      }, {
        "or" : {
          "filters" : [ {
            "prefix" : {
              "produit" : "tv"
            }
          }, {
            "prefix" : {
              "produit" : "magneto"
            }
          } ]
        }
      }, {
        "or" : {
          "filters" : [ {
            "query" : {
              "wildcard" : {
                "serialNumber" : "*123*"
              }
            }
          } ]
        }
      } ]
    }
  }


Ce qui me fait peur, c'est que ce filtre ne semble pas réutilisé pour les facets, mais réécrit et dupliqué?

Est-ce un effet de l'api Java qui duplique l'écriture du filtre? ou est-ce elastic search qui fonctionne comme ça?

Jérôme Mainaud

unread,
Feb 19, 2013, 6:33:12 AM2/19/13
to elastics...@googlegroups.com
Les facettes ne prennent pas en compte le filtre mais seulement la requête.
Il n'est cependant pas nécessaire de dupliquer le filtre si tu utilises une requête de type filtered.

A la fin tu auras ceci au lieu de ton dernier bloc.

SearchRequestBuilder searchRequestBuilder = client.prepareSearch("gmot");
searchRequestBuilder.setQuery(
    QueryBuilders.filteredQuery(
       QueryBuilders.matchAllQuery(),
        afb));
searchRequestBuilder.addFacet(facet);
SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();

Après, tu utilises des requêtes particulièrement couteuses en temps et je pense que tu devrais réfléchir à utiliser un mapping qui correspond mieux à tes requêtes. Regarde notamment du coté des edge ngram et ngram tokenizer.

--
Jérôme Mainaud
jer...@mainaud.com
Message has been deleted

Jérôme Mainaud

unread,
Feb 25, 2013, 2:00:28 PM2/25/13
to elastics...@googlegroups.com
Si ta requête est un commence par, je regarderai plutôt le "edge ngram" avec l'option "front".
Si tu n'utilises le ngram ou le edge ngram uniquement pour l'autocompletion, tu peux utiliser un multi-field comme tu l'as fait pour pour le message avec une version analysée comme un texte normal et une version décomposée.

--
Jérôme Mainaud
jer...@mainaud.com


Le 19 février 2013 17:24, Thibaut Tropardy <ttro...@gmail.com> a écrit :
Je pense effectivement mettre un analyser NGRAM pour le produit. "commencant par" s'y prête bien

Par contre, quand l'utilisateur va saisir les numéro de série en critère, j’envisage de mettre de l'auto-complétion (avec Elastic Search juste sur ce champ, sans les autres facets). le critère utilisé par la suite sera le mot entier.

Une fois le critère saisie, il ne bouge plus, et l'utilisateur va pouvoir mettre à jour les camemberts en fonction des autres donnés (date, etc)

Du coup, la recherche avec toute les facets n'utilisera plus le wildcard, mais le filtre term, ce qui devrait le rendre plus rapide? 

Thibaut Tropardy

unread,
Mar 7, 2013, 9:00:49 AM3/7/13
to elastics...@googlegroups.com
Je reviens vers vous car j'ai encore quelques incompréhension entre les query, filters, filtered.

Voila l'objectif à atteindre, je veux les documents qui contiennent un ou plusieurs mots. Si l'utilisateur saisie 2 mots, je veux impérativement avec les documents qui ont ces 2 mots.
Je pourrais donc m'en sortir en déconcatenant les 2 mots, et en faisant un And.

Le problème, c'est que la recherche sur plusieurs champs : le champ EtapeCode et MesureCode. Avec le And, il me faudrait faire une requête du type:
le premier mot est dans etapeCode, ou dans MesureCode et le deuxième mot est dans etapeCode ou mesureCode. ce qui deviendrait vite compliqué avec 3 ou 4 mots.

J'ai donc pensé utiliser le query_string.
J'ai 2 requêtes Elastic Search, qui sont censé faire la même chose, mais je n'arrive pas à expliquer le résultat.

Pour la première requête, j'ai repris le principe du match_all repris plus haut dans ce sujet.

{
  "query": {
    "filtered": {
      "query": {
        "match_all": {
          
        }
      },
      "filter": {
        "query": {
          "query_string": {
            "query": "attenuation a6",
            "fields": [
              "etapeCode",
              "mesureCode",
              "etapeDesc",
              "mesureDesc"
            ]
          }
        }
      }
    }
  },
  "facets": {
    "etapeMesureCodeTop": {
      "terms": {
        "size": 20,
        "script": "doc['etapeCode'].value +'###'+doc['mesureCode'].value"
      }
    }
  }
}

Ce premier résultat me permet d'avoir les facets correspondant à mes filtres, mais la recherche me retourne les documents contenant attenuation, ou "a6", ce qui ne correspond pas à ce que je veux.


Dans ma deuxième requête, j'ai repris l'exemple donnée sur le guide officiel et j'ai rajouté le query dans les filtered.

{
  "query": {
    "filtered": {
      "query": {
        "query_string": {
          "fields": [
            "etapeCode",
            "etapeDesc",
            "mesureCode",
            "mesureDesc"
          ],
          "query": "atténuation a6"
        }
      }
    }
  },
  "facets": {
    "etapeMesureCodeTop": {
      "terms": {
        "size": 20,
        "script": "doc['etapeCode'].value +'###'+doc['mesureCode'].value+'###'"
      }
    }
  }
}

Avec ce deuxième exemple, pour le coup, les résultats retournés correspondent bien aux documents contenant attenuation et a6, mais les facets ne prenne pas en compte le filtre.

Quel est donc la différence entre le filter -> query -> query_string et le filtered -> query_string ? Je suis un peu perdu.

Jérôme Mainaud

unread,
Mar 8, 2013, 7:46:56 AM3/8/13
to elastics...@googlegroups.com
Dans le deuxième cas, il y a un accent sur "atténuation" qu'il n'y a pas dans le premier cas.
Selon tes analyseurs, il est possible que ça change la donne.

Le résultat attendu d'un query_string avec la chaine "attenuation a6" est un "ou", où les documents possédant les deux termes auront un meilleurs score que les autres, une fois transformée en filtre, le score est perdu et tu as un simple "ou". Tu peux obtenir le résultat attendu avec la chaine "attenuation AND a6" ou "+attenuation +a6".

Cela dit, ton premier cas particulièrement tordu. Tu écris une query qui ramène tout et tu filtres par un filtre qui transpose une recherche.
Dans ton deuxième cas, le filtrered ne sert à rien pluisqu'il n'y a plus de filtre.
Autant écrire :


{
  "query": {
        "query_string": {
          "fields": [
            "etapeCode",
            "etapeDesc",
            "mesureCode",
            "mesureDesc"
          ],
          "query": "atténuation a6"
        }
  },
  "facets": {
    "etapeMesureCodeTop": {
      "terms": {
        "size": 20,
        "script": "doc['etapeCode'].value +'###'+doc['mesureCode'].value+'###'"
      }
    }
  }
}

Les facettes travaillant sur le résultat de la query, feront ce que tu veux. Et si tu veux un ET, il faut écrire la bonne requête.

--
Jérôme Mainaud
jer...@mainaud.com


Message has been deleted

Jérôme Mainaud

unread,
Jan 7, 2014, 5:31:59 PM1/7/14
to elastics...@googlegroups.com
Bonjour,

Tout dépend de ton mapping. C'est pour cela qu'il faut toujours l'indiquer (au moins pour les champs concernés.)

Maintenant que ce passe-t-il si tu fait une recherche avec une query "match", une query "term", ou en mettant la valeur en minuscule "ca000010" ?

Remarque que de base, il est normal que tu ai des résultats différents puisqu'une recherche par préfixe est susceptible de donner plus de résultats qu'une recherche exacte.


--
Jérôme Mainaud
jer...@mainaud.com


Le 6 janvier 2014 19:06, Steeve Danglades <steevens...@gmail.com> a écrit :
Hello,

J'en profite pour poser une question qui va je pense dans la continuite de ce post  - diff entre les queries et filters.

J'execute 2 queries similaires a l'exception que l'une effectue une recherche via query :  "query": "senderFiId:CA000010*"
et la deuxieme utilise les filters "term": { "senderFiId": "CA000010" }
La query 1 me renvoie les resultats attendus mais la query 2 ne revoit rien...

Ce qui est encore plus etrange est que j'ai ce comportement uniquement avec ce field senderFiId, la query 2 fonctionne tres bien avec d'autres field.
Ce field est pourtant de type string comme tous les autres qui fonctionnent.

Any Idea?

Q1:
{
  "from": 0,
  "size": 10,
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ],
  "query": {
    "filtered": {
      "query": {
        "query_string": {
          "query": "senderFiId:CA000010*"
        }
      },
      "filter": {
        "and": [
          {
            "prefix": {
              "status": ""
            }
          },
          {
            "range": {
              "@timestamp": {
                "from": "2014-01-06T16:57:10.456Z",
                "to": "2014-01-06T17:57:10.456Z"
              }
            }
          }
        ]
      }
    }
  },
  "facets": {
    "histo": {
      "date_histogram": {
        "field": "@timestamp",
        "interval": "minute"
      }
    },
    "field1": {
      "terms": {
        "field": "status",
        "size": 10
      }
    },
    "field2": {
      "terms": {
        "field": "senderFiId",
        "size": 10
      }
    },
    "field3": {
      "terms": {
        "field": "recipientFiId",
        "size": 10
      }
    }
  }
}
Q2:
{
  "from": 0,
  "size": 10,
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ],
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      },
      "filter": {
        "and": [
          {
            "prefix": {
              "status": ""
            }
          },
          {
            "term": {
              "senderFiId": "CA000010"
            }
          },
          {
            "range": {
              "@timestamp": {
                "from": "2014-01-06T16:58:20.287Z",
                "to": "2014-01-06T17:58:20.287Z"
              }
            }
          }
        ]
      }
    }
  },
  "facets": {
    "histo": {
      "date_histogram": {
        "field": "@timestamp",
        "interval": "minute"
      }
    },
    "field1": {
      "terms": {
        "field": "status",
        "size": 10
      }
    },
    "field2": {
      "terms": {
        "field": "senderFiId",
        "size": 10
      }
    },
    "field3": {
      "terms": {
        "field": "recipientFiId",
        "size": 10
      }
    }
  }
}

PS :  clavier US dsl pour les accents
Pour envoyer un message à ce groupe, adressez un e-mail à elastics...@googlegroups.com.
Visitez ce groupe à l'adresse http://groups.google.com/group/elasticsearch-fr .
Cette discussion peut être lue sur le Web à l'adresse https://groups.google.com/d/msgid/elasticsearch-fr/2fcbff7b-1f23-4d6f-8c22-92a95a26d547%40googlegroups.com.
Message has been deleted

utilisateur elasticsearch

unread,
Jan 23, 2015, 9:45:55 AM1/23/15
to elastics...@googlegroups.com
Jerome Mainaud 

quand vous dite (Ainsi, un boolQuery avec un should représente un "ou" où les documents qui possèdent les deux cas seront présentés en premier. S'il y a d'autres critères, un document peut être ramené qui ne possède aucun des deux critères du "should".)


le phrase suivant : S'il y a d'autres critères   .. qu'est ce que vous voulez dire par d'autres critére ? d'autres should ou bien un must ? 


car si on combine plusieurs should on reste toujours dans le "ou" logique non ? par contre si un must est ajouté un document repondant a ce critére peut etre ramené meme s'il ne repond a aucun critére des shoulds ...est ce que c'est de ca que vous vouliez parler ?  


Dàja est ce qu'on peut faire plusieurs should (plus de deux) ?


je vous remercie 
Reply all
Reply to author
Forward
0 new messages