фильтровать встроенный массив вложенных документов в мангусте

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

пример события:

{
  "_id" : ObjectId("5d57b6ad754e9d1ec0c123e6"),
  "title" : "wedding",
  "user" : ObjectId("5d4fedfd63dc55207cf128ff"),
  "date" : ISODate("2019-09-24T20:00:00.000Z"),
  "steps" : [
  {
    "_id" : ObjectId("5d57b6ad754e9d1ec0c123e7"),
    "title" : "home",
    "startDate" : ISODate("2019-06-27T20:00:00.000Z"),
    "endDate" : ISODate("2019-07-21T20:00:00.000Z"),
    "isDone" : false,
    "state" : 2
  },
  {
    "_id" : ObjectId("5d57b6ad754e9d1ec0c123e8"),
    "title" : "guests",
    "startDate" : ISODate("2019-07-07T20:00:00.000Z"),
    "endDate" : ISODate("2019-07-30T20:00:00.000Z"),
    "isDone" : false,
    "state" : 2
  },
  {
    "_id" : ObjectId("5d57b6ad754e9d1ec0c123e9"),
    "title" : "car",
    "startDate" : ISODate("2019-07-11T20:00:00.000Z"),
    "endDate" : ISODate("2019-07-20T20:00:00.000Z"),
    "isDone" : false,
    "state" : 2
  }
]
};

агрегация, которую я имею до сих пор:

const now = new Date();
Event.aggregate([
    {
      $match: { date: { $gte: now } },
    },
    {
      $unwind: '$steps',
    },
    {
      $match: { 'steps.startDate': { $lte: now }, 'steps.endDate': { $gte: now }, 'steps.state': { $ne: 1 } },
    },
  ])

Я искал документы, но ничего не нашел.


person Arootin Aghazaryan    schedule 01.09.2019    source источник
comment
вы хотите найти события, в которых некоторые шаги этих событий соответствуют условиям, или найти события и только шаги тех событий, которые соответствуют условиям... я имею в виду, вы можете просто найти события на основе условий шагов, а затем отфильтровать события этих событий вручную (по javascript Array.filter). так ли это?   -  person yaya    schedule 01.09.2019
comment
что-то вроде этого: stackoverflow.com/q/57745539/4718434 (сначала получить события, отвечающие критике, а затем отфильтровать их события опять же.)   -  person yaya    schedule 01.09.2019
comment
@yayapro находит события и только шаги тех событий, которые соответствуют условиям. проблема с производительностью, я просто не могу получить все события, а затем перебрать их все.   -  person Arootin Aghazaryan    schedule 01.09.2019
comment
вы ошибаетесь. я не говорил, что получить все события. я сказал сначала получить совпадающие события, а затем отфильтровать их шаги   -  person yaya    schedule 01.09.2019
comment
Мне нужно обновить состояние каждого шага в соответствии с датой начала и окончания. Я использую повестку дня для создания задания cron для обновления состояний в полночь. поэтому он в основном ищет каждое событие, дата которого еще не пройдена, и соответствующим образом обновляет состояния шага. это приводит к тому, что события, соответствующие условию, имеют большое количество.   -  person Arootin Aghazaryan    schedule 01.09.2019
comment
неееет, не только { date: { $gte: now } },, вы также можете применить условия для вложенных документов, например: Event.find({ $and: [ date: { $gte: now }, 'steps.startDate': { $lte: now }, 'steps.endDate': { $gte: now }, 'steps.state': { $ne: 1 } ]} (у меня может быть синтаксическая ошибка или что-то подобное, но вы можете сделать что-то подобное.)   -  person yaya    schedule 01.09.2019
comment
Я пробовал это, и он возвращает все событие со всеми включенными шагами. он не фильтрует массив шагов.   -  person Arootin Aghazaryan    schedule 01.09.2019
comment
затем используйте stackoverflow.com/a/12241930/4718434 для фильтрации массива шагов в событиях   -  person yaya    schedule 01.09.2019
comment
я только что проверил это в реальном примере проекта, мое последнее предложение не сработало (находит только 1 поддокумент, а не все). я только что отправил ответ, и он сработал, и я добавил полный образец проекта, в котором я тестировался. но я не уверен в производительности, возможно, есть лучший способ с лучшей производительностью.   -  person yaya    schedule 01.09.2019


Ответы (1)


Для обновления поддокументов на основе некоторых критических замечаний, как в основном документе, так и в поддокументе:

  var now = new Date()
  Events.find({
       date: { $gte: now }, 'steps.startDate': { $lte: now }, 'steps.endDate': { $gte: now }, 'steps.state': { $ne: 1 } 
    },
    (err, events) => {
        events.forEach(event => {
            var steps = event.steps.filter(step => (step.startDate < now && step.endDate > now && step.state != 1));
            steps.forEach(step => {step.state = 1;})
            event.save((err, event_)=>{console.log('event updated: ', event_)})
        })
    }
  )

Полный образец проекта, в котором я тестировал его (он не работает): https://codepen.io/ya3ya6/pen/xxKLNNG?editors=0010

(Помощь: вы должны запустить этот код 2 раза, в первый раз установите init в значение true, чтобы создать некоторые образцы данных. а во второй раз установите init в false, чтобы выполнить фактический запрос. затем вы можете проверить данные своей базы данных в mongodb, чтобы проверить результаты.)

person yaya    schedule 01.09.2019
comment
Спасибо за Ваш ответ. Это в значительной степени то, что я делаю сейчас, но с ростом базы данных это повлияет на производительность. Вот почему я пытаюсь, по крайней мере, сделать это, используя только запрос. - person Arootin Aghazaryan; 02.09.2019
comment
@ArootinAghazaryan без проблем. согласен, что это не с точки зрения производительности. обходным путем было бы удаление этих событий (по их идентификаторам и с использованием $in) в 1 запросе и вставка их снова с insertMany с 1 запросом. так что это будет всего 2 запроса. но на самом деле не знаю лучшего способа. - person yaya; 02.09.2019
comment
@ArootinAghazaryan также вы упомянули в своем вопросе so I can use the save() function on each, поэтому я предположил, что вы согласны с несколькими запросами (обратите внимание, что такие вещи, как events.forEach и event.steps.filter и steps.forEach, выполняются в ram, и они не являются запросами (в то время как запросы мангуста выполняются в файловой системе, которая много намного медленнее)) (предположим, что в вашей базе данных есть 10 математических событий, в этом ответе сначала есть 1 запрос, чтобы найти их, и 10 запросов, чтобы сохранить их, так что будет 11) - person yaya; 02.09.2019