I have got the two collection named
BlogCategory and
SubBlogCategory .The document of the BlogCategory consists the array of objectId's of subcategory id.
The category document is as follows
{
"_id" : ObjectId("5af2c6e8bfab7269e9a42ded"),
"title" : "Javascript",
"subcategory" : [
ObjectId("5af29fcc9a52623b7088ef4e"),
ObjectId("5aebf78681273424e5f55ecc")
]
}
The subcategory document is as follows
{
"_id" : ObjectId("5af29fcc9a52623b7088ef4e"),
"title" : "Reactjs"
}
{
"_id" : ObjectId("5aebf78681273424e5f55ecc"),
"title" : "Vuejs",
}
How can I populate the subcategory documents on the category documents while getting all the documents of the collection category?
I have used the following query but its only populating the single id of the array
db.BlogCategory.aggregate([
{
$unwind: "$subcategory"
},
{
$lookup:
{
from: "SubBlogCategory",
localField: "subcategory",
foreignField: "_id",
as: "sub_doc"
}
}
])
No need to $unwind here... $unwind duplicates each document in the pipeline, once per array element.
db.BlogCategory.aggregate([
{
$lookup:
{
from: *Collection_Name*,
localField: "subcategory",
foreignField: "_id",
as: "sub_doc"
}
}
])
if you are using mongodb version 3.6 then you can use pipeline
db.BlogCategory.aggregate([
{ "$lookup": {
"from": *Collection_Name*,
"let": { "subcategory": "$subcategory" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$subcategory" ] } } }
],
"as": "sub_doc"
}}
])
Related
We are really new to MongoDB query writing. We have 2 MongoDB tables Supplier1 & Supplier 2. Both have the same _id. But the version number of these objects can be different sometimes.
We need to find out _id when the version of 2 collections are different (i.e. Suplier1.version != Supplier2.version)
Supplier1
{
"_id" : ObjectId("60cd86b914dfed073d77300f"),
"companyName" : "Main Supplier",
"version" : NumberLong(246),
}
Supplier2
{
"_id" : ObjectId("60cd86b914dfed073d77300f"),
"companyName" : "Main Supplier",
"version" : NumberLong(247),
}
What we have written up to now and no idea to move forward with this. Any help is highly appreciated.
db.getCollection("Supplier1").aggregate([
{
$lookup: {
from: "Supplier2",
localField: "_id",
foreignField: "_id",
as: "selected-supplier"
}
},
You can simply use a sub-pipeline in $lookup. Simply $unwind the result array to filter out unwanted result.
db.Supplier1.aggregate([
{
"$lookup": {
"from": "Supplier2",
"let": {
id1: "$_id",
version1: "$version"
},
"pipeline": [
{
"$match": {
$expr: {
$and: [
{
$eq: [
"$$id1",
"$_id"
]
},
{
$ne: [
"$$version1",
"$version"
]
}
]
}
}
}
],
"as": "selected-supplier"
}
},
{
"$unwind": "$selected-supplier"
}
])
Here is the Mongo playground for your reference.
so i've got a collection like so
_id: "a6c67aad-e90c-4a13-aae0-74e5ca5c8632"
value : true
and one like this
_id: "a6c67aad-e90c-4a13-aae0-74e5ca5c8632"
otherValue : false
How can i use aggregation pipelines to update the second collection otherValue with the value from the first collection based on _id
I've tried using lookup and then unwind like
{
from: 'col1',
localField: 'otherValue',
foreignField: 'value',
as: 'data'
}
and then unwind
{
path: '$val'
}
But im not quite sure where to go from here, any help would be greatly appreciated.
Try this:
db.collection1.aggregate([
{
$lookup: {
from: "collection2",
let: { c1_id: "$_id", value: "$value" },
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$c1_id"] }
}
},
{
$addFields: { otherValue: "$$value" }
}
],
as: "data"
}
},
{
$unwind: "$data"
}
])
Output:
{
"_id" : "a6c67aad-e90c-4a13-aae0-74e5ca5c8632",
"value" : true,
"data" : {
"_id" : "a6c67aad-e90c-4a13-aae0-74e5ca5c8632",
"otherValue" : true
}
}
Where collection1 is:
{
"_id" : "a6c67aad-e90c-4a13-aae0-74e5ca5c8632",
"value" : true
}
Where collection2 is:
{
"_id" : "a6c67aad-e90c-4a13-aae0-74e5ca5c8632",
"otherValue" : false
}
You might use the $merge aggregation stage.
match the documents from the source collection that you want to use to update the second collect.
lookup the matching document from the second collection
unwind so it is a single document instead of an array (this stage also eliminates documents that don't match one from the second collection)
addFields to store the value from the first document into the looked up document
replaceRoot to the modified looked-up document
merge the modified documents with the original collection, matching on _id
db.collection.aggregate([
{$match: { filter to pick the documents }},
{$lookup: {
from: "otherCollection"
localField: "_id",
foreignField: "_id",
as: "otherDocument"
}},
{$unwind: "$otherDocument"},
{$addFields:{
"otherDocument.otherValue": "$value"
}},
{$replaceRoot: {newRoot: "$otherDocument"}},
{$merge: {
into: "otherCollection",
on: "_id",
whenMatched: "replace",
whenNotMatched: "insert"
}}
])
In Lookup with a pipeline, I would like to get the linked records from an array in the parent document.
// Orders
[{
"_id" : ObjectId("5b5b91a25c68de2538620689"),
"Name" : "Test",
"Products" : [
ObjectId("5b5b919a5c68de2538620688"),
ObjectId("5b5b925a5c68de2538621a15")
]
}]
// Products
[
{
"_id": ObjectId("5b5b919a5c68de2538620688"),
"ProductName": "P1"
},
{
"_id": ObjectId("5b5b925a5c68de2538621a15"),
"ProductName": "P2"
}
,
{
"_id": ObjectId("5b5b925a5c68de2538621a55"),
"ProductName": "P3"
}
]
How to make a lookup between Orders and Products when Products field is an array!
I tried this query
db.getCollection("Orders").
aggregate(
[
{
$lookup:
{
from: "Products",
let: { localId: "$_id" , prods: "$Products" },
pipeline: [
{
"$match":
{
"_id" : { $in: "$$prods" }
}
},
{
$project:
{
"_id": "$_id",
"name": "$prods" ,
}
}
],
as: "linkedData"
}
},
{
"$skip": 0
},
{
"$limit": 1
},
]
)
This is not working because $in is expecting an array, and even though $$prods is an array, it is not accepting it.
Is my whole approach correct? How to make this magic join ?
You were going in the right direction the only thing you missed here is to use expr with in aggregation operator which matches the same fields of the document
db.getCollection("Orders").aggregate([
{ "$lookup": {
"from": "Products",
"let": { "localId": "$_id" , "prods": "$Products" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$prods" ] } } },
{ "$project": { "_id": 1, "name": "$ProductName" } }
],
"as": "linkedData"
}},
{ "$skip": 0 },
{ "$limit": 1 }
])
See the docs here
You just need regular $lookup, the documentation states that:
If your localField is an array, you may want to add an $unwind stage to your pipeline. Otherwise, the equality condition between the localField and foreignField is foreignField: { $in: [ localField.elem1, localField.elem2, ... ] }.
So for below aggregation:
db.Orders.aggregate([
{
$lookup: {
from :"Products",
localField: "Products",
foreignField: "_id",
as: "Products"
}
}
])
you'll get following result for your sample data:
{
"_id" : ObjectId("5b5b91a25c68de2538620689"),
"Name" : "Test",
"Products" : [
{
"_id" : ObjectId("5b5b919a5c68de2538620688"),
"ProductName" : "P1"
},
{
"_id" : ObjectId("5b5b925a5c68de2538621a15"),
"ProductName" : "P2"
}
]
}
have you try unwind before the lookup. use unwind to brak the array annd then make lookup.
I'm Strugling with some aggregation functions in mongodb.
I want to get books Documents in author's document that has just books ids as array of strings ids like this :
Author Document
{
"_id" : "10",
"full_name" : "Joi Dark",
"books" : ["100", "200", "351"],
}
And other documents (books) :
{
"_id" : "100",
"title" : "Node.js In Action",
"ISBN" : "121215151515154",
"date" : "2015-10-10"
}
So in result i want this :
{
"_id" : "10",
"full_name" : "Joi Dark",
"books" : [
{
"_id" : "100",
"title" : "Node.js In Action",
"ISBN" : "121215151515154",
"date" : "2015-10-10"
},
{
"_id" : "200",
"title" : "Book 2",
"ISBN" : "1212151454515154",
"date" : "2015-10-20"
},
{
"_id" : "351",
"title" : "Book 3",
"ISBN" : "1212151454515154",
"date" : "2015-11-20"
}
],
}
Use $lookup which retrieves data from the nominated collection in from based on the matching of the localField to the foreignField:
db.authors.aggregate([
{ "$lookup": {
"from": "$books",
"foreignField": "_id",
"localField": "books",
"as": "books"
}}
])
The as is where in the document to write an "array" containing the related documents. If you specify an existing property ( such as is done here ) then that property is overwritten with the new array content in output.
If you have a MongoDB before MongoDB 3.4 then you may need to $unwind the array of "books" as the localField first:
db.authors.aggregate([
{ "$unwind": "$books" },
{ "$lookup": {
"from": "$books",
"foreignField": "_id",
"localField": "books",
"as": "books"
}}
])
Which creates a new document for each array member in the original document, therefore use $unwind again and $group to create the original form:
db.authors.aggregate([
{ "$unwind": "$books" },
{ "$lookup": {
"from": "$books",
"foreignField": "_id",
"localField": "books",
"as": "books"
}},
{ "$unwind": "$books" },
{ "$group": {
"_id": "$_id",
"full_name": { "$first" "$full_name" },
"books": { "$push": "$books" }
}}
])
If in fact your _id values in the foreign collection of of ObjectId type, but you have values in the localField which are "string" versions of that, then you need to convert the data so the types match. There is no other way.
Run something like this through the shell to convert:
var ops = [];
db.authors.find().forEach(doc => {
doc.books = doc.books.map( book => new ObjectId(book.valueOf()) );
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { "books": doc.books }
}
}
});
if ( ops.length >= 500 ) {
db.authors.bulkWrite(ops);
ops = [];
}
});
if ( ops.length > 0 ) {
db.authors.bulkWrite(ops);
ops = [];
}
That will convert all the values in the "books" array into real ObjectId values that can actually match in a $lookup operation.
Just adding on top of the previous answer. If your input consists of an array of strings and you want to convert them to ObjectIds, you can achieve this by using a projection, followed by a map and the $toObjectId method.
db.authors.aggregate([
{ $project: {
books: {
$map: {
input: '$books',
as: 'book',
in: { $toObjectId: '$$book' },
},
},
},},
{ $lookup: {
from: "$books",
foreignField: "_id",
localField: "books",
as: "books"
}
},
])
Ideally, your database would be formatted in such a manner that your aggregates are stored as ObjectIds, but in the case where that is not an option, this poses as a viable solution.
I want to use my array field 0th value to find a match in sale document using Mongo aggregate $lookup query. Here is my query:
db.products.aggregate([
{
$match : { _id:ObjectId("57c6957fb190ecc02e8b456b") }
},
{
$lookup : {
from : 'sale',
localField: 'categories.0',
foreignField: 'saleCategoryId',
as : 'pcSales'
}
}]);
Result :
{
"_id" : ObjectId("57c6957fb190ecc02e8b456b"),
"categories" : [
"57c54f0db190ec430d8b4571"
],
"pcSales" : [
{
"_id" : ObjectId("57c7df5f30fb6eacb3810d1b"),
"Title" : "Latest Arrivals",
}
]}
This query will return me a match but when i check it not a match. I don't get why is this happening, And when i removed 0th part from query its return blank array.
Like this:
{
"_id" : ObjectId("57c6957fb190ecc02e8b456b"),
"categories" : [
"57c54f0db190ec430d8b4571"
],
"pcSales" : []
}
saleCategoryId is also a array field which contain array of categoriesKey.
Please help.
Because your localField is an array, you'll need to add an $unwind stage to your pipeline before the lookup or use the $arrayElemAt in a $project pipeline step to get the actual element in the array.
Here are two examples, one which uses the $arrayElemAt operator:
db.products.aggregate([
{ "$match" : { "_id": ObjectId("57c6957fb190ecc02e8b456b") } },
{
"$project": {
"category": { "$arrayElemAt": [ "$categories", 0 ] }
}
},
{
"$lookup": {
from : 'sale',
localField: 'category',
foreignField: 'saleCategoryId',
as : 'pcSales'
}
}
]);
and this which uses $unwind to flatten the categories array first before applying the $lookup pipeline:
db.products.aggregate([
{ "$match" : { "_id": ObjectId("57c6957fb190ecc02e8b456b") } },
{ "$unwind": "$categories" },
{
"$lookup": {
from : 'sale',
localField: 'categories',
foreignField: 'saleCategoryId',
as : 'pcSales'
}
}
]);