mongodb как иметь вложенный запрос с элементами из массива - PullRequest
1 голос
/ 30 сентября 2019

Вот примеры записей коллекции my_task: при изменении статуса задачи в поле status_changed_utc добавляется запись с кодом состояния и временем, статус 6 означает, что задача выполнена;Также на верхнем уровне есть поле required_completion_utc, указывающее ожидаемое время завершения задачи, так как мне узнать записи с фактическим временем завершения, более поздним, чем запрашиваемый_компонент_utc, для задач, созданных в этом месяце? Пожалуйста, обратите внимание, что требуемый_completion_utc и status_changed_utc со статусом 6 могут отсутствовать в записи.

пример записей:

   { 
        "_id" : ObjectId("5ce726ae92e2247db561a2f2"), 
       "created_utc" : ISODate("2019-05-23T23:03:10.000+0000"), 
        "requested_completion_utc" : ISODate("2019-05-23T00:00:00.000+0000"), 
        "status_changed_utc" : [
            {
                "status" : NumberInt(1), 
                "time" : ISODate("2019-05-23T23:03:10.000+0000")
            }, 
            {
                "status" : NumberInt(2), 
                "time" : ISODate("2019-05-23T23:05: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")
            }
        ]
    },

    { 
        "_id" : ObjectId("5ce726ae92e2247db561a232"), 
      "created_utc" : ISODate("2019-05-23T23:03:10.000+0000"), 
        "status_changed_utc" : [
            {
                "status" : NumberInt(1), 
                "time" : ISODate("2019-05-23T23:03:10.000+0000")
            }
        ]
    },

    { 
        "_id" : ObjectId("5ce726ae92e2247db561a231"), 
      "created_utc" : ISODate("2019-09-23T21:03:10.000+0000"), 
       "requested_completion_utc" : ISODate("2019-09-21T00:00:00.000+0000"), 
        "status_changed_utc" : [
            {
                "status" : NumberInt(1), 
                "time" : ISODate("2019-09-23T23:03:10.000+0000")
            }
        ]
    }

Ответы [ 3 ]

1 голос
/ 30 сентября 2019

Я думаю, что, возможно, есть более эффективный способ написать это, но я смог придумать это с помощью структуры агрегации.

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 } },
]);

С вашими документами сверху это возвращает следующее:

{ "_id" : ObjectId("5ce726ae92e2247db561a2f2"), "requested_completion_utc" : ISODate("2019-05-23T00:00:00Z"), "status_changed_utc" : [ { "status" : 6, "time" : ISODate("2019-05-23T23:05:09Z") } ], "isLate" : true }
{ "_id" : ObjectId("5ce726ae92e2247db561a231"), "requested_completion_utc" : ISODate("2019-09-21T00:00:00Z"), "status_changed_utc" : [ { "status" : 1, "time" : ISODate("2019-09-23T23:03:10Z") } ], "isLate" : true }

Обратите внимание, что возвращается только самый последний «поздний» нарушитель, если вы хотите, чтобы все операции, которые произошли после этого времени, вы могли использовать оператор агрегации $ unwind для создания отдельного документа для каждого элемента в массиве status_changed_utcа не этап $ project, нарезающий самый последний элемент каждого массива.

0 голосов
/ 05 октября 2019

У меня похожий вопрос.

Сначала сортировка, а затем проецирование последней не работает в моем случае, потому что мне нужно вывести более одного завершенного времени.

Вот мой вопрос, кроме использования функции $ 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")
        }
    ]
}
0 голосов
/ 01 октября 2019

следующий агрегированный запрос вернет идентификаторы задач, созданных в мае, который имеет статус завершено (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();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...