Mongo lookup to join a child/sub object? - mongodb

I am joining booking and customers collections.
My bookings object looks like this:
{
"bookingID": 1,
"customerID": "adam.apple#email.com",
"room": [
{
"roomID": "JDS",
"numRooms": 1
}
]
}
and customer object like this:
{
"customer": {
"firstname": "Adam",
"surname": "Apple",
"title": "Nr.",
"emailAddress": "adam.apple#email.com"
},
"address": {
"street": "221B Baker St",
"city": "London",
"state": null,
"zipcode": "NW1 6XE",
"country": "UK"
}
}
I am using this code:
{ $lookup: {from: 'customers', localField: 'customerID', foreignField: 'customer.emailAddress', as: 'traveller'} },
{ $unwind: '$traveller' },
{ $project: {'traveller': {_id: 0, 'address': 0}} }
to combine the output. I'd like to get:
"traveller": {
"firstname": "Adam",
"surname": "Apple",
"title": "Nr.",
"emailAddress": "adam.apple#email.com"
}
and I get:
"traveller": {
"customer": {
"firstname": "Adam",
"surname": "Apple",
"title": "Nr.",
"emailAddress": "adam.apple#email.com"
}
}
How do I do this?
You can check it here:
https://mongoplayground.net/p/ik2UtP4-EHa

Here you go :-
Just change the project stage as below -
{
$project: {
"traveller": "$traveller.customer",
"bookingID": 1,
"cruise": 1,
"customerID": 1,
"room": 1
}
}
Mongo Playground

Try this one its not break your other pipelines
aggregate([{
$lookup: {
from: 'customers',
localField: 'customerID',
foreignField: 'customer.emailAddress',
as: 'traveller'
} },
{ $unwind: '$traveller' },
{ $project: {
'traveller':"$traveller.customer",
bookingID:1,
cruise:1,
customerID:1,
room:1,
} }])

Related

Update data in mongo db with condition of another collection

I have 2 collection in mongodb: Account, Information.
Account:
{
"_id": {
"$oid": "6348dc197a7b552660170d8b"
},
"username": "12345",
"password": "123dsgfdsgdfsg",
"email": "1243",
"role": "123",
"_infoid": {
"$oid": "6348dc197a7b552660170d8a"
}
}
Information:
{
"_id": {
"$oid": "6348dc197a7b552660170d8a"
},
"avatar": "hello",
"name": "Abcd",
"phonenumber": "012345678",
"address": "abcd"
}
I wanna change "phonenumber" to "123" but I just have "_id" of Account. Can I change it with aggregation pipeline?
Does this seem what you try to achieve?
// populate database with test data
use("test_db")
db.account.insertOne({
"_id": "6348dc197a7b552660170d8b",
"username": "12345",
"password": "123dsgfdsgdfsg",
"email": "1243",
"role": "123",
"_infoid": "6348dc197a7b552660170d8a"
})
db.information.insertOne({
"_id": "6348dc197a7b552660170d8a",
"avatar": "hello",
"name": "Abcd",
"phonenumber": "012345678",
"address": "abcd"
})
// define some test variables to use
let some_account_id = "6348dc197a7b552660170d8b"
let new_phone_number = "+9876543210"
// change data
db.account.aggregate([
{
$match: {_id: some_account_id}
},
{
$addFields: {phonenumber: new_phone_number}
},
{
$project: {phonenumber: 1, _id: "$_infoid"}
},
{
$merge:{
into: "information",
whenNotMatched: "fail",
}
}
])
// show final results
db.information.find()
Result:
[
{
"_id": "6348dc197a7b552660170d8a",
"avatar": "hello",
"name": "Abcd",
"phonenumber": "+9876543210",
"address": "abcd"
}
]
You don't need to duplicate _id, findOneAndUpdate() can be executed for this use case.
The definition of it is:
db.collection.findOneAndUpdate( filter, update, options )
which updates a single document based on the filter and sort criteria.
Below you can refer to the link for more details:
https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/
.
Performa a $lookup and perform some wrangling to your desired format. Finally do a $merge to update to the collection Information
db.Information.aggregate([
{
"$lookup": {
"from": "Account",
"localField": "_id",
"foreignField": "_infoid",
"pipeline": [
{
"$project": {
role: 1
}
}
],
"as": "AccountLookup"
}
},
{
"$unwind": "$AccountLookup"
},
{
$set: {
phonenumber: "$AccountLookup.role"
}
},
{
$unset: "AccountLookup"
},
{
"$merge": {
"into": "Information",
"on": "_id",
"whenMatched": "merge",
"whenNotMatched": "discard"
}
}
])
Here is the Mongo Playground for your reference.

How to use current field in second $match?

Let's say i have 2 collections
// Post collection:
[
{
"_id": "somepost1",
"author": "firstuser",
"title": "First post"
},
{
"_id": "somepost2",
"author": "firstuser",
"title": "Second post"
},
{
"_id": "somepost3",
"author": "firstuser",
"title": "Third post"
}
]
// User collection:
[
{
"_id": "firstuser",
"nickname": "John",
"posts": {
"voted": []
}
},
{
"_id": "seconduser",
"nickname": "Bob",
"posts": {
"voted": [
{
"_id": "somepost1",
"vote": "1"
},
{
"_id": "somepost3",
"vote": "-1"
}
]
}
}
]
And i need to get this result:
[
{
"_id": "somepost1",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "First post",
"myvote": "1"
},
{
"_id": "somepost2",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "Second post",
"voted": "0"
},
{
"_id": "somepost3",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "Third post",
"myvote": "-1"
}
]
How can i make a request with aggregation, which will display this output with dynamic _id of elements?
I have problem with using current _id of post in second $match and setting "myvote" to 0 if there are no element in "posts.voted" associated with current post.
Here what i've tried: https://mongoplayground.net/p/v70ZUioVSpQ
db.post.aggregate([
{
$match: {
author: "firstuser"
}
},
{
$lookup: {
from: "user",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$addFields: {
author: {
$arrayElemAt: [
"$author",
0
]
}
}
},
{
$lookup: {
from: "user",
localField: "_id",
foreignField: "posts.voted._id",
as: "Results"
}
},
{
$unwind: "$Results"
},
{
$unwind: "$Results.posts.voted"
},
{
$match: {
"Results.posts.voted._id": "ID OF CURRENT POST"
}
},
{
$project: {
_id: 1,
author: {
_id: 1,
nickname: 1
},
title: 1,
myvote: "$Results.posts.voted.vote"
}
}
])
From the $match docs:
The query syntax is identical to the read operation query syntax
The query syntax does not allow usage of document values. which is what you're trying to do.
What we can do is use $expr within the $match stage, this allows us to use aggregation oprerators, thus also giving access to the document values. like so:
{
$match: {
$expr: {
$eq: ['$Results.posts.voted._id', '$_id'],
}
},
},

How to aggregate nested lookup array in mongoose?

I have a problem with how to lookup nested array, for example i have 4 collections.
User Collection
"user": [
{
"_id": "1234",
"name": "Tony",
"language": [
{
"_id": "111",
"language_id": "919",
"level": "Expert"
},
{
"_id": "111",
"language_id": "920",
"level": "Basic"
}
]
}
]
Language Collection
"language": [
{
"_id": "919",
"name": "English"
},
{
"_id": "920",
"name": "Chinese"
}
]
Job
"job": [
{
"_id": "10",
"title": "Programmer",
"location": "New York"
}
],
CvSubmit Collection
"cvsubmit": [
{
"_id": "11",
"id_user": "1234",
"id_job": "11"
}
]
And my query aggregation is:
db.cvsubmit.aggregate([
{
$lookup: {
from: "user",
localField: "id_user",
foreignField: "_id",
as: "id_user"
}
},
{
$lookup: {
from: "language",
localField: "id_user.language.language_id",
foreignField: "_id",
as: "id_user.language.language_id"
}
},
])
But the result is:
[
{
"_id": "11",
"id_job": "11",
"id_user": {
"language": {
"language_id": [
{
"_id": "919",
"name": "English"
},
{
"_id": "920",
"name": "Chinese"
}
]
}
}
}
]
I want the result like this, also showing all user data detail like name:
[
{
"_id": "11",
"id_job": "11",
"id_user": {
"_id": "1234",
"name": "Tony"
"language": [
{
"_id": "919",
"name": "English",
"Level": "Expert"
},
{
"_id": "920",
"name": "Chinese",
"level": "Basic"
}
]
}
}
]
Mongo Playground link https://mongoplayground.net/p/i0yCucjruey
Thanks before.
$lookup with user collection
$unwind deconstruct id_user array
$lookup with language collection and return in languages field
$map to iterate look of id_user.language array
$reduce to iterate loop of languages array returned from collection, check condition if language_id match then return name
db.cvsubmit.aggregate([
{
$lookup: {
from: "user",
localField: "id_user",
foreignField: "_id",
as: "id_user"
}
},
{ $unwind: "$id_user" },
{
$lookup: {
from: "language",
localField: "id_user.language.language_id",
foreignField: "_id",
as: "languages"
}
},
{
$addFields: {
languages: "$$REMOVE",
"id_user.language": {
$map: {
input: "$id_user.language",
as: "l",
in: {
_id: "$$l._id",
level: "$$l.level",
name: {
$reduce: {
input: "$languages",
initialValue: "",
in: {
$cond: [
{ $eq: ["$$this._id", "$$l.language_id"] },
"$$this.name",
"$$value"
]
}
}
}
}
}
}
}
}
])
Playground
You database structure is not accurate as per NoSQL, there should be max 2 collections, loot of join using $lookup and $unwind will cause performance issues.

MongoDB aggregate merging fields

I have a mongo Database I'll like to "join" two of them and then merge some other fields:
Let's see the schemas:
Students Schema (and data):
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
}
],
"__v": NumberInt("1"),
}
and Subject schema:
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math",
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter",
}
What I'll like to make a query with the subjects (all info) of a student, also if the student have additional fields in subject like passed add it to the subject subdocument.
Here is my query till now:
db.students.aggregate([
{
$match:
{
_id : ObjectId('5fbd564981b1313de790b580')
}
},
{
$lookup :
{
from : "subjects",
localField : "subjects._id",
foreignField : "_id",
as : "FoundSubject"
}
}
]);
which correctly make the "join" but the merge is still missing, I got as result:
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
}
],
"__v": NumberInt("1"),
"FoundSubject": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math"
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter"
}
]
}
but I'll like to have:
{
"_id": ObjectId("5fbd564981b1313de790b580"),
"name": "John Doe",
"age": "21",
"image": "https://XXXX/481.png",
"subjects": [
{
"_id": ObjectId("5fbd4e6881b1313de790b56b"),
"course": 3,
"teacher": "John Smith",
"name": "Math",
"passed": true,
},
{
"_id": ObjectId("5fcb63fa8814d96876c687bf"),
"name": "IT",
"course": 8,
"teacher": "John Peter"
}
],
"__v": NumberInt("1"),
}
with merged data and field "passed" added. How can accomplish that?
I'm new to MongoDB coming from MySQL.
Thanks
You need to merge both objects, add below stage after $lookup,
MongoDB Version From 3.4
$map to iterate loop of students array
$reduce to iterate loop of FoundSubject array, check condition if condition match then return required fields otherwise return initial value
$project to remove FoundSubject from result
{
$addFields: {
subjects: {
$map: {
input: "$subjects",
as: "s",
in: {
$reduce: {
input: "$FoundSubject",
initialValue: {},
in: {
$cond: [
{ $eq: ["$$s._id", "$$this._id"] },
{
_id: "$$this._id",
course: "$$this.course",
name: "$$this.name",
teacher: "$$this.teacher",
passed: "$$s.passed"
},
"$$value"
]
}
}
}
}
}
}
},
{ $project: { FoundSubject: 0 } }
Playground
MongoDB Version From 4.4
$map to iterate loop of students array,
$filter to get matching document from FoundSubject array and $first to get first object from array returned by filter
$mergeObjects to merge current objects with found result object from filter
remove FoundSubject using $$REMOVE
// skipping your stages
{
$addFields: {
FoundSubject: "$$REMOVE",
subjects: {
$map: {
input: "$subjects",
as: "s",
in: {
$mergeObjects: [
"$$s",
{
$first: {
$filter: {
input: "$FoundSubject",
cond: { $eq: ["$$s._id", "$$this._id"] }
}
}
}
]
}
}
}
}
}
Playground

MongoDb documents join with inner objects

I have 2 documents named calls and customers
i want to join locationId of calls to customers.location._id
calls
"_id": "5d456560221b9147e4af4b97",
"customerId": "5d0a3bbfac9470aea56ad619",
"locationId": "5d11d2c4cba8151f67f735b1",
"startDateTime": "2019-06-21T05:30:00.000Z",
"endDateTime": "2019-06-21T06:00:00.000Z"
}
customers
"_id": "5d0a3bbfac9470aea56ad619",
"locations": [
{
"_id": "5d11d2c4cba8151f67f735b1",
"phone": "9889599999",
"ext": "91",
"address": "sad",
"neighborhood": "sda",
"city": "punetest",
"state": "MH",
"zip": "589365",
"country": "india",
"isBillingAddress": false,
"taxPercentage": 0,
"type": "Residential",
"status": "Active",
"firstName": "A",
"lastName": "B"
},
{
"_id": "5d11e457cba8151f67f735b3",
"phone": "969696999",
"ext": "91",
"address": "",
"neighborhood": "",
"city": "udaipur",
"state": "raj",
"zip": "312563",
"country": "india",
"isBillingAddress": false,
"taxPercentage": 0,
"type": "Residential",
"status": "Deleted",
"firstName": "AB",
"lastName": "CD"
}
],
"createdAt": "2019-06-19T13:42:23.479Z",
"updatedAt": "2019-06-25T13:39:07.597Z"
}
[
{
$lookup: {
from: 'customers.locations',
localField: 'locationId',
foreignField: '_id',
as: 'customers.locations',
},
}
]);
it is not working
I have 2 documents named calls and customers i want to join locationId of calls to customers.location._id
I want output```
{
"_id": "5d456560221b9147e4af4b97",
"customerId": "5d0a3bbfac9470aea56ad619",
"locationId": "5d11d2c4cba8151f67f735b1",
"location":{"_id": "5d11d2c4cba8151f67f735b1",
"phone": "9889599999",
"ext": "91",
"address": "sad",
"neighborhood": "sda",
"city": "punetest",
"state": "MH",
"zip": "589365",
"country": "india",
"isBillingAddress": false,
"taxPercentage": 0,
"type": "Residential",
"status": "Active",
"firstName": "A",
"lastName": "B"}
}
Ok, this is what you're looking at :
If your locationId in calls has only one matching customer.locations._id in customer :
db.getCollection('calls').aggregate([
{
$lookup: {
from: 'customers',
localField: 'locationId',
foreignField: 'locations._id',
as: 'customersCalls',
}
}, { $unwind: '$customersCalls' },
{
$project: {
customerId: 1, locationId: 1, locations: {
$filter: {
input: "$customersCalls.locations",
as: "item",
cond: { $eq: ["$$item._id", '$locationId'] }
}
}
}
},
{ $group: { _id: '$_id', result: { $first: '$$ROOT' } } },
{ $replaceRoot: { newRoot: "$result" } }
])
else if your locationId in calls has multiple matching customer.locations._id in customer :
db.getCollection('calls').aggregate([
{
$lookup: {
from: 'customers',
localField: 'locationId',
foreignField: 'locations._id',
as: 'customersCalls',
}
}, { $unwind: '$customersCalls' },
{
$project: {
customerId: 1, locationId: 1, locations: {
$filter: {
input: "$customersCalls.locations",
as: "item",
cond: { $eq: ["$$item._id", '$locationId'] }
}
}
}
},
{ $group: { _id: '$_id', locations: { $push: { $arrayElemAt: ["$locations", 0] } }, result: { $first: '$$ROOT' } } },
{ $addFields: { 'result.locations': '$locations' } },
{ $replaceRoot: { newRoot: "$result" } }
])