MongoDb:$elemMatch,将字符串转换为数字
Posted
技术标签:
【中文标题】MongoDb:$elemMatch,将字符串转换为数字【英文标题】:MongoDb: $elemMatch with convert string to number 【发布时间】:2022-01-23 18:38:30 【问题描述】:我使用 MongoDB,我有这样一个集合。首先,我选择 tableId = 1 的所有文档,然后按 itemId 对它们进行分组 - 我得到 2 组 2 个文档。总而言之,我需要将这些组留在有此类文件的地方:1)城市=伦敦,2)年份大于或等于某个值。为此,我需要将值从字符串转换为数字。如何做到这一点?
在我下面的查询中,比较年份的部分是错误的。
[
"tableId": 1,
"itemId": 10,
"name": "City",
"value": "London"
,
"tableId": 1,
"itemId": 10,
"name": "StartYear",
"value": "2017"
,
"tableId": 1,
"itemId": 20,
"name": "City",
"value": "Paris"
,
"tableId": 1,
"itemId": 20,
"name": "StartYear",
"value": "2018"
,
"tableId": 2,
"itemId": 30,
"name": "City",
"value": "Madrid"
,
"tableId": 2,
"itemId": 30,
"name": "StartYear",
"value": "2016"
]
我的查询是:
db.collection.aggregate([
"$match":
tableId: 1
,
"$group":
_id:
itemId: "$itemId"
,
result:
$push: "$$ROOT"
,
"$match":
$and: [
"result":
$elemMatch:
"name": "City",
"value": "London"
,
"result":
$elemMatch:
$and: [
"name": "StartYear"
,
$lte: [
$toDouble: "$value"
,
2018
]
]
]
])
【问题讨论】:
【参考方案1】:查询
如果找到name/value
并且如果找到startYear/value
,则进行分组并计数
保留找到的那些
取消设置 2 个临时字段 cityFound,yearFound
Test code here
aggregate(
["$match":"$expr":"$eq":["$tableId", 1],
"$group":
"_id":"$itemId",
"cityFound":
"$sum":
"$cond":
["$and":
["$eq":["$name", "City"], "$eq":["$value", "London"]],
1, 0],
"yearFound":
"$sum":
"$cond":
["$and":
["$eq":["$name", "StartYear"],
"$lte":["$toInt":"$value", 2018]],
1, 0],
"result":"$push":"$$ROOT",
"$match":
"$expr":
"$and":["$gt":["$cityFound", 0], "$gt":["$yearFound", 0]],
"$unset":["cityFound", "yearFound"]])
上面的速度很快,因为它在分组时进行检查。
要将字符串转换为数字,我们可以使用$toInt
或toDouble
。
但这些是聚合运算符,匹配时它们应该在 $expr
之下。
您的查询的问题是您在查询运算符中使用聚合表达式,这不起作用,例如$value
是聚合表达式,$toDouble
也不能同时使用。
您可以使用允许内部聚合表达式的$filter
,而不是elem-match,并检查过滤器数组是否为空。
【讨论】:
【参考方案2】:您可以先使用$map
进行条件映射,将年份转换为数值。然后执行您的$elemMatch
标准。
db.collection.aggregate([
"$match":
tableId: 1
,
"$addFields":
"value":
"$cond":
"if":
$eq: [
"$name",
"StartYear"
]
,
"then":
$toInt: "$value"
,
"else": "$value"
,
"$group":
_id:
itemId: "$itemId"
,
result:
$push: "$$ROOT"
,
"$match":
$and: [
"result":
$elemMatch:
"name": "City",
"value": "London"
,
"result":
$elemMatch:
"name": "StartYear",
"value":
$lte: 2018
]
])
这是Mongo playground 供您参考。
【讨论】:
非常适合这个系列。但我的真实数据更复杂:许多不同的字段、层次结构。地图将如何运作? @egeo 更新了解决方案,将条件转换放在更早的阶段,以避免构造大的$map
内容
一个很好的改变。一个小缺点:在数据库中,值以字符串形式存储,并以数字形式返回。这对我来说很好,但其他人可能想查看原始数据类型。
@egeo 请参阅使用辅助字段进行转换的this version。但是,如果可能的话,我建议将值存储在适当的数据类型中,以避免查询中的这种转换开销。以上是关于MongoDb:$elemMatch,将字符串转换为数字的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB (Mongoose) 如何使用 $elemMatch 返回所有文档字段
mongodb 中的Multikey Index Bounds解释$elemMatch
MongoDb $elemMatch 与 $lookup 变量
带有 elemMatch 的 MongoDB 查询,用于从对象内部匹配嵌套数组数据 [重复]