Наконец мне удалось сделать это с помощью всего запроса агрегации, но с 6 шагов, она не может быть наиболее оптимальным но он работает. А вот мой маленький монстр:
db.stuff.aggregate(
// Pipeline
[
// Stage 1
{
$project: {
"_id" : 1,
"units" : 1,
"price" : 1,
"name" : 1,
"dayyear" : [
{
"$dayOfYear" : "$datetime"
},
{
"$multiply" : [
{
"$add" : [
{
"$dayOfYear" : "$datetime"
},
1
]
},
-1
]
}
],
"datetime" : 1
}
},
// Stage 2
{
$unwind: "$dayyear"
},
// Stage 3
{
$project: {
"_id" : 1,
"difference" : {
"$multiply" : [
{
"$cond" : {
"if" : {
"$lt" : [
"$dayyear",
0
]
},
"then" : 1,
"else" : -1
}
},
"$units"
]
},
"units" : {
"$multiply" : [
{
"$cond" : {
"if" : {
"$lt" : [
"$dayyear",
0
]
},
"then" : 1,
"else" : -1
}
},
"$units"
]
},
"price" : {
"$multiply" : [
{
"$cond" : {
"if" : {
"$lt" : [
"$dayyear",
0
]
},
"then" : 1,
"else" : -1
}
},
"$price"
]
},
"price difference" : {
"$multiply" : [
{
"$cond" : {
"if" : {
"$lt" : [
"$dayyear",
0
]
},
"then" : -1,
"else" : 1
}
},
"$price"
]
},
"name" : 1,
"dayyear" : {
"$cond" : {
"if" : {
"$eq" : [
{
"$abs" : "$dayyear"
},
366
]
},
"then" : 1,
"else" : {
"$abs" : "$dayyear"
}
}
},
"year" : {
"$cond" : {
"if" : {
"$eq" : [
{
"$abs" : "$dayyear"
},
366
]
},
"then" : {
"$add" : [
{
"$year" : "$datetime"
},
1
]
},
"else" : {
"$year" : "$datetime"
}
}
},
"datetime" : 1
}
},
// Stage 4
{
$group: {
"_id" : {
"dayyear" : "$dayyear",
"year" : "$year",
"name" : "$name"
},
"difference" : {
"$push" : "$difference"
},
"price difference" : {
"$push" : "$price difference"
},
"things" : {
"$push" : "$$ROOT"
}
}
},
// Stage 5
{
$project: {
"_id" : {
"$arrayElemAt" : [
"$things._id",
0
]
},
"dayyear" : "$_id.dayyear",
"year" : "$_id.year",
"name" : "$_id.name",
"datetime" : "$things.datetime",
"difference" : {
"$cond" : {
"if" : {
"$eq" : [
{
"$size" : [
"$difference"
]
},
2
]
},
"then" : {
"$add" : [
{
"$arrayElemAt" : [
"$difference",
0
]
},
{
"$arrayElemAt" : [
"$difference",
1
]
}
]
},
"else" : 0
}
},
"price difference" : {
"$cond" : {
"if" : {
"$eq" : [
{
"$size" : [
"$price difference"
]
},
2
]
},
"then" : {
"$add" : [
{
"$arrayElemAt" : [
"$price difference",
0
]
},
{
"$arrayElemAt" : [
"$price difference",
1
]
}
]
},
"else" : 0
}
},
"units" : {
"$cond" : {
"if" : {
"$lt" : [
{
"$arrayElemAt" : [
"$things.units",
0
]
},
0
]
},
"then" : {
"$abs" : {
"$arrayElemAt" : [
"$things.units",
0
]
}
},
"else" : {
"$abs" : {
"$arrayElemAt" : [
"$things.units",
1
]
}
}
}
},
"price" : {
"$cond" : {
"if" : {
"$lt" : [
{
"$arrayElemAt" : [
"$things.price",
0
]
},
0
]
},
"then" : {
"$abs" : {
"$arrayElemAt" : [
"$things.price",
0
]
}
},
"else" : {
"$abs" : {
"$arrayElemAt" : [
"$things.price",
1
]
}
}
}
}
}
},
// Stage 6
{
$project: {
"_id" : 1,
"name" : 1,
"units" : 1,
"price" : 1,
"dayyear" : 1,
"year" : 1,
"datetime" : {
"$ifNull" : [
{
"$cond" : {
"if" : {
"$eq" : [
{
"$dayOfYear" : {
"$arrayElemAt" : [
"$datetime",
0
]
}
},
"$dayyear"
]
},
"then" : {
"$arrayElemAt" : [
"$datetime",
0
]
},
"else" : {
"$arrayElemAt" : [
"$datetime",
1
]
}
}
},
{
"$add" : [
{
"$arrayElemAt" : [
"$datetime",
0
]
},
86400000
]
}
]
},
"difference" : 1,
"total difference" : {
"$multiply" : [
"$price",
"$difference"
]
},
"price difference" : 1,
"inventory adjust for price change" : {
"$multiply" : [
"$price difference",
"$units"
]
},
"unitsprice" : {
"$multiply" : [
"$price",
"$units"
]
}
}
},
]
);
В основном то, что я сделал это:
Этап 1: Я изменил поле день на массив, являющийся первым элементом этого поля первоначальный день, и второй, (первоначальный день +1) , умноженный на -1. Причина, по которой я умножил это, - это чтобы можно было позже отличить «клоны» от оригиналов.
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(10),
"price" : NumberInt(100),
"dayyear" : [
NumberInt(363),
NumberInt(-364)
]
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(3),
"price" : NumberInt(50),
"dayyear" : [
NumberInt(363),
NumberInt(-364)
]
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(7),
"price" : NumberInt(100),
"dayyear" : [
NumberInt(362),
NumberInt(-363)
]
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(1),
"price" : NumberInt(50),
"dayyear" : [
NumberInt(362),
NumberInt(-363)
]
}
Этапа 2: Я unwinded используя переменный день, так что теперь каждый вход дублируется (обратите внимание на весь клон «помечены», как их дата в отрицательны).
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(10),
"price" : NumberInt(100),
"dayyear" : NumberInt(363)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(10),
"price" : NumberInt(100),
"dayyear" : NumberInt(-364)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(3),
"price" : NumberInt(50),
"dayyear" : NumberInt(363)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(3),
"price" : NumberInt(50),
"dayyear" : NumberInt(-364)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(7),
"price" : NumberInt(100),
"dayyear" : NumberInt(362)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(7),
"price" : NumberInt(100),
"dayyear" : NumberInt(-363)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(1),
"price" : NumberInt(50),
"dayyear" : NumberInt(362)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(1),
"price" : NumberInt(50),
"dayyear" : NumberInt(-363)
}
Этап 3: Здесь я совмещаю два действия. Сначала я умножаю на -1 единицы и цену в «оригиналах» (те, которые имели положительное число в «dayyear»), а затем умножают на -1 разницу в клонов (здесь вы также можете сделать противоположное - умножьте на -1 разницу в оригиналах, а не в клоны, это будет зависеть от того, как вы хотите, чтобы разница была вычислена), а затем также преобразовать все дневные даты в положительные (чтобы иметь возможность группировать это поле впоследствии). Также в случае инвентаря, который я сделал в последний день года, я установил условия, чтобы клон ссылался на 1-й год следующего года.
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(-10),
"price" : NumberInt(-100),
"dayyear" : NumberInt(363),
"difference" : NumberInt(-10),
"price difference" : NumberInt(100),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(10),
"price" : NumberInt(100),
"dayyear" : NumberInt(364),
"difference" : NumberInt(10),
"price difference" : NumberInt(-100),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(-3),
"price" : NumberInt(-50),
"dayyear" : NumberInt(363),
"difference" : NumberInt(-3),
"price difference" : NumberInt(50),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(3),
"price" : NumberInt(50),
"dayyear" : NumberInt(364),
"difference" : NumberInt(3),
"price difference" : NumberInt(-50),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(-7),
"price" : NumberInt(-100),
"dayyear" : NumberInt(362),
"difference" : NumberInt(-7),
"price difference" : NumberInt(100),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(7),
"price" : NumberInt(100),
"dayyear" : NumberInt(363),
"difference" : NumberInt(7),
"price difference" : NumberInt(-100),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(-1),
"price" : NumberInt(-50),
"dayyear" : NumberInt(362),
"difference" : NumberInt(-1),
"price difference" : NumberInt(50),
"year" : NumberInt(2015)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(1),
"price" : NumberInt(50),
"dayyear" : NumberInt(363),
"difference" : NumberInt(1),
"price difference" : NumberInt(-50),
"year" : NumberInt(2015)
}
Этап 4: Здесь я группа по названию, году и dayyear, так что теперь у меня есть массив , чтобы сделать разницу в цене, а другой на разницу единиц
{
"_id" : {
"dayyear" : NumberInt(362),
"year" : NumberInt(2015),
"name" : "Flying Skateboard"
},
"difference" : [
NumberInt(-1)
],
"price difference" : [
NumberInt(50)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(-1),
"price" : NumberInt(-50),
"dayyear" : NumberInt(362),
"difference" : NumberInt(-1),
"price difference" : NumberInt(50),
"year" : NumberInt(2015)
}
]
}
{
"_id" : {
"dayyear" : NumberInt(362),
"year" : NumberInt(2015),
"name" : "Laser Sable"
},
"difference" : [
NumberInt(-7)
],
"price difference" : [
NumberInt(100)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(-7),
"price" : NumberInt(-100),
"dayyear" : NumberInt(362),
"difference" : NumberInt(-7),
"price difference" : NumberInt(100),
"year" : NumberInt(2015)
}
]
}
{
"_id" : {
"dayyear" : NumberInt(363),
"year" : NumberInt(2015),
"name" : "Flying Skateboard"
},
"difference" : [
NumberInt(-3),
NumberInt(1)
],
"price difference" : [
NumberInt(50),
NumberInt(-50)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(-3),
"price" : NumberInt(-50),
"dayyear" : NumberInt(363),
"difference" : NumberInt(-3),
"price difference" : NumberInt(50),
"year" : NumberInt(2015)
},
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-28T13:28:50.912+0000"),
"units" : NumberInt(1),
"price" : NumberInt(50),
"dayyear" : NumberInt(363),
"difference" : NumberInt(1),
"price difference" : NumberInt(-50),
"year" : NumberInt(2015)
}
]
}
{
"_id" : {
"dayyear" : NumberInt(364),
"year" : NumberInt(2015),
"name" : "Laser Sable"
},
"difference" : [
NumberInt(10)
],
"price difference" : [
NumberInt(-100)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(10),
"price" : NumberInt(100),
"dayyear" : NumberInt(364),
"difference" : NumberInt(10),
"price difference" : NumberInt(-100),
"year" : NumberInt(2015)
}
]
}
{
"_id" : {
"dayyear" : NumberInt(364),
"year" : NumberInt(2015),
"name" : "Flying Skateboard"
},
"difference" : [
NumberInt(3)
],
"price difference" : [
NumberInt(-50)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"name" : "Flying Skateboard",
"datetime" : ISODate("2015-12-29T13:28:50.912+0000"),
"units" : NumberInt(3),
"price" : NumberInt(50),
"dayyear" : NumberInt(364),
"difference" : NumberInt(3),
"price difference" : NumberInt(-50),
"year" : NumberInt(2015)
}
]
}
{
"_id" : {
"dayyear" : NumberInt(363),
"year" : NumberInt(2015),
"name" : "Laser Sable"
},
"difference" : [
NumberInt(-10),
NumberInt(7)
],
"price difference" : [
NumberInt(100),
NumberInt(-100)
],
"things" : [
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-29T13:28:50.823+0000"),
"units" : NumberInt(-10),
"price" : NumberInt(-100),
"dayyear" : NumberInt(363),
"difference" : NumberInt(-10),
"price difference" : NumberInt(100),
"year" : NumberInt(2015)
},
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"name" : "Laser Sable",
"datetime" : ISODate("2015-12-28T13:28:50.823+0000"),
"units" : NumberInt(7),
"price" : NumberInt(100),
"dayyear" : NumberInt(363),
"difference" : NumberInt(7),
"price difference" : NumberInt(-100),
"year" : NumberInt(2015)
}
]
}
Этап 5: Я добавляю элементы в массив «разница», поэтому получаю единиц между деньгами в каждом элементе. Я также выбрать из других массивов, которые были созданы всегда негативные условия (как они были помечена как «оригиналы»), так что я могу иметь первоначальную цену и общих единиц каждый день для каждого пункта
{
"_id" : ObjectId("56828a929da4bc16eb534ea7"),
"difference" : NumberInt(0),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(362),
"year" : NumberInt(2015),
"name" : "Flying Skateboard",
"datetime" : [
ISODate("2015-12-28T13:28:50.912+0000")
],
"units" : NumberInt(1),
"price" : NumberInt(50)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea6"),
"difference" : NumberInt(0),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(362),
"year" : NumberInt(2015),
"name" : "Laser Sable",
"datetime" : [
ISODate("2015-12-28T13:28:50.823+0000")
],
"units" : NumberInt(7),
"price" : NumberInt(100)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"difference" : NumberInt(-2),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(363),
"year" : NumberInt(2015),
"name" : "Flying Skateboard",
"datetime" : [
ISODate("2015-12-29T13:28:50.912+0000"),
ISODate("2015-12-28T13:28:50.912+0000")
],
"units" : NumberInt(3),
"price" : NumberInt(50)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"difference" : NumberInt(-3),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(363),
"year" : NumberInt(2015),
"name" : "Laser Sable",
"datetime" : [
ISODate("2015-12-29T13:28:50.823+0000"),
ISODate("2015-12-28T13:28:50.823+0000")
],
"units" : NumberInt(10),
"price" : NumberInt(100)
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea5"),
"difference" : NumberInt(0),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(364),
"year" : NumberInt(2015),
"name" : "Flying Skateboard",
"datetime" : [
ISODate("2015-12-29T13:28:50.912+0000")
],
"units" : null,
"price" : null
}
{
"_id" : ObjectId("56828a929da4bc16eb534ea4"),
"difference" : NumberInt(0),
"price difference" : NumberInt(0),
"dayyear" : NumberInt(364),
"year" : NumberInt(2015),
"name" : "Laser Sable",
"datetime" : [
ISODate("2015-12-29T13:28:50.823+0000")
],
"units" : null,
"price" : null
}
Наконец, для того, чтобы сохранить правильную ISODate для каждого элемента, я проверить, какой элемент массива эквивалентно полю «dayyear». В случае, когда в массиве имеется только один элемент и он не равен «dayyear», он будет суммировать один день с элементом ISODate (обратите внимание, что «настоящая дата» всегда будет либо на один день, либо ниже.
После этого 6 шагов теперь готов быть сгруппированы по месяцам, год или имя. Обратите внимание, что всегда последние элементы будут оставшиеся «клоны», с датой завтра, но, как и все их поля 0 или null, они не влияют на меры агрегирования (как общие продажи и т. д.). Но вы должны заботиться, когда делаете средние значения, так как они будут влиять на них. Надеюсь, что это поможет!
Можете ли вы опубликовать свой ожидаемый результат? Если вы пытаетесь сделать то, что, как я думаю, вы пытаетесь сделать, это невозможно в одном запросе. –
Я только что отредактировал вопрос, чтобы дать ожидаемый результат. Я хочу, чтобы каждый день отслеживать разницу в запасах, поэтому я думал ежедневно вычислять ежедневную разницу, а затем делать подсчет «ежедневных различий». – Joe82