i'm trying to do a $lookup
in a collection and add some data to my documents. The problem is that when i try matching my $lookup
pipeline by _id
it returns an empty array. Here is my code:
Schedule.aggregate([{ // My Schedule schema
$match: {
store: req.query.store,
"customer.id": req.query.user
}
},
{
$skip: +req.query.skip
}, {
$limit: +req.query.limit
},
{
$lookup: {
from: Employee.collection.name, // "employee" schema,
let: {
id: "$employee.id" // employee _id from the "schedule" collection match above
},
pipeline: [{
$match: {
$expr: {
"_id": "$$id" // here i try to match by _id
}
}
},
{
$project: { // the only fields i need
"_id": 1,
"avatar": 1,
"name": 1
}
}
],
as: "employees" // employees is returned as []
}
}
]).exec((err, resolve) => {
if (err) console.log('error', err);
res.json(resolve);
});
If it helps here's both my collections used in this aggregation:
Schedule schema:
const ScheduleSchema = new Schema({
store: {
type: String,
required: true
},
customer: {
id: {
type: String
},
name: {
type: String
},
avatar: String,
phone: {
type: String
},
email: { type: String },
doc: {
type: String
},
},
employee: {
id: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
avatar: String,
},
service: {
id: {
type: String
},
name: {
type: String,
required: true
},
filters: [String]
},
info: {
channel: {
type: String,
required: true,
default: 'app'
},
id: mongoose.Schema.Types.ObjectId,
name: String
},
scheduleDate: {
type: String,
required: true
},
scheduleStart: {
type: String,
required: true
},
scheduleEnd: {
type: String,
required: true
},
value: {
type: Number
},
comissionType: {
type: String,
default: '$'
},
comissionValue: {
type: Number,
default: 0
},
status: {
type: Number,
required: true
},
observation: String,
paymentMethod: {
type: Number,
default: 0
},
paymentValue: String,
paymentChange: String,
color: String
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
Employee Schema:
const EmployeeSchema = new Schema({
name: {
type: String,
required: true
},
a_to_z: String, // nome normalizado, só minusculas e sem espaços
description: String,
email: {
type: String,
required: true
},
avatar: String,
phone: {
type: String
},
storeOwner: {
type: Boolean,
required: true
},
permissions: [
{
route: String,
hasPermission: Boolean
}
],
scheduleAutomatic: {
type: Boolean,
required: true,
default: false
},
password: {
passwordHash: String,
salt: String
},
active: {
type: Boolean,
default: true
},
storeKey: {
type: String,
required: true
},
notification_token: String,
notification_tokens: {
type: [String],
default: []
},
workingHours: [{
weekDay: {
type: Number,
},
doesWork: {
type: Boolean,
},
startHour: String,
endHour: String,
lunchStart: String,
lunchEnd: String
}],
config: {
available_days: {
type: Number,
default: 365
},
in_advance_schedule: {
type: Number,
default: 0
},
in_advance_interval: {
type: String,
default: 'minute'
}
}
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
});
The result i'm trying to achieve is this:
The employees
property is the one i'm trying to use $lookup
to get, it'll have the same data as the employee
property, in the end it'll be and array of objects with just one object inside.
Some sample docs:
Schedule:
color: "lavander",
created: "2018-07-31T18:50:53.423Z",
customer: {id: "5b60a67206e8a65f48a15f13", name: "Gabriel Barreto", phone: "11995274098", cpf: "40735255814"},
employee: {id: "5b2952c68424872fccece7f5", name: "Gabriel Barreto", avatar: null},
observation: "teste",
scheduleDate: "2018-08-01",
scheduleEnd: "2018-08-01 08:30",
scheduleStart: "2018-08-01 08:00",
service: {filters: Array(3), id: "5b606e8cc59e82354cc931e2", name: "Corte Masc"},
status: 1,
store: "5b16cceb56a44e2f6cd0324b",
updated: "2018-11-27T13:27:40.310Z",
value: 25,
__v: 0,
_id: "5b60af8de558661acc5d70b9"
Employee:
a_to_z: "gabrielbarreto",
active: true,
avatar: "gabriel_barreto_h7qvcn.jpg",
config: {available_days: 180, in_advance_schedule: 10, in_advance_interval: "hour"},
created: "2018-06-19T19:00:22.315Z",
currency: "BRL",
description: "Novo perfil",
email: "[email protected]",
lang: "pt-BR",
name: "Gabriel Barreto",
notification_token: "2d768670-6011-4873-846d-39580b0d82d0",
notification_tokens: ["53049a82-53dc-4bc3-9646-7a4bee1f367b"],
password: null,
permissions: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}],
phone: "11995274098",
scheduleAutomatic: false,
storeKey: "5b16cceb56a44e2f6cd0324b",
storeOwner: true,
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImdhYnJpZWwuYmFycmV0b0B3YWJpei5jb20uYnIiLCJpYXQiOjE1NTA2NzEwNDQsImV4cCI6MTU1MzI2MzA0NH0.0Odizd8pS4WPGSqm_2_XrTw1YE8NMOOXnHIrG-WVxGo",
updated: "2019-02-20T13:34:20.619Z",
workingHours: (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}],
__v: 0,
_id: "5b2952c68424872fccece7f5"
Thanks for your time
The MongoDB, aggregation pipeline is a framework for data aggregation modeled on the concept of data processing pipelines. Documents enter as an input into multi-stage pipeline which transforms the documents into an aggregated results. The MongoDB aggregation pipeline consists various stages.
The MongoDB $lookup aggregation stage The aggregation pipeline stage $lookup makes it possible to join data from an input collection (the collection you’re running the query on) and a lookup collection (the collection you want data from), as long as both collections are on the same database.
The MongoDB $group stages operator groups the documents by some specified expression and groups the document for each distinct grouping. An _id field in the output documents contains the distinct group by key. The output documents can also contain computed fields that hold the values of some accumulator expression grouped by the $group‘s _id field.
The MongoDB $setEquals operators compares between two or more arrays and returns true if they have the same distinct elements otherwise it returns false. $setIntersection. The MongoDB $setIntersection operators takes two or more arrays and returns a set of array with elements that appear in all of the input sets.
I was in trouble too while using $lookup with mongoose to trying to match _id as my collection store the reference as a String and not an ObjectId
Model A: {_id: ObjectId("xxx"), b_id: "eeeee"}
Model B: {_id: ObjectId("eeeee")}
MyCollectionA.aggregate([
{
$lookup: {
from: "collectionb",
let: {id: "$b_id"},
pipeline: [{$match: {$expr: {$eq: ["$_id", "$$id"]}}}],
as: b
}
])
In this example b is never filled as $$id is not considered as an ObjectId
Just add a project to transform $$id in an objectId and its working
MyCollectionA.aggregate([
{
$lookup: {
from: "collectionb",
let: {id: "$b_id"},
pipeline: [
{$project: {_id: 1, bid: {"$toObjectId": "$$id"}}},
{$match: {$expr: {$eq: ["$_id", "$bid"]}}}
],
as: b
}
])
Or with foreignField, localField:
MyCollectionA.aggregate([
{
$project:{
_id: 1,
b_id: {"$toObjectId": "$b_id"}
}
},
{
$lookup: {
from: "collectionb",
localField: "b_id",
foreignField: "_id",
as: b
}
])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With