如何查询MongoDB数组内对象?查询返回空数组问题求助
问题描述
现有MongoDB的messages集合文档结构如下:
{ "_id" : ObjectId("64caa6599b2ec8d2da9e8e58"), "message" : { "text" : "hey" }, "users" : [ { "from" : "64c7acfbe872c97c7912ca09", "to" : "64c2d5612172d620ac18714f" } ], "sender" : ObjectId("64c7acfbe872c97c7912ca09"), "createdAt" : ISODate("2023-08-02T18:54:17.176+0000"), "updatedAt" : ISODate("2023-08-02T18:54:17.176+0000"), "__v" : NumberInt(0) }
想要查询包含指定from和to的users数组的文档,试了两种写法都返回空数组:
const messages = await messageModel.find({ // first try users: { $all: [from, to], }, // second try // users: { // $elemMatch: { // from: from, // to: to, // } }) .sort({ updatedAt: 1 }) console.log("messages=>" ,JSON.stringify(messages))
对应的Mongoose MessageModel定义如下:
const mongoose = require("mongoose"); const messageSchema = mongoose.Schema( { message: { text: { type: String, required: true, }, }, users: Array, sender: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, }, }, { timestamps: true, } ); module.exports = mongoose.model("messages", messageSchema);
疑问:users字段定义为Array类型是不是不能用$elemMatch?怎么才能正确查到目标文档?
解决方案
之前查询失败的原因
$all写法的问题:$all是用来匹配数组包含所有指定元素的文档,但你传的from和to是字符串ID,而users数组里的元素是对象,类型完全不匹配,肯定查不到。$elemMatch写法的问题:users定义成Array类型是可以用$elemMatch的,你的问题一是代码没写完(缺了闭合括号),二是可能存在类型不匹配——文档里的from/to是字符串,但你传入的变量是ObjectId类型,或者反过来,导致匹配失败。
正确的查询方法
方法一:修正$elemMatch查询
先确保from和to的类型和文档里的一致(文档里是字符串就传字符串,是ObjectId就转成对应类型),然后补全语法:
const messages = await messageModel.find({ users: { $elemMatch: { from: from, to: to } } }) .sort({ updatedAt: 1 });
方法二:直接嵌套字段查询(不用$elemMatch)
如果只是匹配数组中单个对象的多个字段,MongoDB支持直接写嵌套路径,写法更简洁:
const messages = await messageModel.find({ "users.from": from, "users.to": to }) .sort({ updatedAt: 1 });
长期优化:完善Schema定义
为了彻底避免类型匹配问题,建议把users数组的结构明确写在Schema里,这样Mongoose会自动处理类型转换,还能做字段校验:
const messageSchema = mongoose.Schema( { message: { text: { type: String, required: true, }, }, users: [{ from: { type: mongoose.Schema.Types.ObjectId, ref: "User" }, to: { type: mongoose.Schema.Types.ObjectId, ref: "User" } }], sender: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, }, }, { timestamps: true, } );
这样定义后,不管你传的是字符串ID还是ObjectId,Mongoose都会自动转换成对应类型,不用再手动处理类型问题。
内容的提问来源于stack exchange,提问作者siimransiingh
相关产品推荐
相关产品推荐

