Représentent les différents types dans un index, comme un utilisateur, un produit, etc.
Représente des tiges de mots, uniques à l'ensemble de l'index. (Par exemple, Run est une tige des mots en cours d'exécution, coureur, exécution, etc.). Une fréquence de document inverse est calculée et stockée par rapport au mot pour fournir un IDF à l'échelle de l'indice qui peut être utilisé pour effectuer des recherches sans schéma contre l'index.
Un terme représente l'existence d'un mot dans un schéma spécifique. Autrement dit, si le mot en tige "run" apparaît dans le schéma utilisateur et qu'il apparaît également dans le schéma du produit, il y aura deux enregistrements à terme pour ce mot, dans ce schéma. Une fréquence de document inverse est également stockée à côté du terme pour fournir un IDF spécifique au schéma qui permet des recherches sur un schéma spécifique (ces recherches ne seront pas affectées par les valeurs IDF à l'échelle de l'index pour les mots).
Une colonne est un champ disponible dans un schéma. Les magasins de colonnes régissent si les données qu'elle représente sont stockées, si elle est indexée et son poids qui peut être utilisé pour hiérarchiser les colonnes dans les requêtes (par exemple, un titre de produit peut avoir plus de poids qu'une description)
Un document représente une entité réelle dans un index, il s'agit de l'élément qui est réellement indexé et qui formera la base des résultats des requêtes.
Un champ est un attribut d'un document qui correspond à une colonne. Les colonnes, les documents et les champs agissent comme une base de données typique. Une colonne est comme une colonne de table, un document est comme une ligne ou un enregistrement et un champ est une cellule ou une entrée.
Une occurrence représente l'existence d'un terme (un mot spécifique au schéma) dans un champ dans un document. Une occurrence est une représentation unique d'un terme et d'un champ. À côté d'une occurrence, nous stockons également une fréquence, ce qui représente le nombre de fois que le terme apparaît dans le champ (qui est utilisé pour calculer les scores plus tard lors de la recherche).
Les positions représentent les positions réelles des termes dans les champs. Si le mot «exécuter» (ou ses tiges) apparaît 3 fois dans un champ, il y aurait 3 enregistrements de position, chacun marquant la position de cette occurrence dans le champ correspondant.
Imaginez que nous ayons un schéma "utilisateur" dans notre index. Ce schéma définit 2 colonnes pour chaque instance utilisateur, "nom" et "À propos". Chaque colonne est à la fois stockée et indexée et ils ont tous deux un poids de 1. Nous avons 2 utilisateurs à ajouter à l'index, [Key: 1, nom: "Joe Bloggs", "About": "Joe aime Jane"] et [Key: 2, nom: "Jane Doe", à propos: "Jane aime faire la fête"]. Nous nous retrouvions avec la structure suivante:
| identifiant | nom |
|---|---|
| 1 | utilisateur |
| identifiant | nom | stocké | indexé | poids |
|---|---|---|---|---|
| 1 | nom | 1 | 1 | 1 |
| 2 | à propos | 1 | 1 | 1 |
| identifiant | mot |
|---|---|
| 1 | Joe |
| 2 | blogg |
| 3 | comme |
| 4 | Jeanne |
| 5 | biche |
| 6 | amour |
| 7 | à |
| 8 | faire la fête |
| identifiant | schéma_id | word_id | document_count |
|---|---|---|---|
| 1 | 1 | 1 | 2 |
| 2 | 1 | 2 | 1 |
| 3 | 1 | 3 | 2 |
| 4 | 1 | 4 | 3 |
| 5 | 1 | 5 | 1 |
| 6 | 1 | 6 | 1 |
| 7 | 1 | 7 | 1 |
| 8 | 1 | 8 | 1 |
| identifiant | schéma_id | clé |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| identifiant | document_id | Column_id | valeur |
|---|---|---|---|
| 1 | 1 | 1 | Joe Bloggs |
| 2 | 1 | 2 | Joe aime Jane |
| 3 | 2 | 1 | Jane Doe |
| 4 | 2 | 2 | Jane aime faire la fête |
| identifiant | field_id | term_id | fréquence |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | 1 | 1 |
| 4 | 2 | 3 | 1 |
| 5 | 2 | 4 | 1 |
| 6 | 3 | 4 | 1 |
| 7 | 3 | 5 | 1 |
| 8 | 4 | 4 | 1 |
| 9 | 4 | 3 | 1 |
| 10 | 4 | 7 | 1 |
| 11 | 4 | 8 | 1 |
| identifiant | occurrence_id | position |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 1 |
| 4 | 4 | 2 |
| 5 | 5 | 3 |
| 6 | 6 | 1 |
| 7 | 7 | 2 |
| 8 | 8 | 1 |
| 9 | 9 | 2 |
| 10 | 10 | 3 |
| 11 | 11 | 4 |
Lorsque l'objet Blixt est créé, il charge initialement tous les objets de schéma et de colonne stockés en mémoire, s'il y en a. Cela permet à Blixt de rechercher rapidement un schéma et ses colonnes associées pour valider une demande d'index et de rassembler rapidement les contraintes is_indexed et is_stored sur chaque colonne.
Avant d'indexer un document, un schéma doit être défini avec un ensemble de colonnes, chacune avec ses propres propriétés qui spécifient si les champs qu'ils représentent doivent être stockés et ou indexés. Si un schéma existe déjà, cette partie peut être ignorée. Si une définition de schéma n'est pas fournie et qu'il n'existe aucun schéma, une exception est lancée.
Un document est fourni sous la forme d'un document indexable, spécifiant une clé qui peut être utilisée par le système client pour l'identifier plus tard. Un document indexable contient un ensemble de champs. Un schéma (ou type) est fourni avec le document afin que Blixt puisse placer le document dans le schéma correct dans l'index.
Blixt commence à traiter le document en vérifiant d'abord pour s'assurer qu'il n'existe pas déjà dans l'index dans ce schéma. S'il existe, une exception est lancée.
Blixt ajoutera ensuite un enregistrement de document et commencera à traiter les champs du document. Chaque champ est divisé en jetons (dans la plupart des cas, un jeton est un mot), puis chaque jeton est entièrement stimulant (c'est-à-dire trouver la racine d'un mot, par exemple avec une tige anglaise / porteur, le mot "run" est la tige des mots "en cours d'exécution", "coureur", "exécute" etc.)
Des enregistrements de mots sont ensuite ajoutés pour chacun des jetons si aucun enregistrement correspondant n'existe déjà, puis des enregistrements de terme sont créés sous le schéma en conséquence. Les totaux du nombre de documents pour les enregistrements du terme sont mis à jour pour refléter l'ajout du nouveau document.
Des enregistrements d'occurrence sont créés pour chaque combinaison de terme et de champ unique, indiquant que le terme spécifié se produit dans le champ spécifique. La fréquence (nombre de fois ce terme s'est produite dans le champ) est stockée à côté de l'enregistrement d'occurrence. Les données de position sont également stockées sur chaque enregistrement d'occurrence représentant chaque position dans un champ où un terme s'est produit.
Les types de requêtes suivants sont pris en charge:
+ qui fait un terme requis (et) ou un ~ qui fait supprimer un terme un document de la considération (pas). Ce type de requête constitue la base de toutes les autres requêtes. La recherche commence par diviser la phrase de recherche en mots distincts (tokenisation), puis transformant chaque mot en sa forme racine (engelant). L'index est ensuite interrogé pour trouver des enregistrements de mots correspondant aux termes. En utilisant les enregistrements de mots et le schéma que nous interrogeons, les enregistrements à terme sont ensuite extraits. Si l'un des termes de recherche dont l'opérateur + s'y attache n'est pas présent dans les enregistrements de terme, alors un ensemble de résultats vide est renvoyé.
Une requête est ensuite effectuée pour trouver des champs qui contiennent les termes de la requête (quel que soit l'opérateur) et les documents associés à ces champs sont retournés (avec leurs champs, occurrences et positions tous chargés en conséquence). Chaque document est ensuite analysé et accepté ou rejeté, si un document ne contient pas de terme requis ( + ) ou contient un terme supprimé ( ~ ) Il est rejeté, tous les autres documents sont acceptés. L'ensemble des documents acceptés est ensuite renvoyé sous forme de résultats.
Les types de requêtes suivants doivent être pris en charge plus tard:
Requête à terme: similaire à une requête à plusieurs termes, les documents sont notés sur un seul terme.
Requête complète de phrase: similaire à une requête à plusieurs termes, mais seuls les documents contenant chaque terme dans le bon ordre sont autorisés.
Il est également destiné à implémenter l'analyse de requête afin qu'une chaîne de recherche d'entrée puisse être convertie en type de requête correct avant la recherche.
Ce sont des améliorations destinées à l'avenir.
Implémentez un cache agnostique de stockage (redis, memcached, Système de fichiers, etc.) que nous pouvons utiliser pour améliorer les performances de la recherche. Il existe un certain nombre de cas d'utilisation pour un cache dans ce projet:
Remarque: Pour implémenter certaines des fonctionnalités ci-dessus, nous aurions potentiellement besoin d'utiliser un identifiant de session unique pour transmettre et recevoir de l'utilisateur pour garantir que les recherches en cache ne sont valables que pour cet utilisateur et pour arrêter ces objets mis en cache. C'est certainement le cas lors de la mise en cache d'un ensemble complet de résultats de recherche pour permettre à l'utilisateur de paginer. Il pourrait également être utile de mettre en cache des versions qui ne sont pas considérées comme spécifiques à la session pour permettre à de nombreux utilisateurs de rechercher la même chose pour voir rapidement les mêmes résultats de recherche.
Pendant le processus d'indexation, nous pouvons utiliser une table supplémentaire pour stocker une cartographie des termes aux documents, qui nous dira dans quels documents un terme donné apparaît.