4 votes

Indexation efficace d'une collection d'emails pour un classement et un filtrage par domaine d'email

J'utilise Mongoose pour conserver une collection centrale d'adresses électroniques, et j'ai également des collections pour les utilisateurs et les organisations. Dans mon application, j'associe les utilisateurs aux organisations par le biais de leurs domaines de messagerie (vérifiés). Par exemple, Acme Ltd possède les domaines acme.com et acme.co.uk, et en sélectionnant tous les courriels utilisant ces domaines, je peux établir une liste unique d'utilisateurs associés.

Les utilisateurs peuvent avoir plusieurs adresses électroniques (1 adresse principale + plusieurs adresses secondaires). Les utilisateurs ne peuvent pas partager leurs adresses électroniques (d'où le champ "verifiedBy" qui impose une relation univoque entre les utilisateurs et les adresses électroniques).

Mon schéma est (actuellement) le suivant :

const emailSchema = new Schema({
    _id: { 
        type: String,
        get: function idReverse(_id) { if(_id) return _id.split("@").reverse().join("@"); },
        set: (str) => { str.trim().toLowerCase().split("@").reverse().join("@") }
    },
    verifiedBy: { type: String, ref: 'User' }
}, options );

Ma question est de savoir s'il vaut la peine d'inverser les parties du domaine de l'adresse e-mail dans le setter et d'inverser les parties du domaine dans le setter. de l'adresse email dans le setter, et de ne pas les inverser dans le getter - comme comme je l'ai montré - afin que l'index MongoDb sous-jacent sur _id puisse améliorer les améliorer les performances et faciliter la gestion des types de recherches que j'ai décrits ? décrit ?

Les alternatives que j'ai déjà envisagées sont les suivantes :

  • Stocker l'email tel quel et utiliser une expression rationnelle pour sélectionner les utilisateurs par domaine (cela me semble coûteux en termes de traitement).
  • Stocker la partie domaine dans un champ séparé et l'indexer (cela semble coûteux car il y aurait deux index et un double stockage des données).

1voto

Radosław Miernik Points 2336

Les premières options devraient en fait fonctionner assez bien. D'après le $regex documents :

[...] Une optimisation supplémentaire peut avoir lieu si l'expression régulière est une "expression préfixe", ce qui signifie que toutes les correspondances potentielles commencent par la même chaîne de caractères. [...]

Une expression régulière est une "expression préfixe" si elle commence par un caret (^) ou une ancre gauche ( \A ), suivie d'une chaîne de symboles simples. [...]

Expérience

Voyons comment cela fonctionne sur une collection avec ~800k documents, ~25% d'entre eux ont un email. L'exemple de requête analysée est {email: /^gmail/} .

Sans index :

db.users.find({email: /^gmail/}).explain('executionStats').executionStats
// ...
//    "nReturned" : 2208,
//    "executionTimeMillis" : 250,
//    "totalKeysExamined" : 0,
//    "totalDocsExamined" : 202720,
// ...

Avec un {email: 1} l'index :

db.users.find({email: /^gmail/}).explain('executionStats').executionStats
// ...
//    "nReturned" : 2208,
//    "executionTimeMillis" : 5,
//    "totalKeysExamined" : 2209,
//    "totalDocsExamined" : 2208,
// ...

Comme nous le voyons, cela est très utile - à la fois en termes de temps d'exécution et de documents examinés (plus de documents examinés signifie éventuellement plus de travail d'IO). Voyons comment cela fonctionne si nous ignorons le préfixe et utilisons la requête plus directement : {email: /gmail/} .

Sans index :

db.users.find({email: /gmail/}).explain('executionStats').executionStats
// ...
//    "nReturned" : 2217,
//    "executionTimeMillis" : 327,
//    "totalKeysExamined" : 0,
//    "totalDocsExamined" : 202720,
// ...

Avec un {email: 1} l'index :

db.users.find({email: /gmail/}).explain('executionStats').executionStats
// ...
//    "nReturned" : 2217,
//    "executionTimeMillis" : 210,
//    "totalKeysExamined" : 200616,
//    "totalDocsExamined" : 2217,
// ...

En fin de compte, l'indice aide beaucoup surtout lorsqu'il s'agit d'une requête préfixée. Il semble que la requête préfixée soit suffisamment rapide pour la conserver telle quelle, dans un seul champ. Un champ séparé peut utiliser l'index encore mieux (jouez avec !), mais 5ms est suffisant, je pense.

Comme toujours, je vous encourage vivement à effectuer des tests sur vos données et à voir comment elles se comportent, car les caractéristiques des données peuvent avoir un impact sur les performances.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X