#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" }
}
}
])