MongoDB найдите последний максимум

#mongodb #pymongo

Вопрос:

Я хочу получить самую близкую цену, которая выше последней цены. Пример (MongoDB) показан ниже.

Последняя дата — «2021/01/05» в примере.

Для «id 0001» ближайшая цена, которая выше последней цены, составляет 30, а последняя цена-20. Для «id 0002» никакая цена не выше последней цены, поэтому ближайшая цена-это последняя цена(140).

db.stock.найти()

//результат

 [

    {'id':'0001','date':'2021/01/01','price':50}
    {'id':'0001','date':'2021/01/02','price':40}
    {'id':'0001','date':'2021/01/03','price':30}
    {'id':'0001','date':'2021/01/04','price':10}
    {'id':'0001','date':'2021/01/05','price':20}
    {'id':'0002','date':'2021/01/01','price':100}
    {'id':'0002','date':'2021/01/02','price':110}
    {'id':'0002','date':'2021/01/03','price':120}
    {'id':'0002','date':'2021/01/04','price':130}
    {'id':'0002','date':'2021/01/05','price':140}
    ...
]
 

Ожидаемый результат показан ниже.

ближайшая цена на 20 выше 10, но она меньше 30(ближайшая цена выше одной цены), поэтому » дни «равны 1 в случае «0001».

ближайшая цена на 140 выше, чем 130,120,110,100 (ближайшая цена выше, чем четыре цены), поэтому » дни «равны 4 в случае «0002».

  [   
        {'id':'0001', 'latest_date':'2021/01/05',
'the_closest_date':'2021/01/03',
'the_closest_price':30, 
'days':1},
        {'id':'0002', 'latest_date':'2021/01/05',
'the_closest_date':'2021/01/05',
'the_closest_price':140, 
'days':4}
    ]
 

Комментарии:

1. Ваше состояние не соответствует действительности. 140 не выше последней цены 140 (она равна). Если ваше условие должно быть «ближайшая цена, которая выше или равна последней цене», то для id: "0001" результата также будет 20 (не 30).

2. Цена должна быть цифрами, а не строками. В противном случае цена "9" была бы выше, чем "100"

Ответ №1:

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

Запустите код здесь

 db.collection.aggregate([
  {
    "$sort": {
      "date": -1
    }
  },
  {
    "$group": {
      "_id": "$id",
      "g": {
        "$push": {
          "price": "$price",
          "date": "$date"
        }
      },
      "latest-date": {
        "$max": "$date"
      },
      "price": {
        "$first": "$price"
      }
    }
  },
  {
    "$addFields": {
      "days-date-found": {
        "$reduce": {
          "input": "$g",
          "initialValue": [
            -1,
            -1,
            false
          ],
          "in": {
            "$let": {
              "vars": {
                "days_date_found": "$value",
                "m": "$this"
              },
              "in": {
                "$let": {
                  "vars": {
                    "days": {
                      "$arrayElemAt": [
                        "$days_date_found",
                        0
                      ]
                    },
                    "date": {
                      "$arrayElemAt": [
                        "$days_date_found",
                        1
                      ]
                    },
                    "found": {
                      "$arrayElemAt": [
                        "$days_date_found",
                        2
                      ]
                    }
                  },
                  "in": {
                    "$switch": {
                      "branches": [
                        {
                          "case": "$found",
                          "then": "$days_date_found"
                        },
                        {
                          "case": {
                            "$gt": [
                              "$m.price",
                              "$price"
                            ]
                          },
                          "then": [
                            "$days",
                            "$m.date",
                            true
                          ]
                        }
                      ],
                      "default": [
                        {
                          "$add": [
                            "$days",
                            1
                          ]
                        },
                        "$m.date",
                        false
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  {
    "$addFields": {
      "days": {
        "$arrayElemAt": [
          "$days-date-found",
          0
        ]
      },
      "closest-date": {
        "$cond": [
          {
            "$arrayElemAt": [
              "$days-date-found",
              2
            ]
          },
          {
            "$arrayElemAt": [
              "$days-date-found",
              1
            ]
          },
          "$latest-date"
        ]
      }
    }
  },
  {
    "$unset": [
      "days-date-found",
      "g"
    ]
  }
])
 

Если это не так, если вы можете предоставить дополнительные данные и как они должны быть после запроса.

Ответ №2:

Как упоминалось в моем отзыве, ваше требование не является последовательным. Также вы не объясняете, как определить поле days .

Это мое решение:

 db.collection.aggregate([
  { $sort: { date: 1 } },
  {
    $group: {
      _id: "$id",
      data: { $push: "$ROOT" },
      latest_price: { $last: "$price" },
      latest_date: { $last: "$date" }
    }
  },
  {   // Remove this stage if the latest price/date can be the result. However, then it will be **always** the latest one
    $set: { 
      data: {
        $slice: [ "$data",  0, { $subtract: [ { $size: "$data" }, 1 ] } ]
      }
    }
  },  
  {  // Filter days where price > latest price
    $set: {
      data: {
        $filter: {
          input: "$data",
          cond: { $gt: [ "$this.price", "$latest_price" ] }
        }
      }
    }
  },
  { // Calculate difference between price and latest price
    $set: {
      data: {
        $map: {
          input: "$data",
          in: {
            $mergeObjects: [
              "$this",
              {
                diff: {
                  $subtract: [
                    "$this.price",
                    "$latest_price"
                  ]
                }
              }
            ]
          }
        }
      }
    }
  },
  { // Filter by minimum difference (note, there might be more than just one)
    $set: {
      data: {
        $filter: {
          input: "$data",
          cond: { $eq: [ "$this.diff", { $min: "$data.diff" } ] }
        }
      }
    }
  },
  { // Compose final output 
    $project: {
      latest_date: 1,
      the_closest_date: { $first: "$data.date" },
      the_closest_price: { $first: "$data.price" }
    }
  }
])
 

Игровая площадка Монго