Elasticsearch - Recherche et analyse de texte

Analyse d'une base de comics

Nous possédons une base de personnages de comics avec des textes de description. Le mapping par défaut est le suivant :

1
{
2
  "mappings": {
3
    "characters": {
4
      "properties": {
5
        "characters": {
6
          "type": "string",
7
          "index": "not_analyzed"
8
        },
9
        "description": {
10
          "type": "string",
11
          "index": "not_analyzed"
12
        }
13
      }
14
    }
15
  }
16
}

Chaque données est constitué de deux champs :

  • "character" : Le nom du personnage

  • "description" : Le texte de description du personnage

Question

Combien de personnages sont de l'univers marvel ? Un des résultat possède un score plus grand que les autres sur cette recherche, quel est le personnage lié à cette recherche ?

Vous devrez préalablement redéfinir votre index avec le mapping correct pour analyser les descriptions et rechercher à partir de mots clés.

Indice

L'analyzer standard permet de découper un texte avec les séparateurs les plus courants (espaces, virgules, tirets ...)

1
PUT comics 
2
{
3
  "mappings": {
4
    "characters": {
5
      "properties": {
6
        "character": {
7
          "type": "string",
8
          "index": "not_analyzed"
9
        },
10
        "description": {
11
          "type": "string",
12
          "index": "???",
13
          "analyzer": "???"
14
        }
15
      }
16
    }
17
  }
18
}
Indice

Utilisez une query pour recherche par un mot clé.

1
GET comics/characters/_search
2
{
3
  "query": {
4
    "term": {
5
      "???": {
6
        "value": "???"
7
      }
8
    }
9
  }
10
}
Solution

Redéfinissons tout d'abord notre mapping

1
PUT comics 
2
{
3
  "mappings": {
4
    "characters": {
5
      "properties": {
6
        "character": {
7
          "type": "string",
8
          "index": "not_analyzed"
9
        },
10
        "description": {
11
          "type": "string",
12
          "index": "analyzed",
13
          "analyzer": "standard"
14
        }
15
      }
16
    }
17
  }
18
}

Nous pouvons maintenant executer notre recherche

1
POST comics/characters/_search
2
{
3
  "query": {
4
    "term": {
5
      "description": {
6
        "value": "marvel"
7
      }
8
    }
9
  }
10
}

La recherche possède un total de 64 résultats, 64 personnages de la base sont donc de l'univers Marvel.

Vision est le personnage associé au résultat de la recherche possédant le plus gros score

  • Ce score est calculé à partir du ratio de mots correspondant à la recherche sur le nombre de mots du texte, le tout multiplié par un coefficient défini par la rareté du mot dans l'ensemble de la base. Cela explique pourquoi ce résultat possède le plus gros score, alors qu'il ne possède le mot marvel qu'une fois.

Question

Quel mot apparaît dans le plus de description de personnages ? On ne veut pas prendre en compte les mots les plus communs (tel que les déterminants) dans notre recherche.

Indice

L'analyzer standard peut être redéfinit afin de filtrer les stops words d'une langue.

1
{
2
  "settings": {
3
    "analysis": {
4
      "analyzer": {
5
        "standard_stop_words": {
6
          "type": "???",
7
          "stopwords": "_???_"
8
        }
9
      }
10
    }
11
  },
12
  "mappings" {
13
    ...
14
  }
15
}
Indice

La recherche peut se faire via une agrégation.

1
GET comics/characters/_search
2
{
3
  "aggs": {
4
    "wordMostUsed": {
5
      "terms": {
6
        "field": "???"
7
      }
8
    }
9
  },
10
  "size": 0
11
}
Solution

Redéfinissons dans un premier temps notre index

1
PUT comics 
2
{
3
  "settings": {
4
    "analysis": {
5
      "analyzer": {
6
        "standard_stop_words": {
7
          "type": "standard",
8
          "stopwords": "_english_"
9
        }
10
      }
11
    }
12
  },
13
  "mappings": {
14
    "characters": {
15
      "properties": {
16
        "character": {
17
          "type": "string",
18
          "index": "not_analyzed"
19
        },
20
        "description": {
21
          "type": "string",
22
          "index": "analyzed",
23
          "analyzer": "standard_stop_words"
24
        }
25
      }
26
    }
27
  }
28
}

On exécute ensuite notre agrégation.

1
GET comics/characters/_search
2
{
3
  "aggs": {
4
    "wordMostUsed": {
5
      "terms": {
6
        "field": "description"
7
      }
8
    }
9
  },
10
  "size": 0
11
}

La liste retournée étant trié, le plus grand agrégat placé en premier, le mot le plus utilisé est "character"

Question

Procédons à une dernière amélioration de l'analyse de notre description. Dans le résultat précédent, on observe que les agrégats des mots comic et comics sont séparés. Comment pourrions nous rassembler les mots ayant la même racine ? Modifier la définition de l'index pour ajouter ce nouveau analyzer.

Indice

Vous pouvez utiliser un analyzer linguistique comme mentionné sur la documentation d'elasticsearch :

Vous allez devoir déclarer un nouvel analyzer à la définition de votre index, possédant des char filters, des tokenizers, et des token filters. Vous pourrez ensuite utiliser votre analyzer dans votre mapping

Indice
1
{
2
  "settings": {
3
    "analysis": {
4
      "filter": {
5
        ...
6
      },
7
      "analyzer": {
8
        "english": {
9
          "tokenizer":  "standard",
10
          "filter": [
11
            ...
12
          ]
13
        }
14
      }
15
    }
16
  },
17
  {
18
    "mappings": {
19
      ...
20
    }
21
  }
22
}
Indice
1
{
2
  "settings": {
3
    "analysis": {
4
      "filter": {
5
        "english_stop": {
6
          "type":       "stop",
7
          "stopwords":  "_english_" 
8
        },
9
        "english_stemmer": {
10
          "type":       "stemmer",
11
          "language":   "english"
12
        },
13
        "english_possessive_stemmer": {
14
          "type":       "stemmer",
15
          "language":   "possessive_english"
16
        }
17
      },
18
      "analyzer": {
19
        "english": {
20
          "tokenizer":  "standard",
21
          "filter": [
22
            "english_possessive_stemmer",
23
            "lowercase",
24
            "english_stop",
25
            "english_stemmer"
26
          ]
27
        }
28
      }
29
    }
30
  },
31
  {
32
    "mappings": {
33
      ...
34
    }
35
  }
36
}
Solution
1
PUT comics
2
{
3
  "settings": {
4
    "analysis": {
5
      "filter": {
6
        "english_stop": {
7
          "type":       "stop",
8
          "stopwords":  "_english_" 
9
        },
10
        "english_stemmer": {
11
          "type":       "stemmer",
12
          "language":   "english"
13
        },
14
        "english_possessive_stemmer": {
15
          "type":       "stemmer",
16
          "language":   "possessive_english"
17
        }
18
      },
19
      "analyzer": {
20
        "english": {
21
          "tokenizer":  "standard",
22
          "filter": [
23
            "english_possessive_stemmer",
24
            "lowercase",
25
            "english_stop",
26
            "english_stemmer"
27
          ]
28
        }
29
      }
30
    }
31
  },
32
  "mappings": {
33
    "characters": {
34
      "properties": {
35
        "character": {
36
          "type": "string",
37
          "index": "not_analyzed"
38
        },
39
        "description": {
40
          "type": "string",
41
          "index": "analyzed",
42
          "analyzer": "english"
43
        }
44
      }
45
    }
46
  }
47
}

Question

Exercice supplémentaire : Quels sont les racines de mots qui apparaissent le plus souvent pour les personnages de marvel ? Notez que tous ces personnages possèdent le mot marvel dans leurs descriptions.

Indice

Il est possible de combiner une query et une agrégation, afin de filtrer les documents avant de les agréger

Solution
1
GET comics/characters/_search
2
{
3
  "query": {
4
    "match": {
5
      "description": "marvel"
6
    }
7
  },
8
  "aggs": {
9
    "wordMostUsed": {
10
      "terms": {
11
        "field": "description",
12
        "size": 10
13
      }
14
    }
15
  },
16
  "size": 0
17
}
PrécédentPrécédentFin
AccueilAccueilImprimerImprimer Oscar Odic, 2016 (Contributions : Stéphane Crozat, les étudiants de l'UTC) Paternité - Partage des Conditions Initiales à l'IdentiqueRéalisé avec Scenari (nouvelle fenêtre)