У меня есть документ, который выглядит так
{
"_id": {
"$oid": "6187e5fa4ebcc2db6c86081f"
},
"publicId": "S23DCL",
"flights": [
{
"name": "f1",
"_id": {
"$oid": "620026293fc350024da614dd"
},
"wines": [
{
"id": "1",
"name": "wine1"
},
{
"id": "2",
"name": "wine2"
}
]
},
{
"name": "f2",
"_id": {
"$oid": "6200263c3fc350024da614de"
},
"wines": [
{
"id": "3",
"name": "leVin"
},
{
"id": "4",
"name": "theWine"
}
]
}
],
"title": "Pinot Noir 2022",
"scores": [
{
"userId": "f8Dv",
"userName": "gugus",
"scores": [
{
"score": 95,
"wineId": "1"
},
{
"score": 88,
"wineId": "2"
}
]
},
{
"userId": "yLjh",
"userName": "test",
"scores": [
{
"score": 92,
"wineId": "1"
},
{
"score": 87,
"wineId": "2"
}
]
},
{
"userId": "B6em",
"userName": "jklsdf",
"scores": [
{
"score": 88,
"wineId": "1"
},
{
"score": 90,
"wineId": "2"
},
{
"score": 92,
"wineId": "3"
},
{
"score": 86,
"wineId": "4"
}
],
"isFinished": false
}
],
}
Если вопрос слишком длинный, я могу его сократить.
Весь документ и запрос также могут быть найдено на монгоплейграунд
Что я хочу сделать, так это создать табло для всех вин, то есть вычислить среднее значение и т. д. Для каждого вина, имея при этом только один документ.
Что-то вроде этого
{
"title": "Pinot Noir 2022"
"results": [
{
"wine": {
"flightName": "f1",
"wineIndex": 1,
"name": "wine1",
"wineId": "1"
},
"avg": 90,
"scores": [
{
"scores": {
"score": 87,
"wineId": "1"
},
"userId": "yLjh",
"userName": "test"
},
]
}
]
}
Я почти закончил, но борюсь с той частью, когда мне нужен единый результирующий документ после того, как я его сгруппировал.
идентификатор вина в баллах соответствует идентификатору в flights.wines
Вот что у меня есть до сих пор
db.collection.aggregate([
{
"$match": {
"publicId": "S23DCL"
}
},
{
"$project": {
"scores": "$scores",
"publicId": "$publicId"
}
},
{
"$unwind": "$scores"
},
{
"$unwind": "$scores.scores"
},
{
$lookup: {
from: "collection",
let: {
wId: "$scores.scores.wineId",
"tastingId": "$_id"
},
pipeline: [
{
"$unwind": "$flights"
},
{
"$unwind": {
"path": "$flights.wines",
"includeArrayIndex": "index"
}
},
{
$match: {
$expr: {
"$and": [
{
"$eq": [
"$flights.wines.id",
"$$wId"
]
},
{
"$eq": [
"$_id",
"$$tastingId"
]
}
]
},
}
},
{
$project: {
_id: 0,
"name": "$flights.wines.name",
"flight": "$flights.name",
"wineId": "$flights.wines.id",
"index": "$index"
}
}
],
as: "wine"
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
{
"_id": "$_id",
"scores": "$scores"
},
{
"wine": {
"$arrayElemAt": [
"$wine",
0
]
}
}
]
}
}
},
{
"$group": {
"_id": "$wine",
"avg": {
"$avg": "$scores.scores.score"
},
"min": {
"$min": "$scores.scores.score"
},
"max": {
"$max": "$scores.scores.score"
},
"stddev": {
"$stdDevPop": "$scores.scores.score"
},
"tmp": {
"$max": 1
},
"scores": {
"$addToSet": "$scores"
}
}
}
])
Это дает мне данные, как показано на results
выше. Но такие данные, как «название», отсутствуют.
Обратите внимание на поле tmp
. Я попытался добавить это фиктивное поле и сгруппировать его на более позднем этапе.
Так как всегда будет только одна группа (т.к. tmp статическая).
Нравиться
// query from abive
,{
"$group": {
"$_id": "tmp"
// add my grouped results from before into an array
}
}
Итак, как я могу добавить свои сгруппированные результаты в массив, чтобы я не возвращал (n) групп, а только один документ, в котором сгруппированные результаты находятся в массиве?
Надеюсь понятно о чем я :)
Может быть, что-то вроде этого:
{
$group: {
_id: "tmp",
array: {
$push: "$$ROOT"
}
}
}
когда вы заменяете корень с помощью replaceRoot, ваш заголовок пропадает. сначала вы должны добавить его на новый корневой уровень. это возвращает ваш титул.
[
{
'$match': {
'publicId': 'S23DCL'
}
}, {
'$project': {
'scores': '$scores',
'publicId': '$publicId',
'title': '$title',
'flights': '$flights'
}
}, {
'$unwind': '$scores'
}, {
'$unwind': '$scores.scores'
}, {
'$lookup': {
'from': 'collection',
'let': {
'wId': '$scores.scores.wineId',
'tastingId': '$_id'
},
'pipeline': [
{
'$unwind': '$flights'
}, {
'$unwind': {
'path': '$flights.wines',
'includeArrayIndex': 'index'
}
}, {
'$match': {
'$expr': {
'$and': [
{
'$eq': [
'$flights.wines.id', '$$wId'
]
}, {
'$eq': [
'$_id', '$$tastingId'
]
}
]
}
}
}, {
'$project': {
'_id': 0,
'name': '$flights.wines.name',
'flight': '$flights.name',
'wineId': '$flights.wines.id',
'index': '$index'
}
}
],
'as': 'wine'
}
}, {
'$addFields': {
'scores.title': '$title'
}
}, {
'$replaceRoot': {
'newRoot': {
'$mergeObjects': [
{
'_id': '$_id',
'scores': '$scores'
}, {
'wine': {
'$arrayElemAt': [
'$wine', 0
]
}
}
]
}
}
}, {
'$group': {
'_id': '$wine',
'title': {
'$last': '$scores.title'
},
'avg': {
'$avg': '$scores.scores.score'
},
'min': {
'$min': '$scores.scores.score'
},
'max': {
'$max': '$scores.scores.score'
},
'stddev': {
'$stdDevPop': '$scores.scores.score'
},
'tmp': {
'$max': 1
},
'scores': {
'$addToSet': '$scores'
}
}
}
]
но ваш текущий конвейер агрегации имеет слишком много этапов. если вы объясните, что именно вам нужно из вашего источника данных, возможно, его можно немного оптимизировать
Если есть (n) рейсов, которые сами имеют (n) вин. Пользователь может добавлять баллы к каждому вину. Они сохраняются в «баллах». Я хочу отобразить рейтинг/список (средний и т. д.) всех вин, чтобы я мог отобразить их в таблице. В основном мне нужно сгладить всю мою структуру, чтобы у меня был список вин (но в виде массива в новом документе)
«$$ROOT» — это то, чего мне не хватало.