如何在Mongoose中实现value字段内嵌套对象与字符串的统一搜索
MongoDB嵌套对象字段搜索问题
数据结构
我的数据库中有两类数据:
{ "name": "Description", "value": "Lorem ipsum" } { "name": "Properties", "value": { "type": "Big house", "address": "21 Main street", "city": "New York" } }
需求
需要实现搜索功能,匹配value字段内的任意内容:
- 搜索'Description'中包含'Lorem'(正常工作)
- 搜索'Properties'中包含'Big'(无法工作)
- 搜索'Properties'中包含'type'(无法工作)
- 搜索'Properties'中包含'address'(无法工作)
当前问题
搜索仅能匹配value为字符串时的内容,当value为嵌套对象时无结果返回。需要让Mongo将value对象视为字符串进行搜索,例如搜索'type'或'house'时能筛选出'Properties'对象。
现有Mongoose聚合代码
{ $group: { _id: "$_id", key: { $first: "$key" }, fields: { $push: { "k": "$fields.name", "v": "$fields.value" } } }, }, { $match: { [filter.name]: { $regex: filter.value || '', } } }
编辑补充
使用Charchit Kapoor提供的解决方案后,出现Feature not supported错误,错误信息如下:
[Nest] 150067 - 05/12/2023, 1:50:53 PM ERROR [ExceptionsHandler] Feature not supported MongoServerError: Feature not supported at Connection.onMessage (/node_modules/mongodb/src/cmap/connection.ts:438:20) at MessageStream.<anonymous> (/node_modules/mongodb/src/cmap/connection.ts:256:56) at MessageStream.emit (events.js:376:20) at MessageStream.emit (domain.js:532:15) at processIncomingData (/node_modules/mongodb/src/cmap/message_stream.ts:193:14) at MessageStream._write (/node_modules/mongodb/src/cmap/message_stream.ts:70:5) at writeOrBuffer (internal/streams/writable.js:358:12) at MessageStream.Writable.write (internal/streams/writable.js:303:10) at TLSSocket.ondata (internal/streams/readable.js:745:22) at TLSSocket.emit (events.js:376:20)
当前尝试的代码:
{ $match: { name: filter.name, $expr: { $regexMatch: { input: { $function: { body: "function (doc) { return JSON.stringify(doc); }", args: [ "$value" ], lang: "js" } }, regex: "ips" } } } }
解决方案
方案1:用原生聚合操作符转换嵌套对象(兼容多数版本)
如果你的MongoDB版本不支持$function,或者环境限制了JS函数,可以用原生操作符把嵌套对象转为字符串:
{ $addFields: { valueAsString: { $reduce: { input: { $objectToArray: "$value" }, initialValue: "", in: { $concat: [ "$$value", "$$this.k", ":", { $cond: [{ $isString: "$$this.v" }, "$$this.v", { $toString: "$$this.v" }] }, ", " ] } } } } }, { $match: { valueAsString: { $regex: filter.value || '', $options: 'i' } // 可选i参数忽略大小写 } }
这个方法会把value的键值对拼接成字符串,比如Properties的value会转为type:Big house, address:21 Main street, city:New York, ,这样就能匹配键(如type)或值(如Big)。
方案2:启用JS函数支持(MongoDB 4.4+)
如果环境支持$function但报错,可能是部署配置限制,检查实例权限后可以用更简洁的写法:
{ $match: { $expr: { $regexMatch: { input: { $function: { body: (doc) => JSON.stringify(doc), args: ["$value"], lang: "js" } }, regex: filter.value || '', options: 'i' } } } }
方案3:预存字符串化字段
如果聚合转换性能不足,可以在数据插入/更新时,同步存储一个valueString字段,直接存JSON.stringify(value)的结果:
// 插入数据时 await Model.create({ name: "Properties", value: { type: "Big house", address: "21 Main street", city: "New York" }, valueString: JSON.stringify({ type: "Big house", address: "21 Main street", city: "New York" }) }); // 查询时 { $match: { valueString: { $regex: filter.value || '', $options: 'i' } } }
内容的提问来源于stack exchange,提问作者decebal
相关产品推荐
相关产品推荐

