我正在使用猫鼬(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索引可以改善性能并使其更轻松地处理我所进行的查找描述了?
我已经考虑过的替代方法是:
第一个选项实际上应该运作良好。根据$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足够了。]
一如既往,我强烈建议您对数据进行测试并查看其性能,因为数据特征可能会影响性能。