Найти предстоящие документы на основе даты вложенного документа - PullRequest
0 голосов
/ 04 января 2019

Предположим, у меня есть несколько документов о событиях MongoDB, в каждом из которых есть несколько сеансов, которые проводятся в разные даты. Мы можем представить это как:

db.events.insert([
  {
    _id: '5be9860fcb16d525543cafe1',
    name: 'Past',
    host: '5be9860fcb16d525543daff1',
    sessions: [
      { date: new Date(Date.now() - 1e8 ) },
      { date: new Date(Date.now() + 1e8 ) }
    ]
  }, {
    _id: '5be9860fcb16d525543cafe2',
    name: 'Future',
    host: '5be9860fcb16d525543daff2',
    sessions: [
      { date: new Date(Date.now() + 2e8) },
      { date: new Date(Date.now() + 3e8) }
    ]
  }
]);

Я бы хотел найти все события, у которых еще не было первой сессии. Поэтому я бы хотел найти «Будущее», но не «Прошлое».

В настоящее время я использую Mongoose и Express для:

  Event.aggregate([
    { $unwind: '$sessions' }, {
      $group: {
        _id: '$_id',
        startDate: { $min: '$sessions.date' }
      }
    },
    { $sort:{ startDate: 1 } }, {
      $match: { startDate: { $gte: new Date() } }
    }
  ])
    .then(result => Event.find({ _id: result.map(result => result._id) }))
    .then(event => Event.populate(events, 'host'))
    .then(events => res.json(events))

Но я чувствую, что у меня тяжелая погода из-за этого. Два попадания в базу данных (три, если вы включите оператор populate) и большой сложный оператор aggregate.

Есть ли более простой способ сделать это? В идеале тот, который включает только одну поездку в базу данных.

Ответы [ 3 ]

0 голосов
/ 04 января 2019

Возможно ли, что вы можете просто войти в сеансы каждого события и откатить каждое событие, где все даты сеансов только в будущем? Что-то вроде этого? Возможно, нужно настроить ..

db.getCollection("events").aggregate(
    [

        {$match:{'$and':
               [
                   {'sessions.date':{'$gt': new Date()}}, 
                   {'sessions.date':{ '$not': {'$lt': new Date()}}} 
               ]
             }}
    ]
);
0 голосов
/ 06 января 2019

Вам не нужно использовать $unwind и $group, чтобы найти $min дату из массив. Вы можете напрямую использовать $min, чтобы извлечь минимальную дату из массива session, а затем использовать $lookup для заполнения host ключ

db.events.aggregate([
  { "$match": { "sessions.date": { "$gte": new Date() }}},
  { "$addFields": { "startDate": { "$min": "$sessions.date" }}},
  { "$match": { "startDate": { "$gte": new Date() }}},
  { "$lookup": {
    "from": "host",
    "localField": "host",
    "foreignField": "_id",
    "as": "host"
  }},
  { "$unwind": "$host" }
])
0 голосов
/ 04 января 2019

Вы можете использовать $reduce, чтобы сложить массив и найти, если какой-либо из элементов имеет прошедший сеанс.

Чтобы проиллюстрировать это, рассмотрите возможность запуска следующего агрегатного конвейера:

db.events.aggregate([
    { "$match": { "sessions.date": { "$gte": new Date() } } },
    { "$addFields": {
        "hasPastSession": { 
            "$reduce": {
                "input": "$sessions.date",
                "initialValue": false,
                "in": { 
                    "$or" : [
                        "$$value", 
                        { "$lt": ["$$this", new Date()] }
                     ] 
                 }
            }
       }
    } },
    //{ "$match": { "hasPastSession": false } }
])

На основании вышеприведенного примера получим следующие документы с дополнительным полем

/* 1 */
{
    "_id" : "5be9860fcb16d525543cafe1",
    "name" : "Past",
    "host" : "5be9860fcb16d525543daff1",
    "sessions" : [ 
        {
            "date" : ISODate("2019-01-03T12:04:36.174Z")
        }, 
        {
            "date" : ISODate("2019-01-05T19:37:56.174Z")
        }
    ],
    "hasPastSession" : true
}

/* 2 */
{
    "_id" : "5be9860fcb16d525543cafe2",
    "name" : "Future",
    "host" : "5be9860fcb16d525543daff2",
    "sessions" : [ 
        {
            "date" : ISODate("2019-01-06T23:24:36.174Z")
        }, 
        {
            "date" : ISODate("2019-01-08T03:11:16.174Z")
        }
    ],
    "hasPastSession" : false
}

Вооружившись этим агрегированным конвейером, вы можете использовать $expr и использовать выражение конвейера в качестве запроса в find() (или с использованием вышеописанной агрегированной операции, но с включенным шагом конвейера $match в конце) как

db.events.find(
    { "$expr": {
        "$eq": [
            false,
            { "$reduce": {
                "input": "$sessions.date",
                "initialValue": false,
                "in": { 
                    "$or" : [
                        "$$value", 
                        { "$lt": ["$$this", new Date()] }
                     ] 
                 }
           } }
        ]
    } }
)

, который возвращает документ

{
    "_id" : "5be9860fcb16d525543cafe2",
    "name" : "Future",
    "host" : "5be9860fcb16d525543daff2",
    "sessions" : [ 
        {
            "date" : ISODate("2019-01-06T23:24:36.174Z")
        }, 
        {
            "date" : ISODate("2019-01-08T03:11:16.174Z")
        }
    ]
}
...