Как отфильтровать подмассив и вернуть документ в монго
У меня две коллекции Employee
, Client
Employee
схема имеет следующую запись
{
"_id": ObjectId("5a852dcd0290f7eca89e9a79"),
"FirstName": "Nirav",
"LastName": "Modi",
"Gender": true,
"Forms": [{
"ClientId": ObjectId("5a8528ed0290f7eca89e9a5f"),
"ProjectId": ObjectId("5a856fde0290f7eca89e9a88"),
"FormId": ObjectId("5a62e561f6647f17f85e54c5")
}]
}
Client
схема имеет следующую запись
{
"_id" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"CompanyName" : "PNB",
"Projects" : [{
"_id" : ObjectId("5a856ca70290f7eca89e9a7f"),
"Name" : "House Loan",
"Description" : "Get house load",
"Forms" : []
}, {
"_id" : ObjectId("5a856fde0290f7eca89e9a88"),
"Name" : "Car Loan",
"Description" : "get car loan",
"Forms" : [
ObjectId("5a62e82299d9fe0a14a1ead5"),
ObjectId("5a6eec263bf43426d4d31780"),
ObjectId("5a62e561f6647f17f85e54c5")
]
}
]
}
В моей системе сотрудникам назначены формы для работы. как вы видите, схема сотрудника содержит Forms
поле, которое содержит ClientId
, ProjectId
, FormId
,
Форма может быть в нескольких клиентах для нескольких проектов.
Когда я получаю данные, я хочу, чтобы вывод был таким, как показано ниже
{
"_id": ObjectId("5a852dcd0290f7eca89e9a79"),
"FirstName": "Aartik",
"LastName": "Ladumor",
"Gender": true,
"Clients": [{
"_id": ObjectId("5a8528ed0290f7eca89e9a5f"),
"CompanyName": "PNB",
"Projects": [{
"_id": ObjectId("5a856fde0290f7eca89e9a88"),
"Name": "Car Loan",
"Description": "get car loan",
"Forms": [
ObjectId("5a62e82299d9fe0a14a1ead5"),
ObjectId("5a6eec263bf43426d4d31780"),
ObjectId("5a62e561f6647f17f85e54c5")
]
}
]
}
]
}
Получать только тех клиентов, в которых проект содержит формы, назначенные сотруднику.
Для этого я делаю агрегацию, как показано ниже
db.Employees.aggregate([{
$lookup: {
from: "Clients",
localField: "Forms.ClientId",
foreignField: "_id",
as: "Clients"
}
}, {
filter projects array in matched client contains only
projects that match Employee.Forms each elements
ProjectsId -> Client.Projects._id and
FormId -> in Client.Projects.Forms array containing forms ObjectId
}
]).pretty()
2 ответа
Вы можете попробовать ниже агрегации
db.employees.aggregate([
{ $unwind: "$Forms" },
{
$lookup: {
from: "clients",
localField: "Forms.ClientId",
foreignField: "_id",
as: "Clients"
}
},
{ $unwind: "$Clients" },
{ $unwind: "$Clients.Projects" },
{
$redact: {
$cond: {
if: { $eq: [ "$Forms.ProjectId", "$Clients.Projects._id" ] },
then: "$$KEEP",
else: "$$PRUNE"
}
}
},
{
$group: {
_id: {
_id: "$_id",
ClientId: "$Clients._id"
},
FirstName: { $first: "$FirstName" },
LastName: { $first: "$LastName" },
Gender: { $first: "$Gender" },
Client: { $first: "$Clients" },
Projects: { $push: "$Clients.Projects" }
}
},
{
$group: {
_id: "$_id._id",
FirstName: { $first: "$FirstName" },
LastName: { $first: "$LastName" },
Gender: { $first: "$Gender" },
Clients: { $push: {
_id: "$Client._id",
CompanyName: "$Client.CompanyName",
Projects: "$Projects"
} }
}
}
])
Обычно вам нужно использовать $ unwind несколько раз, потому что вам нужно сравнивать значения, а не массивы. Таким образом, $lookup объединяет две коллекции. Затем вам нужно сравнить идентификаторы проекта, чтобы вам снова пришлось $ unwind. Чтобы отфильтровать проекты, которые не принадлежат сотруднику, вы можете использовать $ redact для сравнения двух полей. Затем, чтобы получить два уровня вложенных массивов, вы должны использовать $ group.
Попробуйте эту агрегацию, мы $lookup
по идентификатору клиента из коллекции сотрудников, затем $filter
по проекту и идентификатору формы из соответствующих клиентов
db.Employee.aggregate(
[
{$lookup : {
from : "Client",
localField : "Forms.ClientId",
foreignField : "_id",
as : "Clients"
}},
{$addFields : {
"Clients.Projects" : {
$filter : {
input : {$arrayElemAt : ["$Clients.Projects", 0]},
as : "project",
cond : {$and : [
{$eq : [{$arrayElemAt : ["$Forms.ProjectId", 0]}, "$$project._id"]},
{$in : [{$arrayElemAt : ["$Forms.FormId", 0]}, "$$project.Forms"]}
]}}
}
}}
]
).pretty()