如何在Mongoose中按复合索引的部分字段查询文档?
我在MongoDB中通过Mongoose定义了一个使用复合_id索引的Contact Schema,以此保证mail与organisation的组合唯一性,Schema代码如下:
let Schema = mongoose.Schema({ _id:{ organisation:{ type:mongoose.ObjectId, required:true, ref:'organisations' }, mail:{ type:String, required:true } }, firstName:{ type:String, }, lastName:{ type:String }, }) //some methods and statics here const Contact = mongoose.model('contacts',Schema);
我需要查询指定organisation id对应的所有文档(用于组织联系人列表),无需指定mail。尝试了以下两种查询方式都返回空结果:
第一种查询:
await Contact .find( { _id:{ organisation:organisation, mail:{"$exists":true} } } )
第二种使用正则表达式的查询:
await Contact .find( { _id:{ organisation:organisation, mail:{"$regex":"[^]*"} } } )
只有指定完整mail时才能查到数据,但这对组织联系人列表毫无用处。想知道哪里出错了?
为什么之前的查询失效?
你当前的查询是把_id当作一个完整的复合对象来匹配,MongoDB会严格校验整个_id的结构是否完全符合查询条件。比如{_id: {organisation: xxx, mail: {$exists: true}}}会被解读为:寻找_id对象中organisation等于指定值且mail字段满足$exists: true的文档,但这种写法本质是在匹配_id的完整结构,而非部分匹配嵌套的子字段,所以无法返回预期结果。
正确的查询写法
要匹配复合_id里的子字段,必须使用点符号来精准定位嵌套字段,而不是将_id作为整体对象构造查询:
await Contact.find({ "_id.organisation": organisation })
这样MongoDB会正确识别你要筛选_id对象下organisation字段等于指定值的所有文档,自动忽略mail字段的具体值,从而返回该组织下的全部联系人。
优化建议
虽然复合_id能实现organisation与mail的组合唯一性,但如果后续有更多复杂查询需求,推荐将这两个字段设为普通字段,再单独创建复合唯一索引,这样查询逻辑会更直观:
let Schema = mongoose.Schema({ organisation:{ type:mongoose.ObjectId, required:true, ref:'organisations' }, mail:{ type:String, required:true }, firstName:{ type:String, }, lastName:{ type:String }, }) // 创建复合唯一索引,保证organisation与mail的组合唯一性 Schema.index({ organisation: 1, mail: 1 }, { unique: true }); const Contact = mongoose.model('contacts',Schema);
此时查询该组织下的联系人就可以直接写:
await Contact.find({ organisation: organisation })
写法更简洁,也更符合Mongoose的常规使用习惯。
内容的提问来源于stack exchange,提问作者BootEXE

