【问题标题】:Get the all the document with only lasted subdocument in mongodb获取 mongodb 中仅包含最后一个子文档的所有文档
【发布时间】:2022-01-17 22:31:45
【问题描述】:

我有一些这样的记录,很多记录在子文档list

{
  id:1
  name:a
  list:[
   {type:x,time:1/1/2021},
   {type:y,time:1/1/2022}
  ]
},
{
  id:2
  name:b
  list:[
    {type:x,time:1/1/2021},
    {type:y,time:1/1/2022},
    {type:y,time:1/1/2023}
  ]
}

所以我需要获取具有列表最新时间类型y

的记录
{
    id:1
    name:a
    list:[{type:y,time:1/1/2022}]
},
{
    id:2
    name:b
    list:[{type:y,time:1/1/2023}]
}

所以我最近才使用mongodb,对这个案例没有任何想法

【问题讨论】:

  • 这些都是字符串吗,例如{type:"x",time:"1/1/2021"} ?

标签: mongodb mongodb-query


【解决方案1】:

您可以通过$unwindlist$sort通过time字段、$group再次获取最新文档。

db.collection.aggregate([
  {
    "$unwind": "$list"
  },
  {
    $match: {
      "list.type": "y"
    }
  },
  {
    $sort: {
      "list.time": -1
    }
  },
  {
    $group: {
      _id: "$_id",
      name: {
        "$first": "$name"
      },
      list: {
        $first: "$list"
      }
    }
  },
  {
    "$addFields": {
      "list": [
        "$list"
      ]
    }
  }
])

这是Mongo playground 供您参考。

【讨论】:

  • 需要在某处添加$filter$match。 OP 要求在给定的type 上进行匹配,例如y.
  • @BuzzMoschetti 是的,你是对的。更新了答案,感谢您的有用提醒!
【解决方案2】:

这是一个替代解决方案,它使用$map$filter 将文档数量减少到$unwind。它还将字符串日期转换为真正的可排序 ISODate。

db.foo.aggregate([
    {$addFields: {

        // filter() for only type y, then pass that list to map() and                                    
        // convert the time to ISODate.  i.e. X = map(filter($list))                                     
        X: {$map: {
            input: {$filter: {
                input: "$list",
                as: "zz",
                cond: {$eq:[ '$$zz.type','y']}
            }},
            as:"qq",
            in: {
                "type":"$$qq.type", // carry fwd "type" for convenience                                  
                "time":{$dateFromString: {dateString: "$$qq.time",format:"%m/%d/%Y"}}
            }
        }}
    }}

    ,{$unwind: "$X"}
    ,{$sort: {"X.time":-1}}
    ,{$group: {_id: "$id",
               name: {$first: "$name"},
               type: {$first: "$X.type"},
               time: {$first: "$X.time"}
              }}



]);

产生这样的东西:

{
    "_id" : 1,
    "name" : "a",
    "type" : "y",
    "time" : ISODate("2022-01-01T00:00:00Z")
}
{
    "_id" : 2,
    "name" : "b",
    "type" : "y",
    "time" : ISODate("2023-01-01T00:00:00Z")
}

我没有将 typetime 放回名为 list 的数组中的子文档中,因为只能有一个最新项目,并且它避免了额外的 $addFields 阶段将其“包装”在一个数组中.

【讨论】:

    【解决方案3】:

    查询

    • 从最小值开始减少{"type": "y", "time": "1/1/0000"}
    • 如果我们找到日期为 y 和更大日期的成员,我们会更新 $$value 中的时间
    • 否则保留旧的
    • 完成后,我们检查是否发现更大的 y 值
    • 如果我们这样做了,我们会这样做 [member_we_found] 否则 []

    *如果许多有相同的最大日期,则只保留最大日期,只保留 1
    *它更嵌套但不使用unwind/group/sort,我主要将其作为替代文档本地发送,全部使用reduce

    Test code here

    aggregate(
    [{"$set": 
        {"list": 
          {"$reduce":  
            {"input": "$list",
              "initialValue": {"type": "y", "time": "1/1/0000"},
              "in": 
              {"$cond": 
                [{"$and": 
                    [{"$eq": ["$$this.type", "y"]},
                      {"$gt": 
                        [{"$dateFromString": 
                          {"dateString": "$$this.time", "format": "%m/%d/%Y"}},
                         {"$dateFromString": 
                          {"dateString": "$$value.time", "format": "%m/%d/%Y"}}]}]},
                  {"$setField": 
                    {"field": "time", "input": "$$value", "value": "$$this.time"}},
                  "$$value"]}}}}},
      {"$set": 
        {"list": 
          {"$cond": 
            [{"$eq": 
                [{"$getField": {"field": "time", "input": "$list"}}, "1/1/0000"]},
              [],
              ["$list"]]}}}])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-20
      • 2015-10-31
      • 1970-01-01
      • 2017-01-29
      • 2021-03-11
      • 2017-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多