Etude de cas - optimisation de requête ELS


(Blured Derulb) #1

Bonjour,

Je voudrais pour obtenir les résultats de ma recherche selon cet ordre :

  1. Documents qui matchent exactement exactement (Tous les mots doivent exister dans les documents retournés, les documents retournés ne doivent pas avoir plus de mot que ceux recherchés)

  2. Documents qui matchent partiellement (Tous les mots recherchés doivent exister (+ synonymes éventuels + fuzzy –inversion de caractère -)

  3. Documents qui matchent en fuzzy (Tous les mots recherchés doivent exister + fuzzy correction + phonetic)

  4. Documents qui matchent en related (50% des mots de la recherche doivent être trouvés)

J’ai pris le parti de faire 4 requêtes Elastic et de merger les résultats en java, ça fonctionne mais ça me semble un peu usine à gaz. N’y aurait il pas moyen de faire une seule requête elastic à la place ?

Voici le détail de ce que je fais actuellement :
http://pastebin.com/HHeUFNEF

Le schéma de mes documents est simplissime :

"labels-fr" : {  
"mappings" : {
      "advText" : {      
            "_id" : {       
               "path" : "code"
        },
       "properties" : {  
             "code" : {
             "type" : "string",
             "index" : "not_analyzed"
        }, 
        "hash" : {   
             "type" : "string",
             "index" : "not_analyzed"
          },
         "key" : {          
              "type" : "string",
              "index" : "not_analyzed"
          },
          "text" : { 
              "type" : "string",
              "analyzer" : "french",
              "fields" : {
                  "phonetic" : {
                      "type" : "string",
                      "analyzer" : "french_phonetic"
                   },
                   "raw" : {
                       "type" : "string",
                       "index" : "not_analyzed"
                   },        
                   "syn" : {
                       "type" : "string",
                       "analyzer" : "french_syn"
                   }
            }
          }
        }
      }

(David Pilato) #2

Est-ce qu'une Bool Query avec des clauses should ne serait pas ce que tu cherches?

Peut-être booster ensuite chacune des clauses ?

Sinon function-score me semble un bon candidat.


(Camilo Sierra) #3

Hello @Blured_Derulb en effet comme dit @dadoonet tu peut utiliser le bool query, je peut te donner une example :

{
"query": {
    "bool": {
        "should": [
            {
                "multi_match": {
                    "query": "the walking dead saison 6",
                    "type": "best_fields",
                    "fields": [
                        "tags", "description"
                    ],
                    "boost": 6
                }
            },
            {
                "multi_match": {
                    "query": "the walking dead saison 6",
                    "type": "phrase",
                    "slop": 4,
                    "fields": [
                        "title^5",
                        "content"
                    ],
                    "boost": 6
                }
            },
            {
                "match": {
                    "title": {
                        "query": "the walking dead saison 6",
                        "operator": "or",
                        "analyzer": "stemmer_letter_stop_words"
                    }
                }
            }
        ],
        "minimum_number_should_match": 1
    }
},
"sort": {
    "_score": {
        "order": "desc"
    }
}}

apres pour ce qui est de fuzzy ça peut etre une operation tres lourd et ça peut ralentir ta requete principal, moi j'ai pris comme option de faire une autre requete apart en ajax et comme google proposer le resultat en dehors des resultats de recheche, une genre de :

search : halking dead saison 6

what do you mean ? walking dead saison 6 (resultat de ma query ajax fuzzy)

et ici tous les resultats recuperes pour ma query principal pour la recherche "halking dead saison 6"...

(et vu que dans ma query j'ai un analyser stemmer_letter_stop_words je vais quand meme trouver le document avec le title: walking dead saison 6)


(Blured Derulb) #4

Merci beaucoup pour ces infos.

Je vais essayer en utilisant cette requête (+ intégration de la recherche en mode Exact) afin de vérifier si les performances sont meilleures :

{
  "query": 
    {"bool": {
      "should": [
        {
              "function_score": {
              "boost_mode": "replace",
              "query": {
                "match": {
                  "text.syn": {
                    "query": "sorbone",
                    "operator": "and",
                    "fuzziness": 1,
                    "minimum_should_match": "100%"
                  }
                }
              },
              "script_score": {
                "script": "1"
              }
          }
        },
        {
              "function_score": {
              "boost_mode": "replace",
              "query": {
                "match": {
                  "text.phonetic": {
                    "query": "sorbone",
                    "operator": "and",
                    "fuzziness": 1,
                    "minimum_should_match": "100%"
                  }
                }
              },
              "script_score": {
                "script": "3"
              }
          }
        },
        {
              "function_score": {
              "boost_mode": "replace",
              "query": {
                "match": {
                  "text.phonetic": {
                    "query": "sorbone",
                    "operator": "or",                    
                    "minimum_should_match": "50%"
                  }
                }
              },
              "script_score": {
                "script": "7"
              }
          }
        }
      ]
    }
  }
} 

(Blured Derulb) #5

Etonnant j'ai des scores non entiers quand j'utilise la requête portée sous l'API Java :

Normalement, il ne devrait y avoir que des scores en 1, 7 ou 15 ou des cumuls de ces chiffres, or avec l'api Java je me retrouve avec des scores en plus de ceux qui me paraissent cohérent : 14.666667, 5.3333335, 2.333333

Après vérification de la chaine XML générée, il s'avère que le coding via l'api génère :

"functions" : [ {
          "script_score" : {
            "script" : "1"
          }
        } ],
        "boost_mode" : "replace"

alors que normalement ça devrait générer un

 "script_score": {
                "script": "1"
   }

Sans l'enrobage du tableau de functions

Voyez-vous où pourrait se trouver l'erreur dans mon appel de l'api Java ?

QueryBuilder allTextForExact = matchQuery("text", "sorbonne")
                .operator(org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND)
                .fuzziness(Fuzziness.ZERO)
                .minimumShouldMatch("100%");         
                                    
        QueryBuilder termsSyn = matchQuery("text.syn", "sorbonne")
                .operator(org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND)
                .fuzziness(Fuzziness.ONE)
                .minimumShouldMatch("100%");
        
        QueryBuilder termsPhon = matchQuery("text.phonetic", "sorbonne")
                .operator(org.elasticsearch.index.query.MatchQueryBuilder.Operator.AND)
                .fuzziness(Fuzziness.ONE)
                .minimumShouldMatch("100%");
        
        QueryBuilder termsText = matchQuery("text", "sorbonne")
                .operator(org.elasticsearch.index.query.MatchQueryBuilder.Operator.OR)
                .minimumShouldMatch("50%");
        
        QueryBuilder functionScorePartial = functionScoreQuery(termsSyn)
                .add(ScoreFunctionBuilders.scriptFunction("1"))
                .boostMode("replace");    

        QueryBuilder functionTextExact = functionScoreQuery(allTextForExact)
                .add(ScoreFunctionBuilders.scriptFunction("ep_score_all_terms", "native", params))
                .boostMode("replace");
        
        QueryBuilder functionScoreFuzzy = functionScoreQuery(termsPhon)
                .add(ScoreFunctionBuilders.scriptFunction("7"))
                .boostMode("replace");    
        
        QueryBuilder functionScoreRelated = functionScoreQuery(termsText)
                .add(ScoreFunctionBuilders.scriptFunction("15"))
                .boostMode("replace")
                ;    
        
        QueryBuilder boolQ = boolQuery()
                //.should(functionTextExact)
                .should(functionScorePartial)
                .should(functionScoreFuzzy)
                .should(functionScoreRelated);
        
        sqb.setQuery(boolQ);
        
        SearchResponse response = sqb.execute().actionGet();
        SearchHits hits = response.getHits();

(Blured Derulb) #6

On dirait que dans les résultats dans le cas de l'utilisation de functions[] on a un mix des script function_score et du tf/idf ou quelque chose comme ça, alors que quand "script_score" est utilisé directement le tf/idf est ignoré. Bizarre non ?


(system) #7