如何查询无固定键名的对象数组中的expiryDate字段?
问题:MongoDB高效过滤无固定键名的数组对象过期日期
我正在处理如下结构的JSON数据:
[ { "_id": { "$oid": "65143f76653355483f68981d" }, "date": "2023-09-27", "certificates": [ { "cnv2234": { "expiryDate": "2024-08-17" } }, { "crs14359": { "expiryDate": "2023-09-14" } }, { "crs14359": { "expiryDate": "2030-03-06" } }, { "crs14359": { "expiryDate": "2040-02-25" } } ] } ]
尝试了以下几种查询语句,均未返回任何数据:
query = { "certificates.expiryDate": { "$lte": "2023-09-14" } } query = { "certificates": { "$elemMatch": { "expiryDate": { "$lte": "2023-09-14" } } } } query = { "certificates": { "$elemMatch": { "expiryDate": { "$lte": "2023-09-14" } } } }
虽然可以获取全部数据后逐个遍历处理,但大数据集下不实用,需要修正查询以高效过滤无固定键名的对象数组中的expiryDate字段。
解决方案
问题原因
之前的查询失效,是因为certificates数组中的每个元素都是带动态键名的嵌套对象(比如{ "cnv2234": { expiryDate: ... } }),expiryDate并非直接在数组元素的顶层,常规的点语法或$elemMatch无法直接定位到该字段。
正确查询语句
使用$expr结合聚合表达式处理动态键,以下分两种MongoDB版本情况:
MongoDB 5.0+(支持$some)
query = { $expr: { $some: { input: "$certificates", in: { $lte: [ { $getField: "expiryDate", input: { $arrayElemAt: [{ $objectToArray: "$$this" }, 0] } }, "2023-09-14" ] } } } }
MongoDB 4.4及以下(用$anyElementTrue替代$some)
query = { $expr: { $anyElementTrue: { $map: { input: "$certificates", as: "cert", in: { $lte: [ { $getField: "expiryDate", input: { $arrayElemAt: [{ $objectToArray: "$$cert" }, 0] } }, "2023-09-14" ] } } } } }
表达式逻辑说明
$objectToArray:将每个动态键的证书对象(如{ "cnv2234": { expiryDate: ... } })转换为键值对数组[{ k: "cnv2234", v: { expiryDate: ... } }]$arrayElemAt:提取键值对数组的第一个元素(因为每个证书对象仅含一个键)$getField:从提取出的v对象中获取expiryDate字段值$some/$anyElementTrue:检查certificates数组中是否存在至少一个元素满足expiryDate <= "2023-09-14"的条件
性能优化
如果数据量较大,可创建表达式索引加速查询(MongoDB 4.2+支持):
db.collection.createIndex({ $expr: { $map: { input: "$certificates", as: "cert", in: { $getField: "expiryDate", input: { $arrayElemAt: [{ $objectToArray: "$$cert" }, 0] } } } } })
内容的提问来源于stack exchange,提问作者Unknownzdx
相关产品推荐
相关产品推荐

