#node.js #mongodb #mongoose #mongodb-query #aggregation-framework
#node.js #mongodb #мангуст #mongodb-запрос #агрегация-фреймворк
Вопрос:
У меня есть данные mongo в таком формате
[
{
_id:ObjectId("5f71890730a4421699b1fbff"),
timestamp: ISODate("2020-01-12T03:07:52Z"),
running_fig: "circle",
},
{
_id:ObjectId("5f718ac330a4421699b1fc15"),
timestamp: ISODate("2020-01-12T03:08:48Z"),
running_fig: "circle",
},
{
_id:ObjectId("5f718ac330a4421699b1fc16"),
timestamp: ISODate("2020-01-12T03:09:32Z"),
running_fig: "rombous",
},
{
_id:ObjectId("5f718ac330a4421699b1fc14"),
timestamp: ISODate("2020-01-12T03:10:11Z"),
running_fig: "triangle",
},
{
_id:ObjectId("5f718ac330a4421699b1fc13"),
timestamp: ISODate("2020-01-12T03:11:52Z"),
running_fig: "triange",
},
{
_id:ObjectId("5f718ac330a4421699b1fc12"),
timestamp: ISODate("2020-01-12T03:15:22Z"),
running_fig: "circle",
},
{
_id:ObjectId("5f718ac330a4421699b1fc1e"),
timestamp: ISODate("2020-01-12T03:20:52Z"),
running_fig: "circle",
},
]
** Теперь я хочу составить диаграмму событий в соответствии с текущим временем выполнения, я ожидал получить результат запроса в приведенной ниже форме, которую я дал**
[
{
running_fig:“circle”,
from: 2020-12-21T03: 07: 52Z,
to: 2020-12-21T03: 09: 48Z,
duration: 2 min.
},
{
running_fig:“rombous”,
from: 2020-12-21T03: 09: 48Z,
to: 2020-12-21T03: 10: 32Z,
duration: 1 min.
},
{
running_fig:“triangle”,
from: 2020-12-21T03: 10: 32Z,
to: 2020-12-21T03: 15: 22Z,
duration: 5 min.
},
{
running_fig:“circle”,
from: 2020-12-21T03: 15: 22Z,
to: 2020-12-21T03: 25: 52Z (current time),
duration: 10 min.
}
]
итак, я хочу, чтобы результирующие данные были в этом формате, чтобы я мог соответствующим образом создать диаграмму, в этих данных моя диаграмма
running_fig circle start_time — это собственные временные метки, а ее end_time — следующие временные метки
running_fig в моем случае следующий ромб, поэтому продолжительность running_fig circle равна (3:09 -3:07) = 2 минуты , и они объединение в единые данные показывает мой ожидаемый результат.
кто-нибудь, пожалуйста, помогите мне выполнить этот запрос, заранее спасибо
Комментарии:
1. Почему первый круг заканчивается на
2020-12-21T03:09:48Z
? Я не понимаю, откуда берется время. То же самое относится и к другим временам.2. @WernfriedDomscheit спасибо за ваше внимание, в этом случае данные продолжают поступать. когда значение running_fig изменяется в моем случае circle на Rombous, это означает, что при времени начала круга (3:07) и времени начала Rombous (3:09) между этой продолжительностью circle переходит в running_fig, поэтому его продолжительность составляет 3 минуты. то же самое относится ко всем.
3. Не ясно, что вы имеете в виду. Пожалуйста, предоставьте точные входные данные в соответствии с желаемым результатом.
4. @WernfriedDomscheit спасибо за ответ. на самом деле данные моего датчика регистрируются в MongoDB при изменении данных, у меня есть около 10 параметров для регистрации, когда любой параметр изменяет его, чтобы регистрировать все данные в моей базе данных, поэтому я получаю тот же 2020-12-21T03:09:48Z
5. Я все еще не понимаю твоей логики. Скорее всего, вам придется работать с $reduce . Конечно, он будет содержать выражение
{$last: "$$value."$timestamp"}
Ответ №1:
Как уже упоминалось, ваши выборочные данные не соответствуют ожидаемому результату, поэтому трудно понять логику. Но эта агрегация должна показать направление, в котором она может развиваться.
Версия с использованием $reduce
:
db.collection.aggregate([
{ $sort: { timestamp: -1 } },
// Transform documents to array
{ $group: { _id: null, data: { $push: "$ROOT" } } },
// combine timestamp with previous timestamp
{
$set: {
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: ["$value",
[{
running_fig: "$this.running_fig",
from: "$this.timestamp",
to: { $ifNull: [{ $last: "$value.from" }, "$NOW"] }
}]
]
}
}
}
}
},
{ $unwind: "$data" },
{ $sort: { "data.from": 1 } },
{ $group: { _id: null, data: { $push: "$ROOT.data" } } },
// find consequtive running_fig
{
$set: {
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: ["$value",
[{
$cond: {
if: { $ne: [{ $last: "$value.running_fig" }, "$this.running_fig"] },
then: "$this",
else: null
}
}]
]
}
}
}
}
},
// remove null values from array
{ $set: { data: { $filter: { input: "$data", cond: { $ne: ["$this", null] } } } } },
{ $unwind: "$data" }
{ $replaceRoot: { newRoot: "$data" } }
])
Версия с использованием $map
и $range
:
db.collection.aggregate([
{ $sort: { timestamp: 1 } },
{ $group: { _id: null, data: { $push: "$ROOT" } } },
{
$set: {
data: {
$map: {
input: { $range: [0, { $size: "$data" }] },
as: "idx",
in:
{
$cond: {
if: {
$ne: [
{ $arrayElemAt: ["$data.running_fig", "$idx"] },
{ $arrayElemAt: ["$data.running_fig", { $add: ["$idx", 1] }] }
]
},
then: {
running_fig: { $arrayElemAt: ["$data.running_fig", "$idx"] },
from: { $arrayElemAt: ["$data.timestamp", "$idx"] },
to: { $ifNull: [{ $arrayElemAt: ["$data.timestamp", { $add: ["$idx", 1] }] }, "$NOW"] }
},
else: null
}
}
}
}
}
},
{ $set: { data: { $filter: { input: "$data", cond: { $ne: ["$this", null] } } } } },
{ $unwind: "$data" },
{ $replaceRoot: { newRoot: "$data" } }
]);
Комментарии:
1. очень, очень спасибо, я ожидаю того же. еще один вопрос, который я должен задать, если у меня есть миллиарды данных, так какой метод более эффективен. @WernfriedDomscheit
2. Использование
$map
явно быстрее, см. jira.mongodb.org/browse/SERVER-53503
Ответ №2:
Это немного утомительное решение.
Объяснение
- Поместите данные в массив (при условии, что вы получаете данные от датчика 1)
- Сортировка по метке времени
- проверьте предварительный индекс в массиве, если предварительный индекс running_fig = текущий индекс running_fig, удалите эти данные из массива.
- Добавьте следующие индексные данные.
- если следующая временная метка индекса равна нулю, добавьте текущую временную метку
Код
db.collection.aggregate([
/** group by machine name*/
{
"$group": {
"_id": "$sensor",
docs: {
$push: "$ROOT"
}
},
},
/** sort by date to make event list*/
{
$sort: {
"docs.timestamp": -1
}
},
/** get pre data*/
{
$project: {
docs: {
/** transform the "docs" field*/
$map: {
/** into something*/
input: {
$range: [
0,
{
$size: "$docs"
}
]
},
/** an array from 0 to n - 1 where n is the number of documents*/
as: "this",
/** which shall be accessible using "$this"*/
in: {
$mergeObjects: [
/** we join two documents*/
{
$arrayElemAt: [
"$docs",
"$this"
]
},
/** one is the nth document in our "docs" array*/
{
"pre_index": {
$cond: [
{
"$gte": [
{
"$subtract": [
"$this",
1
]
},
0
]
},
{
"$arrayElemAt": [
"$docs",
{
"$subtract": [
"$this",
1
]
},
]
},
null
]
},
index: "$this"
}/** and the second document is the one with our "index" field*/
]
}
}
}
}
},
/**remove same state data*/
{
$project: {
_id: "$_id",
noDuplicateArray: {
$filter: {
input: "$docs",
as: "a",
cond: {
$ne: [
"$a.running_fig",
"$a.pre_index.running_fig"
]
}
}
}
}
},
/**add next data*/
{
$project: {
docs: {
/** transform the "docs" field*/
$map: {
/** into something*/
input: {
$range: [
0,
{
$size: "$noDuplicateArray"
}
]
},
/** an array from 0 to n - 1 where n is the number of documents*/
as: "this",
/** which shall be accessible using "$this"*/
in: {
$mergeObjects: [
/** we join two documents*/
{
$arrayElemAt: [
"$noDuplicateArray",
"$this"
]
},
/** one is the nth document in our "docs" array*/
{
"to": {
"$arrayElemAt": [
"$noDuplicateArray",
{
$add: [
"$this",
1
]
},
]
},
index: "$this"
}/** and the second document is the one with our "index" field*/
]
}
}
}
}
},
{
"$unwind": "$docs"
},
{
"$project": {
_id: "$docs._id",
sensor: "$_id",
from: "$docs.timestamp",
to: {
"$ifNull": [
"$docs.to.timestamp",
"$NOW"
]
},
running_fig: "$docs.running_fig",
duration: {
$concat: [
{
$toString: {
$round: [
{
$divide: [
{
$subtract: [
{
"$ifNull": [
"$docs.to.timestamp",
"$NOW"
]
},
"$docs.timestamp"
]
},
60000
]
},
1
]
}
},
" min"
]
}
}
}
])
Игровая площадка Монго : https://mongoplayground.net/p/CxHNdO6vLop
Комментарии:
1. Я думаю, что это
$sort
должно прийти раньше$group
— иdate
в любом случае поля нет.2. @WernfriedDomscheit каков параметр производительности, если миллион данных