Совокупность MongoDB не за полные часы

Прежде всего, я должен сказать, что у меня нет большого опыта работы с mongoDB и, вероятно, из-за этого я не могу решить следующую проблему. Проблема в том, что мне нужно агрегировать записи не по полным часам, а по часам, когда приходит первая запись и в часе от предыдущего события.

Пример:

Время Прилавок записей пока нет 0 11:55 1 11:58 1 12:02 1 12:05 1 12:55 1 12:56 2 13:04 2

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

ОБНОВЛЯТЬ

Образец данных:

    {
       "_id":{
          "$oid":"5f7eddd73b0e3e7259cf18b9"
       },
       "agentCodeSystem":"2.16.840.1.113883.2.4.3.118.2.1",
       "agentCode":"653259001653360",
       "agentName":"Diabetic",
       "date":"20201008",
       "organisationId":"97490137",
       "user":{
          "identifiers":[
             {
                "codeSystemName":"organisation/user",
                "oid":"2.16.840.1.113883.2.4.3.118.2.1",
                "code":"653259001653360",
                "displayName":""
             }
          ],
          "organisation":{
             "identifiers":[
                {
                   "codeSystemName":"my-system-name",
                   "oid":"2.16.840.1.113883.2.4.6.1",
                   "code":"97490137",
                   "displayName":""
                }
             ],
             "type":"Organisation type",
             "name":"My org name"
          },
          "overseer":{
             "identifiers":[
                {
                   "codeSystemName":"my code system name",
                   "oid":"2.16.840.1.113883.2.4.3.118.2.1",
                   "code":"653259001653360",
                   "displayName":""
                }
             ],
             "name":"Diabetic",
             "role":{
                "identifiers":[
                   {
                      "codeSystemName":"my code system name",
                      "oid":"2.16.840.1.113883.2.4.4.30.2",
                      "code":"99",
                      "displayName":"overig"
                   }
                ],
                "name":"Overig"
             }
          },
          "role":{
             "identifiers":[
                {
                   "codeSystemName":"my code system name",
                   "oid":"2.16.840.1.113883.2.4.4.30.2",
                   "code":"99",
                   "displayName":"overig"
                }
             ],
             "name":"Overig"
          },
          "name":"Diabetic"
       },
       "event":{
          "identifiers":[
             {
                "codeSystemName":"my code system name",
                "oid":"2.16.840.1.113883.2.4.3.118.2.13",
                "code":"uid",
                "displayName":""
             }
          ],
          "eventType":{
             "codeSystemName":"my code system name",
             "oid":"2.16.840.1.113883.2.4.3.118.2.1",
             "code":"5",
             "displayName":""
          },
          "actionCode":"R",
          "dateTime":"20200612165713",
          "date":"2019-12-13",
          "additionalText":"MyService"
       },
       "userSubjects":[
          {
             "identifiers":[
                {
                   "codeSystemName":"my code system name",
                   "oid":"2.16.840.1.113883.2.4.3.118.2.1",
                   "code":"653259001653360",
                   "displayName":""
                }
             ],
             "type":"",
             "name":"Diabetic",
             "gender":"Man",
             "birthDate":"2019-10-09",
             "organisationId":"97490137",
             "organisationName":"my org name"
          }
       ],
       "organisationSubjects":[
          {
             "identifiers":[
                {
                   "codeSystemName":"my code system name",
                   "oid":"2.16.840.1.113883.2.4.6.1",
                   "code":"123214453",
                   "displayName":""
                }
             ],
             "type":"org type",
             "name":"org name"
          }
       ]
    }

{
   "_id":{
      "$oid":"5f7eddd73b0e3e7259cf18b9"
   },
   "agentCodeSystem":"2.16.840.1.113883.2.4.3.118.2.1",
   "agentCode":"653259001653360",
   "agentName":"Diabetic",
   "date":"20201008",
   "organisationId":"97490137",
   "user":{
      "identifiers":[
         {
            "codeSystemName":"organisation/user",
            "oid":"2.16.840.1.113883.2.4.3.118.2.1",
            "code":"653259001653360",
            "displayName":""
         }
      ],
      "organisation":{
         "identifiers":[
            {
               "codeSystemName":"my-system-name",
               "oid":"2.16.840.1.113883.2.4.6.1",
               "code":"97490137",
               "displayName":""
            }
         ],
         "type":"Organisation type",
         "name":"My org name"
      },
      "overseer":{
         "identifiers":[
            {
               "codeSystemName":"my code system name",
               "oid":"2.16.840.1.113883.2.4.3.118.2.1",
               "code":"653259001653360",
               "displayName":""
            }
         ],
         "name":"Diabetic",
         "role":{
            "identifiers":[
               {
                  "codeSystemName":"my code system name",
                  "oid":"2.16.840.1.113883.2.4.4.30.2",
                  "code":"99",
                  "displayName":"overig"
               }
            ],
            "name":"Overig"
         }
      },
      "role":{
         "identifiers":[
            {
               "codeSystemName":"my code system name",
               "oid":"2.16.840.1.113883.2.4.4.30.2",
               "code":"99",
               "displayName":"overig"
            }
         ],
         "name":"Overig"
      },
      "name":"Diabetic"
   },
   "event":{
      "identifiers":[
         {
            "codeSystemName":"my code system name",
            "oid":"2.16.840.1.113883.2.4.3.118.2.13",
            "code":"uid",
            "displayName":""
         }
      ],
      "eventType":{
         "codeSystemName":"my code system name",
         "oid":"2.16.840.1.113883.2.4.3.118.2.1",
         "code":"5",
         "displayName":""
      },
      "actionCode":"R",
      "dateTime":"20200612155713",
      "date":"2019-12-13",
      "additionalText":"MyService"
   },
   "userSubjects":[
      {
         "identifiers":[
            {
               "codeSystemName":"my code system name",
               "oid":"2.16.840.1.113883.2.4.3.118.2.1",
               "code":"653259001653360",
               "displayName":""
            }
         ],
         "type":"",
         "name":"Diabetic",
         "gender":"Man",
         "birthDate":"2019-10-09",
         "organisationId":"97490137",
         "organisationName":"my org name"
      }
   ],
   "organisationSubjects":[
      {
         "identifiers":[
            {
               "codeSystemName":"my code system name",
               "oid":"2.16.840.1.113883.2.4.6.1",
               "code":"123214453",
               "displayName":""
            }
         ],
         "type":"org type",
         "name":"org name"
      }
   ]
}

Пожалуйста, предоставьте некоторые образцы данных. Что вы пробовали до сих пор?

Wernfried Domscheit 21.12.2020 11:31

Это возможно, но потребует огромного количества проекционных вычислений и этапов, и сложность действительно того не стоит, если вы не ограничены в производительности. Вам лучше делать это в памяти, учитывая, что набор не будет превышать ваши возможности. Если это так, вы должны просто пакетно разделить записи. В любом случае кешируйте вычисленные результаты. Кроме того, вы зависите от первой записи в зависимости от ее рекордного времени, верно?

zhulien 21.12.2020 12:05

@zhulien да, я завишу от первой записи, и мне нужна необходимая информация только для статистики, поэтому производительность не должна быть проблемой, поэтому все в порядке, если программа будет собирать статистику, например, в течение недели. Спасибо zhulien за ваши усилия.

exStas 21.12.2020 12:13

@exStas На самом деле я начал играть с примером решения для вас, но диапазоны групп (11:55–12:55, 12:56–13:56) должны рассчитываться для каждой отдельной даты, а также смешиваться между датами, поэтому это довольно усложняет код монго, поэтому я бы придерживался стратегии реализации вне БД ради вашего собственного здравомыслия.

zhulien 21.12.2020 12:21

@zhulien, если хотите, можете опубликовать ответ со своими рекомендациями, и я приму его.

exStas 21.12.2020 12:46

Как вы можете сгруппировать по любому часу, когда ваше поле date содержит только дату, но не информацию о времени? Пожалуйста, предоставьте действительные образцы данных, тогда я смогу вам помочь. Я думаю, вам нужно использовать этап $reduce.

Wernfried Domscheit 21.12.2020 14:05

@WernfriedDomscheit я обновил вопрос. Сейчас все в порядке или мне предоставить больше информации?

exStas 21.12.2020 14:19

Как можно сгруппировать один документ? date:2020-06-12 недействителен JSON

Wernfried Domscheit 21.12.2020 14:33

@WernfriedDomscheit это просто пример какой-то записи. У меня их количество. Я исправил это, так что теперь это действительный JSON

exStas 21.12.2020 14:37

И как вы группируете один документ? Предоставьте образец входных данных, который должен дать результат, который вы показываете в своем ответе. - Это последний раз, когда я прошу об этом.

Wernfried Domscheit 21.12.2020 14:42

@WernfriedDomscheit я предоставил реальные данные json, которые я храню. Я немного изменил имена и значения некоторых полей. Необходимо сгруппировать/уменьшить/и т. д. по «event.dateTime» в диапазоне «event.date» для определенного «organisationId». Вы можете заполнить больше документов в БД с заданными интервалами времени в примере таблицы.

exStas 21.12.2020 15:01

Не портите свои посты. Размещая на этом сайте, вы безоговорочно предоставляете сети Stack Exchange право распространять этот контент по лицензии CC BY-SA 4.0 до тех пор, пока она считает это целесообразным. Альтернативы удалению см.: Я лучше подумал над своим вопросом; могу ли я удалить его?

Sabito stands with Ukraine 30.12.2020 11:44
Использование JavaScript и MongoDB
Использование JavaScript и MongoDB
Сегодня я собираюсь вкратце рассказать о прототипах в JavaScript, а также представить и объяснить вам работу с базой данных MongoDB.
0
12
130
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Поскольку это непростая задача в MongoDB, вам лучше сделать это в вашей бизнес-логике. Результат, которого вы хотите достичь, в основном представляет собой форму агрегации по диапазонам. Дело в том, что ваши диапазоны не являются предопределенным набором, а являются динамическими и рассчитываются на основе первой записи + дополнительного значения времени. Итак, в основном у вас есть: [firstTime -> firstTime + 60m], [firstTime + 60m + 1s -> firstTime + 60m + 60m], ... до бесконечности. Это выполнимо, но требует либо хакерских динамических выражений для этапа группировки, либо проекционных расчетов. Оба эти подхода кажутся ненужными сложными для вашего варианта использования.

Вы можете проверить некоторые связанные сценарии здесь и здесь, если вы действительно хотите пойти по этому пути.

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

Ответ принят как подходящий

Ваши образцы данных по-прежнему бесполезны! Удалите все не относящиеся к делу вещи. И дайте нам больше, чем просто 2 документа, чтобы охватить все требования.

В любом случае, я буду стараться изо всех сил.

Это очень плохой дизайн для хранения значений даты/времени в виде строки (или числа). Всегда используйте подходящие Date объекты.

db.collection.aggregate([
   // convert string into proper `Date` object 
   { $set: { ts: { $dateFromString: { dateString: "event.dateTime", format: "%Y%m%d%H:%M:%S" } } } }
   { $sort: { ts: 1 } },
   // put documents into an array
   { $group: { _id: { date: "$event.date", organisationId: "$organisationId" }, data: { $push: "$$ROOT" } } },
   {
      $set: {
         data: {
            $reduce: {
               input: "$data",
               initialValue: [],
               in: {
                  $concatArrays: [
                     "$$value",
                     [
                        {
                           $cond: {
                              // compare timestamp with boundary of previous element  
                              if: { $gt: ["$$this.ts", { $last: "$$value.boundary" }] },
                              // new interval: increase bucket number and set new boudnary
                              then: {
                                 $mergeObjects: [
                                    "$$this", {
                                       bucket: { $add: [{ $ifNull: [{ $last: "$$value.bucket" }, 0] }, 1] },
                                       boundary: { $add: ["$$this.ts", 1000 * 60 * 60] }
                                    }
                                 ]
                              },
                              // same interval: append element   
                              else: { $mergeObjects: ["$$this", { boundary: { $last: "$$value.boundary" }, bucket: { $last: "$$value.bucket" } }] }
                           }
                        }
                     ]
                  ]
               }
            }
         }
      }
   },       
   { $unwind: "$data" }, // transpose array back to documents
   // count the documents by bucket number
   {
      $group: {
         _id: { date: "$data.date", organisationId: "$data.organisationId", bucket: "$data.bucket" },
         ts_min: { $min: "$data.ts" },
         ts_max: { $max: "$data.ts" },
         count: { $sum: 1 }
      }
   },
   // some cosmetic
   { $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$_id"] } } },
   { $unset: "_id" }
])

Благодарю вас за ваше усилие! Я только что проверил запрос и подтверждаю, что он работает нормально! Чего я не совсем понимаю, так это зачем нам здесь нужна операция $concatArrays? Что именно он здесь делает? Заранее спасибо!

exStas 23.12.2020 13:54

Мне нужно создать массив, в котором я могу сравнить текущую метку времени с предыдущими. Без $concatArrays поле data будет иметь только последнее значение (для 13:04), больше ничего

Wernfried Domscheit 23.12.2020 14:00

Хм, а почему поле data будет пустым, если мы будем помещать документы в это поле в операции $group?

exStas 23.12.2020 14:08

Другие вопросы по теме