Пытаюсь создать агрегацию результатов опроса
у меня две коллекции
опрос - вот один документ
{
"_id": {
"$oid": "636027704f7a15587ef74f26"
},
"question": "question 1",
"ended": false,
"options": [
{
"id": "1",
"option": "option 1"
},
{
"id": "2",
"option": "option 2"
},
{
"id": "3",
"option": "option 3"
}
]
}
Голосовать - вот один документ
{
"_id": {
"$oid": "635ed3210acbf9fd14af8fd1"
},
"poll_id": "636027704f7a15587ef74f26",
"poll_option_id": "1",
"user_id": "1"
}
и я хочу выполнить агрегированный запрос, чтобы получить результаты опроса
поэтому я делаю следующий запрос
db.vote.aggregate(
[
{
$addFields: {
poll_id: { "$toObjectId": "$poll_id" }
},
},
{
$lookup: {
from: "poll",
localField: "poll_id",
foreignField: "_id",
as: "details"
}
},
{
$group:
{
_id: { poll_id: "$poll_id", poll_option_id: "$poll_option_id" },
details: { $first: "$details" },
count: { $sum: 1 }
}
},
{
$addFields: {
question: { $arrayElemAt: ["$details.question", 0] }
}
},
{
$addFields: {
options: { $arrayElemAt: ["$details.options", 0] }
}
},
{
$group: {
_id: "$_id.poll_id",
poll_id: { $first: "$_id.poll_id" },
question: { $first: "$question" },
options: { $first: "$options" },
optionsGrouped: {
$push: {
id: "$_id.poll_option_id",
count: "$count"
}
},
count: { $sum: "$count" }
}
}
]
)
Это дает мне эту форму результатов
{ _id: ObjectId("636027704f7a15587ef74f26"),
poll_id: ObjectId("636027704f7a15587ef74f26"),
question: 'question 1',
options:
[ { id: '1', option: 'option 1' },
{ id: '2', option: 'option 2' },
{ id: '3', option: 'option 3' } ],
optionsGrouped:
[ { id: '1', count: 2 },
{ id: '2', count: 1 } ],
count: 3 }
Итак, что меня интересует, я хочу, чтобы результаты выглядели так (например, объединение обоих параметров и группы параметров)
{ _id: ObjectId("636027704f7a15587ef74f26"),
poll_id: ObjectId("636027704f7a15587ef74f26"),
question: 'question 1',
optionsGrouped:
[ { id: '1', option: 'option 1', count: 2 },
{ id: '2', option: 'option 2', count: 1 },
{ id: '3', option: 'option 3', count: 0 } ],
count: 4 }
Другой вопрос, является ли структура БД приемлемой в целом, или я могу представить это лучше?
Один из вариантов — сначала сгруппировать, а затем использовать $lookup
, чтобы получить меньше данных из коллекции poll
. После $lookup
используйте $map
с $cond
для объединения массивов:
db.vote.aggregate([
{$group: {
_id: {poll_id: {$toObjectId: "$poll_id"}, poll_option_id: "$poll_option_id"},
count: {$sum: 1}
}},
{$group: {
_id: "$_id.poll_id",
counts: {
$push: {count: "$count", option: {$concat: ["option ", "$_id.poll_option_id"]}}
},
countAll: {$sum: "$count"}
}},
{$lookup: {
from: "poll",
localField: "_id",
foreignField: "_id",
as: "poll"
}},
{$project: {poll: {$first: "$poll"}, counts: 1, countAll: 1}},
{$project: {
optionsGrouped: {
$map: {
input: "$poll.options",
in: {$mergeObjects: [
"$$this",
{$cond: [
{$gte: [{$indexOfArray: ["$counts.option", "$$this.option"]}, 0]},
{$arrayElemAt: ["$counts", {$indexOfArray: ["$counts.option", "$$this.option"]}]},
{count: 0}
]}
]}
}
},
count: "$countAll",
question: "$poll.question"
}}
])
Посмотрите, как это работает на примере детской площадки
Я не уверен, что полностью понимаю ваш запрос, пожалуйста, предоставьте образцы документов и запрошенные результаты, и я рассмотрю их.
поэтому я делаю запрос от пользователя (приложение Frontend), который хочет увидеть все эти опросы, и мне нужно отметить вариант, который он выбрал для каждого опроса, если он уже сделал это, если вы можете увидеть там в документе для голосования, который у меня есть user_id, поэтому, учитывая входной идентификатор пользователя, есть ли способ сопоставить истинное значение в случае, если данный user_id == option.user_id, SO в конце будет выглядеть как ``` [ { "_id": ObjectId("636027704f7a15587ef74f26"), "count": 3, "optionsGrouped": [ { "count": 2 и т. д.. "checked": "true", } ], "question": "вопрос 1" } ]```
В этом случае используйте это
я внес некоторые изменения в запрос, и все хорошо, только один сложный случай, когда голосов нет, опрос вообще не возвращается, поскольку $lookup выполняет левое соединение, поэтому, скорее всего, мне нужно будет запросить коллекцию опросов, чтобы выполнить соединение затем поступит по той же логике; скорее всего с этого направления будет не просто, попробую и выложу сюда соль если получилось
я пытался инвертировать соединения, но проблема в том, что агрегация теперь находится на втором уровне после соединения, что меня немного сбивает с толку; у вас есть идеи, что нужно сделать, я просто хочу получить все опросы, даже если у них нет голосов, в настоящее время я получаю только те, у которых есть голоса, извините, чувак, исходящие из реляционной базы данных, поэтому термины и то, как выполняются запросы, здесь отличается и смущает меня
Я переработал запрос, чтобы он соответствовал моим желаниям
и этот запрос отвечает на вопрос, который я задал
db.poll.aggregate([
{
$addFields: {
_id: {
$toString: "$_id"
}
}
},
{
$lookup: {
from: "poll_vote",
localField: "_id",
foreignField: "poll_id",
as: "votes"
}
},
{
$replaceRoot: {
newRoot: {
$let: {
vars: {
count: {
$size: "$votes"
},
options: {
$map: {
input: "$options",
as: "option",
in: {
$mergeObjects: [
"$$option",
{
count: {
$size: {
$slice: [
{
$filter: {
input: "$votes",
as: "v",
cond: {
$and: [
{
$eq: [
"$$v.poll_option_id",
"$$option._id"
]
}
]
}
}
},
0,
100
]
}
}
},
{
checked: {
$toBool: {
$size: {
$slice: [
{
$filter: {
input: "$votes",
as: "v",
cond: {
$and: [
{
$eq: [
"$$v.user_id",
2
]
},
{
$eq: [
"$$v.poll_option_id",
"$$option._id"
]
}
]
}
}
},
0,
100
]
}
}
}
}
]
}
}
}
},
"in": {
_id: "$_id",
question: "$question",
count: "$$count",
ended: "$ended",
options: "$$options"
}
}
}
}
},
{
$addFields: {
answered: {
$reduce: {
input: "$options",
initialValue: false,
in: {
$cond: [
{
$eq: [
"$$this.checked",
true
]
},
true,
"$$value"
]
}
}
}
}
}
])
спасибо за помощь, выглядит очень хорошо для меня; у меня есть еще один вопрос, если я собираюсь передать пользователю этот запрос, чтобы добавить больше свойств, скажем, проверено, и мне нужно добавить это проверено внутри группы параметров с помощью true, если переданный пользователь является тем же пользователем из документа для голосования