1
votes

mongodb - moyen efficace de rechercher des chaînes

Je cherche à rechercher des chaînes divisées en un préfixe, des chiffres et des lettres, j'ai actuellement besoin de rechercher 55 millions de lignes et une ligne ressemble à ceci (j'enregistre également une version concatanée de la chaîne entière):

{"prefix": "AA", "numbers": "02", "letters": "AFO", "code": "AA02AFO" "price": "699"} p>

Quelle est la manière la plus efficace de rechercher si je veux une recherche combinée qui correspondra exactement à ce qui précède mais qui renverra également des correspondances comme:

AA*AFO
A*AFO
*A02AFO
**02AFO

Et ainsi de suite, Je souhaite renvoyer une correspondance exacte si cela existe et je souhaite également renvoyer tous les enregistrements similaires à l'enregistrement de recherche.

J'ai actuellement un index sur tous les champs sauf le prix et un index sur le code concatané. Existe-t-il également un moyen de garantir que la requête renvoie des résultats distincts si une chaîne correspond à plusieurs expressions régulières?


0 commentaires

3 Réponses :



2
votes

Tout d'abord, la construction de cette requête en tant qu'expression régulière par rapport à une chaîne sera l'un des moyens d'interrogation les moins efficaces. Au lieu de cela, vous devriez interroger par les champs réels:

{
$or: [
  {prefix: "AA", numbers: "02", "letters": "AFO"}, -- full match
  {prefix: {$exists: true}, numbers: "02", "letters: "AFO" } -- varying prefix,
   ...etc
]}

L'interrogation par les champs réels permettra à Mongo d'utiliser efficacement les index. Une expression régulière comme /^..02AFO/ nécessitera de vérifier chaque enregistrement de l'index pour les correspondances.

J'ai actuellement un index sur tous les champs sauf le prix et un index sur le code concatané. Existe-t-il également un moyen de garantir que la requête renvoie des résultats distincts si une chaîne correspond à plusieurs expressions régulières?

Si vous souhaitez effectuer une correspondance avec des expressions régulières, vous pouvez utiliser $ in pour combiner plusieurs expressions régulières et renvoyer uniquement les documents correspondants uniques: {code: {$ in: [/ foo /, / bar / ]}

Vous pouvez également utiliser $ ou : {$ ou: [{code: / foo /}, {code: / bar /}], otherCriterion: "yay"} .


6 commentaires

Je pensais qu'une expression régulière pouvait être très lente, je pense que je devrais peut-être revenir à la planche à dessin, mais je vais essayer la combinaison de ces deux messages pour voir comment elle fonctionne. En ce qui concerne les index, indexeriez-vous chaque champ individuel, j'ai relu mon message et ce que je voulais dire, c'est que j'ai un index composite de tous les champs et un sur la colonne concatanée? Les index vont nécessiter beaucoup de RAM!


Si vous utilisez Expliquer sur les requêtes regex, vous devriez voir qu'il examinera chaque document de la collection. Si vous avez 3 champs, a, b et c et que vous interrogez par {a, b, c} , {a, b} , {a, c} et {b, c} , vous devriez normalement obtenir des performances acceptables avec 2 index: abc et bc


Sinon, vous pouvez commencer à stocker les requêtes de chaîne qu'un document doit correspondre sur le document lui-même et l'indexer. De cette façon, vous pouvez indexer ce champ de tableau avec un index multi-niveaux et y obtenir des recherches rapides.


J'obtiens des réponses assez rapides sans avoir besoin d'utiliser des expressions régulières, je recherche simplement des champs indexés. Un changement que j'ai fait après votre suggestion était de diviser la chaîne entière en champs individuels. J'obtiens des temps de réponse d'environ 2 secondes pour une recherche des 55 millions d'enregistrements qui me rapportent environ 5000 correspondances, donc je pense que c'est bien. Est-il possible d'indiquer l'ordre des résultats renvoyés en utilisant ou? Il ne semble pas qu'ils soient retournés dans l'ordre dans lequel je passe les objets.


Jusqu'à 380 ms maintenant!


C'est génial! Je pense qu'il sera difficile d'utiliser un tri pour renvoyer les documents dans l'ordre dans lequel vous interrogez, mais les documents sont ici - docs.mongodb.com/manual/reference/method/cursor.sort



1
votes

Je voudrais revenir sur la réponse de Willis et vous suggérer de faire des correspondances exactes sur chacun des composants de la chaîne. Cependant, si ce n'est pas assez flexible (c'est-à-dire que vous souhaitez également pouvoir faire des correspondances partielles dans chaque composant), je vous recommande de configurer un moteur ElasticSearch en miroir, en particulier compte tenu du nombre d'enregistrements que vous avez . MongoDB n'est pas vraiment conçu pour effectuer une recherche de texte de forme libre efficace.

Ma propre expérience personnelle était qu'avec environ 10 millions d'enregistrements, même avec l'indexation du champ de chaîne concaténé, la recherche de forme libre prenait> 30 secondes par requête, même avec suffisamment de RAM pour contenir l'index (assez gros). Comme j'essayais de faire une saisie automatique en temps réel sur un champ de texte, cela ne fonctionnerait pas :-)

ElasticSearch a plusieurs solutions pour répliquer automatiquement les données de mongodb dans sa propre base de données, donc j'ai trouvé une fois que j'ai tout configuré, tout s'est bien passé. J'utilise monstache ( https://github.com/rwynn/monstache ) mais il y en a d'autres aussi . Je réalise que ce n'est pas exactement ce que vous avez demandé, mais j'ai traversé un processus douloureux d'essayer d'optimiser mongodb pour la recherche de texte libre qui n'a pas pris une éternité et a finalement abandonné. S'il s'agit d'une requête courante et que vous avez besoin de performances décentes, je vous suggère fortement de rechercher quelque chose comme ElasticSearch qui est optimisé pour cela.


1 commentaires

Ce n'est pas une option très viable et j'avais pensé à peut-être utiliser la recherche élastique ou autre chose, mais je n'avais pas été plus loin que cela car j'étais très nouveau toi mongo lui-même et essayais de limiter la quantité de choses supplémentaires à apprendre dans un court laps de temps. . étant donné ce que vous avez dit à propos de vos expériences avec la recherche de texte libre, je n'ai peut-être pas le choix car ~ 30 s ou plus les temps de réponse sont beaucoup trop lents.