用于对电子邮件域进行排序和过滤的电子邮件集合的有效索引

问题描述 投票:4回答:1

我正在使用猫鼬(Mongoose)来保存电子邮件地址的中央集合,并且我还为用户和组织提供了集合。在我的应用程序中,我通过(已验证的)电子邮件域将用户与组织相关联。例如。 Acme Ltd拥有acme.com和acme.co.uk域,通过使用这些域从所有电子邮件中进行选择,我可以整理一个唯一的关联用户列表。

用户可以有多个电子邮件地址(1个主要电子邮件地址+多个次要电子邮件地址)。用户无法共享电子邮件地址(因此,“ verifiedBy”字段会在用户和电子邮件之间建立一对一关系)。

我的模式(当前)如下:

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 );

我的问题是,是否值得逆转setter中的电子邮件地址,并在getter中撤消它们-如我已经展示了-为了使_id上的基础MongoDb索引可以改善性能并使其更轻松地处理我所进行的查找描述了?

我已经考虑过的替代方法是:

  • 按原样存储电子邮件,并使用正则表达式按域部分选择用户(对我来说处理起来感觉很昂贵)
  • 将域部分存储在一个单独的字段中并对其进行索引(由于要有两个索引,并且需要重复的数据存储,因此会很昂贵)
mongodb mongoose data-modeling
1个回答
1
投票

第一个选项实际上应该运作良好。根据$regex docs

[...]如果正则表达式是“前缀表达式”,则可能会发生进一步的优化,这意味着所有潜在的匹配都以相同的字符串开头。 [...]

如果正则表达式以尖号(^)或左锚(\ A)开头,后跟一串简单符号,则它是“前缀表达式”。 [...]

实验

让我们检查它在包含约80万个文档的集合中的工作方式,其中约25%的人有一封电子邮件。分析的示例查询为$regex

没有索引:

{email: /^gmail/}

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

{email: 1}

如我们所见,它在执行时间和经过检查的文档方面都绝对有帮助(更多的经过检查的文档可能意味着更多的IO工作)。让我们看看如果忽略前缀并直接使用查询,它是如何工作的:db.users.find({email: /^gmail/}).explain('executionStats').executionStats // ... // "nReturned" : 2208, // "executionTimeMillis" : 5, // "totalKeysExamined" : 2209, // "totalDocsExamined" : 2208, // ...

没有索引:

{email: /gmail/}

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

{email: 1}

最后,索引有助于很多,尤其是在执行前缀查询时。看起来前缀查询足够快,可以在单个字段中保持原样。一个单独的字段may可以更好地利用索引(使用它!),但我认为5ms足够了。]

一如既往,我强烈建议您对数据进行测试并查看其性能,因为数据特征可能会影响性能。

© www.soinside.com 2019 - 2024. All rights reserved.