How to combine $lookup in MongoDB - mongodb

I got 3 collections that's look like this:
Type :
{
uuid : "uuid_type_1",
info: "info sur le type 1"
}
Piece :
{
uuid : "uuid_piece_1",
type_uuid : "uuid_type_1",
info: "info sur la piece 1"
}
Photo :
{
uuid : "uuid_photo_1",
piece_uuid : "uuid_piece_1",
info: "info photo 1"
}
And I want to join all this 3 collections.
For 2, I succeed with lookup, for 3 I know I have to use pipeline and I try this but without successful.
db("Essais").collection("TYPE").aggregate([
{
$match:recherche
},
{
$lookup:{
from: "PIECE",
localField: "uuid",
foreignField : "type_uuid",
pipeline:[{
$lookup:{
from: "PHOTO",
localField: "uuid",
foreignField : "piece_uuid",
as: "PHOTOSPIECE"
}
}],
as: "PIECES"
}
}
)];

The following solution is working :
$lookup:
{
from: "PIECE",
let: { "uuidtype" : "$uuid" },
pipeline:[
{ $match: { "$expr": { "$eq": [ "$type_uuid", "$$type_uuid" ] } } },
{$lookup:
{
from: "PHOTO",
as: "PHOTOSPIECE",
let: { "uuidpiece" : "$uuid" },
pipeline:[
{ $match: { "$expr": { "$eq": [ "$piece_uuid", "$$uuidpiece" ] } }},
]
}
}
],
as: "PIECES"
}

Related

Mongodb $lookup when localField is string and foreignField is in ObjectId formats

Trying to do mongodb aggregate $lookup query for following collections :
Shop collection :
{
"_id" : ObjectId("5b618a57759612021aaa2ed"),
"no" : "23456",
"date" : ISODate("2012-01-04T16:00:00.000+0000"),
"clientId" : "5b55cc5c05546200217ae0f3"
}
Client collection :
{
"_id" : ObjectId("5b55cc5c05546200217ae0f3"),
"title" : "Ms",
"name" : "Jane Marie"
}
the query :
db.getCollection("shop").aggregate([
{ $lookup:
{
from: "client",
localField: "clientId",
foreignField: "_id",
as: "client"
}
}
])
above query ends up giving an empty patient array :
{
"_id" : ObjectId("5b618a57759672021aaa2ed"),
"no" : "20190000024274",
"date" : ISODate("2012-01-04T16:00:00.000+0000"),
"clientId" : "5b55cc5c05546200217ae0f3",
"client" : []
}
Edit :
and when trying to lookup using an array of ids as local Field :
transaconsIds: ["5b61d4320550de077143b763", "5b61d4324450de002143b777"]
by using :
{
$lookup:
{
from: "transcation",
let: { vid: "transaconsIds" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", { $toObjectId: "$$vid" }]
}
}
}
],
as: "transactions"
}
}
this leads to an Mongo Server error.
Edit 02 :
when trying to lookup for localField which is a nested as follows :
"transactions" : [
{
"bill" : {
"soldItemIds" : [
"5b55aabf0550770021097ed2"
]
}
}
]
by using :
{ $unwind : "$transactions"},
{
$lookup:
{
from: "bill",
let: { did: "$transactions.bill.soldItemIds" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$did",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "bills"
}
}
this leads to an Mongo Server error too.
this should do it:
db.shop.aggregate([
{
$lookup:
{
from: "client",
let: { pid: "$clientId" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", { $toObjectId: "$$pid" }]
}
}
}
],
as: "client"
}
},
{
$set: {
client: {
$arrayElemAt: ["$client", 0]
}
}
}
])
update: array of id strings
db.collection.aggregate(
[
{
$lookup:
{
from: "transactions",
let: { vid: "$transactionIds" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$vid",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "transactions"
}
}
])
db.collection("shop").aggregate([
{ $match: { _id: shopId } },
{ $addFields: {
convertedId: {$toObjectId: "$clientId"}
}},
{ $lookup: {
from: "users",
localField: "convertedId",
foreignField: "_id",
as: "result"
}}
]);

MongoDB doing $lookup on field of different types yet same content

I know that in logTable the "mailID" is "1234" (String)
and in mailTable the "_id" is 1234 (NumberInt)
But is there any way at all to do this $lookup?
log table
{
"_id" : "mailStuff0234",
"mailID" : "1234",
"typeState" : "NEW",
"changeByType" : "ADMIN"
}
mail table
{
"_id" : NumberInt(1234),
"user" : "torben#sunnythailand.com",
"subject" : "Visit to Atlantis Condo Resort"
}
and here is the aggregate
db.log.aggregate([
{ '$match': { typeState: 'NEW'} },
{ '$lookup': {
from: 'mail',
localField: 'mailID',
foreignField: '_id',
as: 'mail'
} },
{ '$unwind': '$mail' }
], {})
MongoDB 4.0 introduced $toInt operator so you can convert mailID value before applying $lookup.
db.log.aggregate([
{
$addFields: { mailID: { $toInt: "$mailID" } }
},
{
$lookup: {
from: "mail",
localField: "mailID",
foreignField: "_id",
as: "mail"
}
}
])
You can also use $lookup with custom pipeline:
db.log.aggregate([
{
$lookup: {
from: "mail",
let: { mailID: { $toInt: "$mailID" } },
pipeline: [ { $match: { $expr: { $eq: [ "$_id", "$$mailID" ] } } } ],
as: "mail"
}
}
])

Mongodb 3.2 : mongodb aggregation with $project before $lookup

I've a problem, actually I'm trying to join different collections for some operation, but I'm not able to get the result as both "BatchInfo" and "UserInfo" is coming as null.
The reason I'm doing "project" first is because I want to take out "Batch Id" and "User Id" from respective objects and then apply in respective "lookup" "foreignField". Also, I'm using mongo 3.2.
db.getCollection('coursecompletedfeedbacks').aggregate([
{
$project: {
"BATCH" : 1,
"FEEDBACK" : 1,
"USER" : 1,
"batchId" : "$BATCH._id",
"userId" : "$USER._id"
}
},
{
$lookup:{
from: "batches",
localField: "batchId",
foreignField: "_id",
as: "BatchInfo"
}
},
{
$lookup:{
from: "users",
localField: "userId",
foreignField: "_id",
as: "UserInfo"
}
}
])
Try as below:
db.getCollection('coursecompletedfeedbacks').aggregate([
{
$lookup:{
from: "batches",
let: { bId: "$BATCH._id" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", "$$bId"]
}
}
},
],
as: "BatchInfo"
}
},
{
$lookup:{
from: "users",
let: { uId: "$USER._id" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", "$$uId"]
}
}
},
],
as: "UserInfo"
}
},
{
$project: {
"BatchInfo" : 1,
"UserInfo" : 1,
""FEEDBACK" : 1,
}
},
])

Mongodb aggretate apply sort to lookup results, and add field index number

The aggregate was executed.
I got the results using lookup, but I need a sort.
In addition, I want to assign an index to the result value.
CollectionA :
{
"_id" : ObjectId("5a6cf47415621604942386cd"),
"contents" : [
ObjectId("AAAAAAAAAAAAAAAAAAAAAAAA"),
ObjectId("BBBBBBBBBBBBBBBBBBBBBBBB")
],
"name" : "jason"
}
CollectionB :
{
"_id" : ObjectId("AAAAAAAAAAAAAAAAAAAAAAAA")
"title" : "a title",
"date" : 2018-01-02
},
{
"_id" : ObjectId("BBBBBBBBBBBBBBBBBBBBBBBB")
"title" : "a title",
"date" : 2018-01-01
}
Query:
db.getCollection('A').aggregate([
{
$match : { "_id" : ObjectId("5a6cf47415621604942386cd") }
},
{
$lookup : {
from: "B",
localField: "contents",
foreignField: "_id",
as: "item"
}
},
{ $sort: { "item.date" : -1 } }
]);
Want Result:
{
"_id" : ObjectId("5a6cf47415621604942386cd"),
"contents" : [
{
"_id" : ObjectId("BBBBBBBBBBBBBBBBBBBBBBBB")
"title" : "a title",
"date" : 2018-01-01,
"index" : 0
},
{
"_id" : ObjectId("AAAAAAAAAAAAAAAAAAAAAAAA")
"title" : "a title",
"date" : 2018-01-02,
"index" : 1
}],
"name" : "jason"
}
The current problem does not apply to the sort.
And I don't know how to designate an index.
Below Aggregation may you. For your desire result.
db.CollectionA.aggregate([
{
$match: { "_id": ObjectId("5a6cf47415621604942386cd") }
},
{
$lookup: {
from: "CollectionB",
let: { contents: "$contents" },
pipeline: [
{
$match: { $expr: { $in: ["$_id", "$$contents"] } }
},
{ $sort: { date: 1 } }
],
as: "contents"
}
},
{
$project: {
contents: {
$map: {
input: { $range: [0, { $size: "$contents" }, 1 ] },
as: "element",
in: {
$mergeObjects: [
{ index: "$$element" },
{ $arrayElemAt: [ "$contents", "$$element" ]}
]
}
}
}
}
}
])
One way to go about it would be to unwind the array, sort it and then group it back
db.A.aggregate([
{
$match: {
"_id": ObjectId("5a6cf47415621604942386cd")
}
},
{
$lookup: {
from: "B",
localField: "contents",
foreignField: "_id",
as: "item"
}
},
{
$unwind: "$item"
},
{
$sort: {
"item.date": -1
}
},
{
$group: {
_id: "$_id",
contents: {
$push: "$item"
}
}
}
])
Another method is, (this is applicable only if the date field corresponds to the document creation date),
db.A.aggregate([
{
$match: {
"_id": ObjectId("5a6cf47415621604942386cd")
}
},
{
$lookup: {
from: "B",
localField: "contents",
foreignField: "_id",
as: "item"
}
},
{
$sort: {
"item": -1
}
}
])
Basically, this sorts on the basis of _id, and since _id is created using the creation date, it should sort accordingly.

Aggregate pipeline Match -> Lookup -> Unwind -> Match issue

I am puzzled as to why the code below doesn't work. Can anyone explain, please?
For some context: My goal is to get the score associated with an answer option for a survey database where answers are stored in a separate collection from the questions. The questions collection contains an array of answer options, and these answer options have a score.
Running this query:
db.answers.aggregate([
{
$match: {
userId: "abc",
questionId: ObjectId("598be01d4efd70a81c1c5ad4")
}
},
{
$lookup: {
from: "questions",
localField: "questionId",
foreignField: "_id",
as: "question"
}
},
{
$unwind: "$question"
},
{
$unwind: "$question.options"
},
{
$unwind: "$answers"
}
])
I get:
{
"_id" : ObjectId("598e588e0c5e24452c9ee769"),
"userId" : "abc",
"questionId" : ObjectId("598be01d4efd70a81c1c5ad4"),
"answers" : {
"id" : 20
},
"question" : {
"_id" : ObjectId("598be01d4efd70a81c1c5ad4"),
"options" : {
"id" : 10,
"score" : "12"
}
}
}
{
"_id" : ObjectId("598e588e0c5e24452c9ee769"),
"userId" : "abc",
"questionId" : ObjectId("598be01d4efd70a81c1c5ad4"),
"answers" : {
"id" : 20
},
"question" : {
"_id" : ObjectId("598be01d4efd70a81c1c5ad4"),
"options" : {
"id" : 20,
"score" : "4"
}
}
}
All great. If I now add to the original query a match that's supposed to find the answer option having the same id as the answer (e.g. questions.options.id == answers.id), things don't work as I would expect.
The final pipeline is:
db.answers.aggregate([
{
$match: {
userId: "abc",
questionId: ObjectId("598be01d4efd70a81c1c5ad4")
}
},
{
$lookup: {
from: "questions",
localField: "questionId",
foreignField: "_id",
as: "question"
}
},
{
$unwind: "$question"
},
{
$unwind: "$question.options"
},
{
$unwind: "$answers"
},
{
$match: {
"question.options.id": "$answers.id"
}
},
{
$project: {
_id: 0,
score: "$question.options.score"
}
}
])
This returns an empty result. But if I change the RHS of the $match from "$answers.id" to 20, it returns the expected score: 4. I tried everything I could think of, but couldn't get it to work and can't understand why it doesn't work.
I was able to get it to work with the following pipeline:
{
$match: {
userId: "abc",
questionId: ObjectId("598be01d4efd70a81c1c5ad4")
}
},
{
$lookup: {
from: "questions",
localField: "questionId",
foreignField: "_id",
as: "question"
}
},
{
$unwind: "$question"
},
{
$unwind: "$question.options"
},
{
$unwind: "$answers"
},
{
$addFields: {
areEqual: { $eq: [ "$question.options.id", "$answers.id" ] }
}
},
{
$match: {
areEqual: true
}
},
{
$project: {
_id: 0,
score: "$question.options.score"
}
}
I think the reason it didn't work with a direct match is the fact that questions.options.id doesn't actually reference the intended field... I needed to use $questions.options.id which wouldn't work as a LHS of a $match, hence the need to add an extra helper attribute.