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"]])

上面的速度很快,因为它在分组时进行检查。

要将字符串转换为数字,我们可以使用$toInttoDouble。 但这些是聚合运算符,匹配时它们应该在 $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 查询,用于从对象内部匹配嵌套数组数据 [重复]

MongoDB:$ elemMatch和$ and和在数组内查找对象之间有什么区别?

使用Java中的$ elemMatch实现Mongodb查询