mongodb如何使用数组中的元素进行嵌套查询

时间:2019-09-30 18:44:28

标签: mongodb mongodb-query

这里是my_task集合的样本记录,当任务状态改变时,将有一条记录添加到status_changed_utc字段中,其中包含状态代码和时间,状态6表示任务已完成;在最顶层还具有requested_completion_utc字段,表示该任务的预期完成时间是多少,那么对于该月创建的任务,我如何找出实际完成时间晚于requested_completion_utc的记录?请注意,记录中可能不存在状态为6的request_completion_utc和status_changed_utc。

样本记录:

formikProps

3 个答案:

答案 0 :(得分:1)

我认为可能有一种更有效的编写方法,但是我能够使用聚合框架提出。

Hierarchy

您的文档从上面返回以下内容:

db.tasks.aggregate([
  // Only show results with the requested_completion_utc value
  { $match: { requested_completion_utc: { $exists: 1 } } },

  // Get only the most recent status change by slicing the last element of the array
  { $project: { 
    id: 1, 
    requested_completion_utc: 1, 
    status_changed_utc: { $slice: [ "$status_changed_utc", -1 ] } 
  }},

  // Perform the comparison to determine if the operation was late
  { $project: { 
    id: 1,
    requested_completion_utc: 1,
    status_changed_utc: 1,
    isLate: { $gt: [ "$requested_completion_utc", "$status_changed_utc.time" ] },
  }},

  // Only display the late results
  { $match: { isLate: true } },
]);

请注意,这仅返回最新的“后期”违规者,如果您希望在此之后发生的所有操作,则可以使用$ unwind聚合运算符为status_changed_utc数组中的每个元素而不是$创建一个单独的文档。项目阶段将每个数组的最新元素切片。

答案 1 :(得分:0)

以下汇总查询将返回在5月创建的任务的ID,这些任务的状态为(6),该状态在请求的完成日期之后已完成。希望我能正确理解您的要求。

db.my_task.aggregate([
    {
        "$match": {
            "created_utc": {
                "$gt": ISODate("2019-04-30T18:30:00.000Z"),
                "$lt": ISODate("2019-05-31T18:30:00.000Z")
            },
            "status_changed_utc": {
                "$elemMatch": {
                    "status": 6
                }
            }
        }
    },
    {
        "$unwind": "$status_changed_utc"
    },
    {
        "$project": {
            "status": "$status_changed_utc.status",
            "isLate": {
                "$gt": [
                    "$status_changed_utc.time",
                    "$requested_completion_utc"
                ]
            },
            "_id": 1
        }
    },
    {
        "$match": {
            "isLate": true,
            "status": 6
        }
    },
    {
        "$project": {
            "_id": 1
        }
    }
])

如果有人感兴趣,这是生成上述命令的c#代码:

using MongoDB.Entities;
using System;
using System.Linq;

namespace StackOverflow
{
    [Name("my_task")]
    public class MyTask : Entity
    {
        public DateTime created_utc { get; set; }
        public DateTime requested_completion_utc { get; set; }
        public Status[] status_changed_utc { get; set; }
    }

    public class Status
    {
        public int status { get; set; }
        public DateTime time { get; set; }
    }

    public static class Program
    {
        public static void Main()
        {
            new DB("my_database", "localhost");

            var start = new DateTime(2019, 05, 1, 0, 0, 0).ToUniversalTime();
            var end = new DateTime(2019, 06, 1, 0, 0, 0).ToUniversalTime();

            var result = DB.Queryable<MyTask>()
                           .Where(t =>
                                  t.created_utc > start &&
                                  t.created_utc < end &&
                                  t.status_changed_utc.Any(s => s.status == 6))
                           .SelectMany(t => t.status_changed_utc,
                                            (t, s) => new
                                            {
                                                t.ID,
                                                s.status,
                                                isLate = s.time > t.requested_completion_utc
                                            })
                           .Where(x => x.isLate == true && x.status == 6)
                           .Select(x => x.ID)
                           .ToList();
        }
    }
}

答案 2 :(得分:0)

我有类似的问题。

先排序,然后再投影最新的一个就不能解决我的问题,因为我需要输出多个完成时间。

这是我的问题,除了使用$ unwind函数外,还有其他方法可以处理文档中的此数组吗?

我不想使用$ unwind的原因是要构造一个数组字段以为每个元素输出文档,并且我对查询应用了多个过滤器,这将增加与其他记录匹配的记录总数过滤器。

这是我的样本记录

"_id" : ObjectId("5d9b69fae4757402b4b4ca0d"), 
"status_changed_utc" : [
    {
        "status" : NumberInt(1), 
        "time" : ISODate("2019-05-20T23:03:10.000+0000")
    }, 
    {
        "status" : NumberInt(2), 
        "time" : ISODate("2019-05-23T23:04:03.000+0000")
    }, 
    {
        "status" : NumberInt(4), 
        "time" : ISODate("2019-05-23T23:05:06.000+0000")
    }, 
    {
        "status" : NumberInt(5), 
        "time" : ISODate("2019-05-23T23:05:07.000+0000")
    }, 
    {
        "status" : NumberInt(6), 
        "time" : ISODate("2019-05-23T23:05:09.000+0000")
    }
], 
"requested_completion_utc" : ISODate("2019-05-22T23:05:09.000+0000")

当我尝试使用此查询时

db.getCollection("test").aggregate(
[
    { 
        "$match" : {
            "requested_completion_utc" : {
                "$exists" : 1.0
            }
        }
    }, 
    { 
        "$project" : {
            "_id" : 1.0, 
            "requested_completion_utc" : 1.0, 
            "status_changed_utc" : {
                "$slice" : [
                    "$status_changed_utc", 
                    -1.0
                ]
            }
        }
    }
]

它将把这个结果返回给我。

{ 
"_id" : ObjectId("5d9b69fae4757402b4b4ca0d"), 
"requested_completion_utc" : ISODate("2019-05-22T23:05:09.000+0000"), 
"status_changed_utc" : [
    {
        "status" : NumberInt(6), 
        "time" : ISODate("2019-05-23T23:05:09.000+0000")
    }
]
}

是否可以返回状态NumberInt(2)代替对我来说是最新完成状态的NumberInt(6)?

这是我预期的结果

{ 
    "_id" : ObjectId("5d9b69fae4757402b4b4ca0d"), 
    "requested_completion_utc" : ISODate("2019-05-22T23:05:09.000+0000"), 
    "status_changed_utc" : [
        {
            "status" : NumberInt(2), 
            "time" : ISODate("2019-05-23T23:04:03.000+0000")
        }
    ]
}