Recherche dans le système d'information de l'entreprise - déjà à partir de cette phrase elle-même reste coincée dans la bouche. C'est bien si vous en avez un, vous n'avez même pas à penser à une expérience utilisateur positive. Comment inverser l'attitude des utilisateurs gâtés par les moteurs de recherche et créer un produit rapide, précis et parfaitement compréhensible? Nous devons prendre un bon morceau d'Elasticsearch, une poignée de services intelligents et les pétrir sur ce guide.
Il existe de nombreux articles sur la façon d'attacher la recherche de texte intégral basée sur Elasticsearch à la base de données existante. Mais il n'y a clairement pas assez d'articles sur la façon de faire une recherche vraiment intelligente.
Dans le même temps, l'expression "Smart Search" elle-même est déjà devenue un mot à la mode et est utilisée pour le lieu et non. Alors, que doit faire un moteur de recherche pour être considéré comme intelligent? En fin de compte, cela peut être décrit comme donnant le résultat dont l'utilisateur a réellement besoin, même si ce résultat ne correspond pas tout à fait au texte de la demande. Les moteurs de recherche populaires comme Google et Yandex vont plus loin et ne trouvent pas seulement les informations dont ils ont besoin, mais répondent directement aux questions des utilisateurs.
D'accord, nous ne nous attaquerons pas tout de suite à la solution ultime, mais que peut-on faire pour rapprocher une recherche plein texte régulière d' une recherche intelligente ?
Éléments d'intelligence
Recherche intelligente - c'est juste le cas lorsque la quantité peut entrer dans la qualité et que de nombreuses fonctionnalités petites et assez simples peuvent former un sentiment de magie.
- Correction d'erreurs utilisateur - qu'il s'agisse d'une faute de frappe, d'une mise en page incorrecte ou, peut-être, d'une demande avec un nombre étrangement faible de résultats, mais similaire à une demande pour laquelle il existe beaucoup plus d'informations.
- Pour
e Discussions en PNL (traitement du langage naturel, pas ce que vous pensiez) - si l'utilisateur a saisi des offres commerciales pour l'année dernière , voulait-il vraiment rechercher ces mots dans le texte de tous les documents ou avait-il vraiment besoin uniquement d'offres commerciales et seulement l'année dernière ? - Prédisez la saisie en fonction des requêtes précédentes ou des documents populaires.
- La présentation du résultat est le point fort habituel du fragment trouvé, des informations supplémentaires en fonction de ce que vous cherchiez. Étant donné que des propositions commerciales étaient nécessaires dans le paragraphe précédent, peut-être est-il logique de montrer immédiatement le sujet de la proposition et de quelle organisation elle provient?
- Exploration facile - la possibilité d'affiner la requête de recherche à l'aide de filtres et de facettes supplémentaires.
Introduction
Il existe un ECM DIRECTUM contenant de nombreux documents. Le document se compose d'une carte avec des méta-informations et d'un corps, qui peut avoir plusieurs versions.
L'objectif est de rechercher rapidement et facilement des informations dans ces documents de la manière habituelle pour un utilisateur de moteurs de recherche.
Indexation
Pour bien rechercher quelque chose, vous devez d'abord bien l'indexer.
Les documents dans ECM ne sont pas statiques, les utilisateurs modifient le texte, créent de nouvelles versions, changent les données dans les cartes; de nouveaux documents sont constamment créés et les anciens sont parfois supprimés.
Pour conserver des informations à jour dans Elasticsearch, les documents doivent être constamment réindexés. Heureusement, ECM a déjà sa propre file d'attente d'événements asynchrones, donc lorsque vous modifiez un document, ajoutez-le simplement à la file d'attente pour l'indexation.
Mappage de documents ECM à des documents Elasticsearch
Un corps de document dans ECM peut avoir plusieurs versions. Dans Elasticsearch, cela pourrait être considéré comme un tableau d'objets imbriqués, mais il devient alors difficile de travailler avec eux - il devient plus difficile d'écrire des requêtes, lorsque vous changez l'une des versions, vous devez tout réindexer, différentes versions du même document ne peuvent pas être stockées dans différents index (pourquoi cela pourrait-il être nécessaire - dans la section suivante). Par conséquent, nous dénormalisons un document d'ECM en plusieurs documents Elasticsearch avec la même carte mais des corps différents.
En plus de la carte et du corps, diverses informations de service sont ajoutées au document Elasticsearch, qui mérite d'être mentionné séparément:
- une liste des identifiants des groupes et des utilisateurs qui ont des droits sur le document - pour les recherches avec des droits;
- le nombre d'appels au document - pour régler la pertinence;
- heure de la dernière indexation.
Composition de l'indice
Oui, plusieurs indices. Habituellement, plusieurs index pour stocker des informations de signification similaire dans Elasticsearch ne sont utilisés que si ces informations sont immuables et liées à une certaine période, par exemple, les journaux. Ensuite, les indices sont créés chaque mois / jour ou plus souvent en fonction de l'intensité de la charge. Dans notre cas, tout document peut être modifié et il serait possible de tout stocker dans un seul index.
Mais - les documents dans le système peuvent être dans différentes langues, et le stockage de données multilingues dans Elasticsearch pose 2 problèmes:
- Mauvaise racine. Pour certains mots, la base sera trouvée correctement, pour certains - incorrectement (il y aura un autre mot dans l'index), pour certains - ne sera pas trouvée du tout (l'index sera obstrué par des formes de mots). Pour certains mots de différentes langues et ayant des significations différentes, la base coïncidera, puis le sens du mot sera perdu. L'utilisation de plusieurs stemmers consécutifs peut conduire à un calcul supplémentaire de la base pour une base déjà calculée.
Stamming - trouver la base du mot. La tige ne doit pas être la racine du mot ou sa forme normale. En général, il suffit que les mots associés soient projetés dans un seul cadre.
La lemmatisation est un type de radical dans lequel la forme normale (vocabulaire) d'un mot est considérée comme la base.
- Fréquence de mot incorrecte. Certains mécanismes de détermination de la pertinence dans ES prennent en compte la fréquence des mots recherchés dans le document (plus souvent, plus la pertinence est élevée) et la fréquence des mots recherchés dans l'index (plus souvent, plus la pertinence est faible). Ainsi, une petite diffusion de la langue russe dans un document anglais, lorsque les documents anglais sont principalement dans l'index, aura un poids élevé, mais cela vaut la peine de mélanger les documents anglais et russes dans l'index, et le poids diminuera.
Le premier problème peut être résolu dans le cas où différentes langues utilisent différents jeux de caractères (les documents russe-anglais utilisent des lettres cyrilliques et latines) - les stemmers de langue ne traiteront que "leurs" caractères.
Pour résoudre le deuxième problème, nous avons utilisé l'approche avec un index séparé pour chaque langue.
En combinant les deux approches, nous obtenons des indices de langue, qui contiennent néanmoins des analyseurs pour plusieurs langues qui ne se croisent pas dans des ensembles de caractères: russe-anglais (et séparément anglais-russe), polonais-russe, allemand-russe, ukrainien-anglais, etc. .
Afin de ne pas créer tous les index possibles à l'avance, nous avons utilisé des modèles d'index - Elasticsearch vous permet de spécifier un modèle contenant des paramètres et des mappages, et de spécifier un modèle de nom d'index. Lorsque vous essayez d'indexer un document dans un index inexistant, dont le nom correspond à l'un des modèles du modèle, non seulement un nouvel index sera créé, mais également des paramètres et des mappages du modèle correspondant lui seront appliqués.
Structure de l'index
Pour l'indexation, nous utilisons deux analyseurs à la fois (via plusieurs champs): par défaut pour la recherche par phrase exacte et personnalisé pour tout le reste:
"ru_en_analyzer": { "filter": [ "lowercase", "russian_morphology", "english_morphology", "word_delimiter", "ru_en_stopwords" ], "char_filter": [ "yo_filter" ], "type": "custom", "tokenizer": "standard"}
Avec le filtre en minuscules, tout est clair, je vais vous parler du reste.
Les filtres russian_morphology et english_morphology sont destinés à l'analyse morphologique du texte russe et anglais, respectivement. Ils ne font pas partie d'Elasticsearch et sont inclus dans le cadre d'un plugin d'analyse-morphologie distinct. Ce sont des lemmatiseurs qui utilisent l'approche du vocabulaire en combinaison avec certaines heuristiques et fonctionnent de manière significative, BEAUCOUP, mieux que les filtres intégrés pour les langues correspondantes.
POST _analyze { "analyzer": "russian", "text": " " } >>
Et:
POST _analyze { "analyzer": "ru_en_analyzer", "text": " " } >>
Filtre word_delimiter très curieux. Cela aide, par exemple, à éliminer les fautes de frappe lorsqu'il n'y a pas d'espace après le point. Nous utilisons la configuration suivante:
"word_delimiter": { "catenate_all": "true", "type": "word_delimiter", "preserve_original": "true" }
yo_filter vous permet d'ignorer la différence entre E et E:
"yo_filter": { "type": "mapping", "mappings": [ " => ", " => " ] }
ru_en_stopwords filter type stop - notre dictionnaire de mots vides.
Processus d'indexation
Les corps de documents dans ECM sont, en règle générale, des fichiers de formats bureautiques: .docx, .pdf, etc. Pour extraire le texte, le plugin ingest-attachment est utilisé avec le pipeline suivant:
{ "document_version": { "processors": [ { "attachment": { "field": "content", "target_field": "attachment", "properties": [ "content", "content_length", "content_type", "language" ], "indexed_chars": -1, "ignore_failure": true } }, { "remove": { "field": "content", "ignore_failure": true } }, { "script": { "lang": "painless", "params": { "languages": ["ru", "en" ], "language_delimeter": "_" }, "source": "..." } }, { "remove": { "field": "attachment", "ignore_failure": true } } ] } }
De l'inhabituel dans le pipeline, en ignorant les erreurs d'absence du corps (cela se produit pour les documents cryptés) et en déterminant l'index cible en fonction de la langue du texte. Ce dernier se fait dans un script indolore, dont je donnerai le corps séparément, car en raison des restrictions JSON, il doit être écrit sur une seule ligne. Associé à des difficultés de débogage (la méthode recommandée consiste à lever des exceptions ici et là), cela devient complètement douloureux.
if (ctx.attachment != null) { if (params.languages.contains(ctx.attachment.language)) ctx._index = ctx._index + params.language_delimeter + ctx.attachment.language; if (ctx.attachment.content != null) ctx.content = ctx.attachment.content; if (ctx.attachment.content_length != null) ctx.content_length = ctx.attachment.content_length; if (ctx.attachment.content_type != null) ctx.content_type = ctx.attachment.content_type; if (ctx.attachment.language != null) ctx.language = ctx.attachment.language; }
Ainsi, nous envoyons toujours le document à index_name . Si la langue n'est pas définie ou n'est pas prise en charge, le document s'installe dans cet index, sinon il tombe dans index_name_language .
Nous ne stockons pas le corps d'origine du fichier, mais le champ _source est activé, car il est nécessaire de mettre à jour partiellement le document et de mettre en évidence le trouvé.
Si seule la carte a changé depuis la dernière indexation, nous utilisons l' API Update By Query sans pipeline pour la mettre à jour. Cela permet, d'une part, de ne pas extraire des corps de document potentiellement lourds d'ECM, et d'autre part, cela accélère considérablement la mise à jour du côté Elasticsearch - vous n'avez pas à extraire le texte des documents des formats bureautiques, ce qui est très gourmand en ressources.
En tant que tel, il n'y a aucune mise à jour du document dans Elasticsearch, techniquement, lors de la mise à jour à partir de l'index, l'ancien document est retiré, modifié et complètement indexé à nouveau.
Mais si le corps a changé, l'ancien document est généralement supprimé et indexé à partir de zéro. Cela permet aux documents de passer d'un index de langue à un autre.
Chercher
Pour faciliter la description, je vais donner une capture d'écran du résultat final

Texte intégral
Le type de requête principal que nous avons est la requête de chaîne de requête simple :
"simple_query_string": { "fields": [ "card.d*.*_text", "card.d*.*_text.exact", "card.name^2", "card.name.exact^2", "content", "content.exact" ], "query": " ", "default_operator": "or", "analyze_wildcard": true, "minimum_should_match": "-35%", "quote_field_suffix": ".exact" }
où .exact sont les champs indexés par l'analyseur par défaut . L'importance du nom du document est deux fois plus élevée que le reste des champs. La combinaison de "default_operator": "or"
et "minimum_should_match": "-35%"
vous permet de trouver des documents qui ne contiennent pas jusqu'à 35% des mots recherchés.
Synonymes
En général, différents analyseurs sont utilisés pour l'indexation et la recherche, mais la seule différence entre eux est l'ajout d'un filtre pour ajouter des synonymes à la requête de recherche:
"search_analyzer": { "filter": [ "lowercase", "russian_morphology", "english_morphology", "synonym_filter", "word_delimiter", "ru_en_stopwords" ], "char_filter": [ "yo_filter" ], "tokenizer": "standard" }
"synonym_filter": { "type": "synonym_graph", "synonyms_path": "synonyms.txt" }
Droits comptables
Pour les recherches basées sur les droits, la requête principale est intégrée dans Bool Query , avec l'ajout d'un filtre:
"bool": { "must": [ { "simple_query_string": {...} } ], "filter": [ { "terms": { "rights": [ ] } } ] }
Comme nous nous en souvenons de la section sur l'indexation, l'index a un champ avec l'ID des utilisateurs et des groupes qui ont des droits sur le document. S'il y a une intersection de ce champ avec le tableau transmis, alors il y a des droits.
Réglage de la pertinence
Par défaut, Elasticsearch évalue la pertinence des résultats à l'aide de l'algorithme BM25 à l'aide de la requête et du texte du document. Nous avons décidé que trois autres facteurs devraient influencer l'évaluation de la conformité avec le résultat souhaité et réel:
- le moment de la dernière édition du document - plus il était loin dans le passé, moins il est probable que ce document soit nécessaire
- le nombre d'appels au document - plus il est probable que ce document est nécessaire;
Les versions de corps ECM ont plusieurs états possibles: en cours de développement, opérationnel et obsolète. Il est logique que le jeu soit plus important que les autres.
Vous pouvez obtenir cet effet à l'aide de la fonction Function Score Query :
"function_score": { "functions": [ { "gauss": { "modified_date": { "origin": "now", "scale": "1095d", "offset": "31d", "decay": 0.5 } } }, { "field_value_factor": { "field": "access_count", "missing": 1, "modifier": "log2p" } }, { "filter": { "term": { "life_stage_value_id": { "value": "" } } }, "weight": 1.1 } ], "query": { "bool": {...} } }
Par conséquent, toutes choses égales par ailleurs, nous obtenons approximativement la dépendance suivante du modificateur d'évaluation des résultats à la date de son dernier changement X et au nombre de résultats Y:

Intelligence externe
Pour une partie des fonctionnalités de la recherche intelligente, nous devons extraire divers faits de la requête de recherche: dates avec leur application (création, modification, approbation, etc.), noms des organisations, types de documents recherchés, etc.
Il est également souhaitable de classer la demande dans une certaine catégorie, par exemple, les documents par organisation, par employé, réglementaire, etc.
Ces deux opérations sont effectuées par le module intelligent ECM - DIRECTUM Ario .
Processus de recherche intelligent
Il est temps d'examiner plus en détail quels mécanismes sont mis en œuvre les éléments du renseignement.
Correction d'erreurs utilisateur
La justesse de la mise en page est déterminée sur la base du modèle de langue du trigramme - pour une ligne, on calcule sa probabilité de respecter ses séquences de trois caractères dans des textes en anglais et en russe. Si la mise en page actuelle est considérée comme moins probable, alors, tout d'abord, un indice avec une mise en page correcte s'affiche:

et deuxièmement, les autres étapes de la recherche sont effectuées avec la mise en page correcte:

Et si rien ne peut être trouvé avec la mise en page corrigée, la recherche commence par la ligne d'origine.
La correction des fautes de frappe est implémentée à l'aide de Phrase Suggester . Il y a un problème - si vous exécutez une requête sur plusieurs index en même temps, alors suggest peut ne rien retourner, tandis que si vous exécutez sur un seul index, il y a des résultats. Ceci est traité en définissant la confiance = 0, mais suggère ensuite de remplacer les mots par leur forme normale. D'accord, il sera étrange lorsque vous rechercherez "lettre a " d'obtenir une réponse dans l'esprit: Peut - être cherchiez-vous une lettre sur ?
Cela peut être contourné en utilisant deux invites dans la demande:
"suggest": { "content_suggest": { "text": " ", "phrase": { "collate": { "query": { {{suggestion}} } }, } }, "check_suggest": { "text": "", "phrase": { "collate": { "query": { {{suggestion}} - ({{source_query}}) }, "params": { "source_query": " " } }, } } }
Des paramètres communs utilisés
"confidence": 0.0, "max_errors": 3.0, "size": 1
Si le premier opposant a renvoyé le résultat, mais pas le second, alors ce résultat est la chaîne d'origine elle-même, éventuellement avec des mots sous d'autres formes, et il n'est pas nécessaire d'afficher un indice. Si l'indice est toujours requis, la phrase de recherche d'origine fusionne avec l'indice. Cela se produit en remplaçant uniquement les mots corrigés et uniquement ceux que le correcteur orthographique (utilisant Hunspell) considère comme incorrects.
Si la recherche sur la chaîne source a renvoyé 0 résultats, elle est remplacée par la chaîne obtenue par la fusion et la recherche est à nouveau effectuée:

Sinon, la chaîne d'invite résultante est renvoyée uniquement en tant qu'invite pour la recherche:

Classification des requêtes et extraction des faits
Comme je l'ai mentionné, nous utilisons DIRECTUM Ario, à savoir le service de classification de texte et le service d'extraction de faits. Pour ce faire, nous avons donné aux analystes des requêtes de recherche anonymes et une liste de faits qui nous intéressent. Sur la base de requêtes et de connaissances sur les documents présents dans le système, les analystes ont identifié plusieurs catégories et formé le service de classification pour déterminer la catégorie en fonction du texte de la requête. Sur la base des catégories et de la liste de faits résultants, nous avons formulé les règles d'utilisation de ces faits. Par exemple, l'expression pour la dernière année dans la catégorie Tout le monde est considérée comme la date de création du document, et dans la catégorie Par organisation - la date d'enregistrement. Dans le même temps, ceux créés l'année dernière devraient dans n'importe quelle catégorie tomber à la date de création.
Du côté de la recherche - ils ont fait une configuration dans laquelle ils ont enregistré les catégories, quels faits sont appliqués à quels filtres de facettes.
Achèvement de la saisie
En plus des corrections de mise en page déjà mentionnées, les recherches antérieures de l'utilisateur et des documents publics tombent en auto-complétion.

Ils sont implémentés à l'aide d'un autre type de Suggestion - Suggestion d'achèvement , mais chacun a ses propres nuances.
Saisie semi-automatique: historique des recherches
Il y a beaucoup moins d'utilisateurs dans ECM que les moteurs de recherche et allouez suffisamment de requêtes communes pour eux pourquoi champignon lénine pas possible. Tout afficher à la suite n'en vaut pas la peine pour des raisons de confidentialité. Le Suggestion de complétion habituel ne peut rechercher que l'ensemble des documents dans l'index, mais le Suggestion de contexte vient à la rescousse - un moyen de définir un contexte pour chaque indice et de filtrer par ces contextes. Si les noms d'utilisateur sont utilisés comme contextes, alors seul son historique peut être montré à tout le monde.
Vous devez également donner à l'utilisateur la possibilité de supprimer l'invite pour laquelle il a honte. Comme clé de suppression, nous avons utilisé le nom d'utilisateur et le texte de l'infobulle. En conséquence, pour l'index avec des conseils, nous avons obtenu un mappage légèrement dupliqué:
"mappings": { "document": { "properties": { "input": { "type": "keyword" }, "suggest": { "type": "completion", "analyzer": "simple", "preserve_separators": true, "preserve_position_increments": true, "max_input_length": 50, "contexts": [ { "name": "user", "type": "CATEGORY" } ] }, "user": { "type": "keyword" } } } }
Le poids de chaque nouvel indice est défini sur un et augmente chaque fois que vous le saisissez à nouveau à l'aide de l' API Update By Query avec un script ctx._source.suggest.weight++
très simple.
Saisie semi-automatique: documents
Mais il peut y avoir beaucoup de documents et des combinaisons possibles de droits. Par conséquent, ici, au contraire, nous avons décidé de ne pas filtrer par droits lors de l'auto-complétion, mais uniquement d'indexer les documents publics. Oui, et vous n'avez pas besoin de supprimer les conseils individuels de cet index. Il semblerait que la mise en œuvre en tout soit plus facile que la précédente, sinon pour deux points:
Le premier - Completion Suggester ne prend en charge que la recherche de préfixe, et les clients adorent attribuer des numéros d'article à tout, et certaines .01.01
lorsque vous tapez une requête Il n'y a .01.01
. Ici, avec le nom complet, vous pouvez également indexer les n-grammes qui en dérivent:
{ "extension": "pdf", "name": ".01.01 ", "suggest": [ { "input": "", "weight": 70 }, { "input": " ", "weight": 80 }, { "input": " ", "weight": 90 }, { "input": ".01.01 ", "weight": 100 } ] }
Ce n'était pas si critique avec l'histoire, mais le même utilisateur entre approximativement la même ligne s'il recherche à nouveau quelque chose. Probablement .
La seconde - par défaut, tous les conseils sont égaux, mais nous aimerions rendre certains d'entre eux plus égaux et de préférence afin que cela soit cohérent avec le classement des résultats de recherche. Pour ce faire, répétez grossièrement les fonctions gauss et field_value_factor utilisées dans la fonction Function Score Query .
Il s'avère que c'est un tel pipeline:
{ "dir_public_documents_pipeline": { "processors": [ ... { "set": { "field": "terms_array", "value": "{{name}}" } }, { "split": { "field": "terms_array", "separator": "\\s+|$" } }, { "script": { "source": "..." } } ] } }
avec le script suivant:
Date modified = new Date(0); if (ctx.modified_date != null) modified = new SimpleDateFormat('dd.MM.yyyy').parse(ctx.modified_date); long dayCount = (System.currentTimeMillis() - modified.getTime())/(1000*60*60*24); double score = Math.exp((-0.7*Math.max(0, dayCount - 31))/1095) * Math.log10(ctx.access_count + 2); int count = ctx.terms_array.length; ctx.suggest = new ArrayList(); ctx.suggest.add([ 'input': ctx.terms_array[count - 1], 'weight': Math.round(score * (255 - count + 1)) ]); for (int i = count - 2; i >= 0 ; --i) { if (ctx.terms_array[i].trim() != "") { ctx.suggest.add([ "input": ctx.terms_array[i] + " " + ctx.suggest[ctx.suggest.length - 1].input, "weight": Math.round(score * (255 - i))]); } } ctx.remove('terms_array'); ctx.remove('access_count'); ctx.remove('modified_date');
Pourquoi s'embêter avec un pipeline indolore au lieu de l'écrire dans une langue plus pratique? Parce que maintenant, à l'aide de l' API Reindex , vous pouvez remplacer le contenu des index de recherche en index pour les conseils (en spécifiant uniquement les champs nécessaires, bien sûr) en une seule commande.
La composition des documents publics vraiment nécessaires n'est pas souvent mise à jour, donc cette commande peut être laissée sur un démarrage manuel.
Afficher les résultats
Les catégories
La catégorie détermine quelles facettes seront disponibles et à quoi ressemblera l'extrait. Il peut être détecté automatiquement par une intelligence externe ou sélectionné manuellement au-dessus de la barre de recherche.
Facettes
Les facettes sont une chose tellement intuitive pour tous ceux dont le comportement est cependant décrit par des règles très simples. En voici quelques uns:
Les valeurs des facettes dépendent des résultats de la recherche, MAIS et les résultats de la recherche dépendent des facettes sélectionnées. Comment éviter la récursivité?
La sélection de valeurs dans une facette n'affecte pas les autres valeurs de cette facette, mais elle affecte les valeurs dans d'autres facettes:

- Les valeurs de facette sélectionnées par l'utilisateur ne doivent pas disparaître, même si un choix dans une autre facette les annule à 0 ou si elles ne sont plus en haut:

En élasticité, les facettes sont réalisées à travers le mécanisme d'agrégation, mais pour respecter les règles décrites, ces agrégations doivent être investies les unes dans les autres et filtrées les unes par les autres.
Considérez les fragments de requête responsables de cela:
Code trop grand { ... "post_filter": { "bool": { "must": [ { "terms": { "card.author_value_id": [ "1951063" ] } }, { "terms": { "editor_value_id": [ "2337706", "300643" ] } } ] } }, "query": {...} "aggs": { "card.author_value_id": { "filter": { "terms": { "editor_value_id": [ "2337706", "300643" ] } }, "aggs": { "card.author_value_id": { "terms": { "field": "card.author_value_id", "size": 11, "exclude": [ "1951063" ], "missing": "" } }, "card.author_value_id_selected": { "terms": { "field": "card.author_value_id", "size": 1, "include": [ "1951063" ], "missing": "" } } } }, ... "editor_value_id": { "filter": { "terms": { "card.author_value_id": [ "1951063" ] } }, "aggs": { "editor_value_id": { "terms": { "field": "editor_value_id", "size": 11, "exclude": [ "2337706", "300643" ], "missing": "" } }, "editor_value_id_selected": { "terms": { "field": "editor_value_id", "size": 2, "include": [ "2337706", "300643" ], "missing": "" } } } }, ... } }
Qu'est-ce que c'est que:
- post_filter vous permet d'imposer une condition supplémentaire aux résultats d'une requête déjà terminée et n'affecte pas les résultats des agrégations. Ce même écart de récursivité. Inclut toutes les valeurs sélectionnées de toutes les facettes.
- agrégations de niveau supérieur, dans les exemples card.author_value_id et editor_value_id . Chacun a:
- filtrer par les valeurs de toutes les autres facettes, sauf la vôtre;
- agrégation imbriquée pour des valeurs de facettes sélectionnées - protection contre l'annihilation ;
- agrégation imbriquée pour d'autres valeurs de facette. Nous affichons le top 10 et demandons le top 11 - pour déterminer s'il faut afficher le bouton Afficher tout .
Extraits
Selon la catégorie sélectionnée, l'extrait peut sembler différent, par exemple, le même document lors de la recherche dans une catégorie
Tous :

et employés :

Ou souvenez-vous, nous voulions voir le sujet d'une offre commerciale et de qui venait-elle?

Afin de ne pas faire glisser la carte entière de l'élastique (cela ralentit la recherche), le filtrage source est utilisé :
{ ... "_source": { "includes": [ "id", "card.name", "card.card_type_value_id", "card.life_stage_value_id", "extension", ... ] }, "query": {...} ... }
Pour mettre en évidence les mots trouvés dans le texte du document, le surligneur Fast Vector est utilisé - comme générant les extraits de code les plus appropriés pour les gros textes, et pour le nom - Surligneur unifié - comme le moins exigeant en termes de ressources et de structure d'index:
"highlight": { "pre_tags": [ "<strong>" ], "post_tags": [ "</strong>" ], "encoder": "html", "fields": { "card.name": { "number_of_fragments": 0 }, "content": { "fragment_size": 300, "number_of_fragments": 3, "type": "fvh" } } },
Dans ce cas, le nom est mis en évidence dans son intégralité, et du texte nous obtenons jusqu'à 3 fragments de 300 caractères. Le texte renvoyé par le surligneur Fast Vector est ensuite compressé par un algorithme de fortune pour obtenir un état d'extrait minimisé.
Réduire
Historiquement, les utilisateurs de cet ECM sont habitués au fait que la recherche leur renvoie des documents , mais en fait Elasticsearch recherche parmi les versions des documents . Il peut s'avérer que plusieurs versions presque identiques se trouvent sur la même requête. Cela encombrera les résultats et confondra l'utilisateur. Heureusement, ce comportement peut être évité en utilisant le mécanisme de regroupement de champs - une version légère d'agrégations qui fonctionne déjà sur les résultats finis (en cela, il ressemble à post_filter, deux béquilles sont une paire ). L' effondrement entraînera le plus pertinent des objets qui s'effondrent.
{ ... "query": {...} ... "collapse": { "field": "id" } }
Malheureusement, l'effondrement a un certain nombre d'effets désagréables, par exemple, diverses caractéristiques numériques du résultat de la recherche continuent de revenir comme s'il n'y avait pas d'effondrement. Autrement dit, le nombre de résultats, le nombre de valeurs de facette - tout sera légèrement incorrect, mais l'utilisateur ne le remarque généralement pas, tout comme le lecteur fatigué, qui a peu de chances d'avoir lu cette proposition auparavant.
La fin.