MongoDB中使用$in匹配数组变量无结果?求正确实现方法
问题场景
现有test_collection集合数据如下:
{ "event_type" : "ENTRY", "DateTime" : ISODate("2024-02-08T00:00:00.000+0000") }, { "event_type" : "ENTRY", "DateTime" : ISODate("2024-02-09T00:00:00.000+0000") }, { "event_type" : "ENTRY", "DateTime" : ISODate("2024-02-10T00:00:00.000+0000") }, { "event_type" : "DELETE", "DateTime" : ISODate("2024-02-10T00:00:00.000+0000") }, { "event_type" : "DELETE", "DateTime" : ISODate("2024-02-24T00:00:00.000+0000") }
需求是找出所有DateTime与任意DELETE类型文档DateTime匹配的ENTRY文档,编写的Shell脚本如下:
var col = db.getCollection("test_collection"); var delete_dates = col.find({"event_type" : "DELETE" }).map(function(x) {return x.DateTime}); col.find({"event_type" : "ENTRY", "DateTime" : {$in : delete_dates}});
实际运行时,delete_dates数组确实包含两个预期的日期值,但查询返回空结果;而将{$in : delete_dates}替换为{$in : [ ISODate("2024-02-10T00:00:00.000+0000"), ISODate("2024-02-24T00:00:00.000+0000")]}则能正常返回结果。
问题原因
通过find().map()获取的delete_dates数组元素是JavaScript原生Date对象,而MongoDB集合中存储的DateTime是BSON规范的ISODate类型(Mongo Shell中显示为ISODate(...))。尽管两者代表的时间数值相同,但MongoDB查询会进行严格的类型匹配,JS原生Date对象无法匹配集合中的BSON日期类型数据,因此返回空结果。手动调用ISODate()构造函数生成的是符合BSON规范的日期对象,所以能正确匹配。
解决方法
方法1:显式转换为BSON日期对象
在map函数中将JS Date对象转换为Mongo Shell的ISODate对象:
var col = db.getCollection("test_collection"); var delete_dates = col.find({"event_type": "DELETE"}).map(function(x) { return ISODate(x.DateTime.toISOString()); }); col.find({"event_type": "ENTRY", "DateTime": {$in: delete_dates}});
方法2:通过聚合直接提取BSON日期数组
使用聚合查询的$push操作直接从集合中提取BSON日期数组,避免类型转换问题:
var col = db.getCollection("test_collection"); var delete_dates = col.aggregate([ {$match: {"event_type": "DELETE"}}, {$group: {_id: null, dates: {$push: "$DateTime"}}} ]).toArray()[0].dates; col.find({"event_type": "ENTRY", "DateTime": {$in: delete_dates}});
方法3:单步聚合查询(更高效)
无需提前获取日期数组,直接用聚合关联查询完成需求,减少客户端与服务器的交互:
db.getCollection("test_collection").aggregate([ {$match: {"event_type": "ENTRY"}}, {$lookup: { from: "test_collection", localField: "DateTime", foreignField: "DateTime", as: "matches" }}, {$match: {"matches.event_type": "DELETE"}}, {$project: {matches: 0}} // 移除冗余的matches字段 ]);
内容的提问来源于stack exchange,提问作者EatATaco
相关产品推荐
相关产品推荐

