Найти дату изменения значения для каждого инструмента - PullRequest
3 голосов
/ 18 мая 2019

У меня есть ряд заказов, как показано ниже в коллекции mongodb:

id date       value
A  1 Jan 18   1
A  2 Jan 18   0
A  3 Jan 18   0
B  14 Jan 18  4
B  15 Jan 18  5
B  16 Jan 18  0

Используя конвейер агрегации mongodb (mongo 3.4), я пытаюсь выяснить для каждого идентификатора, на какую дату это значениеизменяет на 0 с ненулевого значения, а также на «группу идентификаторов» для этих записей.

Второе обновление: 26 мая 19

Я обновил вопрос допрояснить, как выглядит первая, последняя реализация:

{
    "$addFields": {
      "date": {
        "$dateFromString": {
          "dateString": "$date"
        }
      }
    }
  },
  {
    $group: {
      _id: {
        "id": "$id",
        "value": "$value"
      },
      "first": {
        "$first": "$date"
      },
      "last": {
        "$last": "$date"
      }
    }
  },
  {
    "$match": {
      "_id.value": 0
    }
  }

https://mongoplayground.net/p/moBRI2Q7aGu

Это дает мне:

id value   first      last
A  0       2 Jan 18   3 Jan 18
B  0       16 Jan 18  16 Jan 18

Если я посмотрю на «первый«даты, это даты, когда значение вначале становится 0 из ненулевого значения.

Однако я хотел бы видеть всю« группу идентификаторов »тех значений, которые в какой-то момент становятся 0 из ненулевого значениявремя.Итак:

id value   first      last
A  1       1 Jan 18   1 Jan 18
A  0       2 Jan 18   3 Jan 18
B  4       14 Jan 18  14 Jan 18
B  5       15 Jan 18  15 Jan 18
B  0       16 Jan 18  16 Jan 18

Чтобы получить это, мне нужно получить доступ к групповому этапу перед совпадением вышеуказанного конвейера, поэтому https://mongoplayground.net/p/YTP-NBJtO4R, и как-то отфильтровать это с набором результатов из первого конвейера агрегации.Я делаю это в пандах через левое соединение с первым набором результатов, но это выглядит не элегантно.

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

Ответы [ 2 ]

2 голосов
/ 18 мая 2019

Вы можете использовать агрегацию ниже

db.collection.aggregate([
  { "$addFields": {
    "date": { "$dateFromString": { "dateString": "$date" }}
  }},
  { "$sort": { "date": 1 }},
  { "$match": { "value": 0 }},
  { "$group": {
    "_id": "$id",
    "date": { "$first": "$date" },
    "value": { "$first": "$value" }
  }}
])

MongoPlayground

С еще одним приемом агрегации

db.collection.aggregate([
  { "$match": { "value": "0" }},
  { "$addFields": {
    "date": { "$dateFromString": { "dateString": "$date" }}
  }},
  { "$sort": { "date": 1 }},
  { "$group": {
    "_id": "$id",
    "data": {
      "$push": {
        "value": "$value",
        "date": "$date"
      }
    }
  }},
  { "$project": {
    "data": {
      "$arrayElemAt": [
        { "$filter": {
          "input": "$data",
          "cond": { "$eq": ["$$this.value", "0"] }
        }},
        0
      ]
    }
  }},
  { "$replaceRoot": {
    "newRoot": { "$mergeObjects": [{ "id": "$_id" }, "$data"] }
  }}
])

Вы можете удалить первый этап $addFields, если ваши документы уже содержат дату в формате даты, а не так, как показано выше.

MongoPlayground

1 голос
/ 18 мая 2019

Начните с группировки по идентификатору, чтобы мы могли работать с каждым «заказом» по отдельности:

{
  $group: {
     _id: "$id",
     date_x_value: {$push: {date: "$date", value: "$value"}},
     sum: {$sum: "$value"}
  }
}

Теперь сопоставляйте только соответствующие документы:

{
  $match: {
      $and: [ {"date_x_value.value": 0}, {sum: {$gt: 0}}]
   }
}

Сортировка по дате:

{
  $sort: {
    "date_x_value.date": 1
  }
}

Теперь для фактического запроса:

{ $addFields:
    {
        matches: { 
            $reduce: {
               input: "$date_x_value", 
               initialValue: {"last_value": 0, "dates": []},
               in: { 
                   last_value: "$$this.value",
                   dates: { $concatArrays : [
                       {
                          $cond:{
                             if: {$and: [{$gt: ["$$value.last_value", 0]}, {$eq: ["$$this.value", 0]}]},
                             then: ["$$this.date"],
                             else: []
                          }
                      }, "$$value.dates"] 
                   }
               }
          }
       } 
    }
}

Это вернет массив дат, обратите внимание, что даты являются датами после 0. Значения для двух документов:

date: Jan 1,  value: 4
date: Jan 2,  value: 0

массив будет содержать 2 января.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...