I am using MongoDB 3.4.
Have 2 collection as follows.
Collection 1:- type
{
"_id": {
"$numberLong": "1234"
},
"name" : "board1"
"type" : "electronic"
},
{
"_id": {
"$numberLong": "1235"
},
"name" : "board2",
"type" : "electronic"
}
Collection 2:- products
{
"_id": {
"$numberLong": "9876"
},
"types" : [
"1234",
"1235",
"1238"
]
},
{
"_id": {
"$numberLong": "9875"
},
"types" : [
"1234",
"1238"
]
}
Type collection will have multiple types and each product in products collection will have multiple types.
There can be multiple document with different ids for the same type in type collection. And, product collection might have types array with different Ids of same type or different type.
I would like to get all the ids of type electronic and find the products which has id in the types array for each product.
I want result like the below one.
{
"_id": {
"$numberLong": "1234"
},
"name" : "board1",
"products" : [
"9876",
"9875"
]
},
{
"_id": {
"$numberLong": "1235"
},
"name" : "board2"
"products" : [
"9876",
"9875"
]
}
Currently, I am making so many calls, like for each type id, get all products.
Is there any other simple way with single query using $lookup or any other mechanism?
You can try below aggregation in mongodb 3.6 and above
db.types.aggregate([
{ "$match": { "type" : "electronic" }},
{ "$lookup": {
"from": "testCollection2",
"let": { "typeId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$$typeId", "$types"] }}}
],
"as": "products"
}},
{ "$addFields": {
"products": "$products._id"
}}
])
You can try bbelow aggregation in mongodb 3.4
db.types.aggregate([
{ "$match": { "type" : "electronic" }},
{ "$lookup": {
"from": "testCollection2",
"localField": "_id",
"foreignField": "types",
"as": "products"
}},
{ "$addFields": {
"products": "$products._id"
}}
])
In MongoDB 3.4 you can use $lookup and then $addFields to get _id from products:
db.types.aggregate([
{
"$match": { "type" : "electronic" }
},
{
$lookup: {
from: "products",
localField: "_id",
"foreignField": "types",
"as": "products"
}
},
{
$project: {
field1: 1,
field2: 1,
products: {
$map: {
input: "$products",
as: "p",
in: "$$p._id"
}
}
}
}
])
Related
how to correct lookup collection product which has array of ids of prices and need execute query exactly from prices collection query. Need to show price record with lookup product record
so, I have prices record
{
"_id" : "813f02ff-882e-44f7-b2bc-2f067427daf6",
"unit_amount" : 333,
"currency" : "USD",
"interval" : "year",
"active" : true
}
and product
"_id" : "3c46f277-8953-4f96-baf1-bd871ee3301f",
"name" : "test",
"prices" : [
"813f02ff-882e-44f7-b2bc-2f067427daf6",
"f5c76122-6132-4e4b-a26b-41bbd6325acc",
"3e4be68e-fbed-47f7-b871-92de72cb00df"
]
and my query, I thought it should be like that
db.getCollection('price').aggregate([
{
"$lookup": {
"from": "product",
"let": { "prid": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$$prid", '$prices'] } } }
],
"as": "product_tbl"
}
}
])
faced with
{
"ok" : 0,
"errmsg" : "PlanExecutor error during aggregation :: caused by :: $in requires an array as a second argument, found: missing",
"code" : 40081,
"codeName" : "Location40081"
}
but it's not works. How it shoul be look ?
Seems like some of the documents in your product collection are missing prices
key. You can try this:
db.prices.aggregate([
{
"$lookup": {
"from": "product",
"let": {
"prid": "$_id"
},
"pipeline": [
{
"$addFields": {
"prices": {
"$ifNull": [
"$prices",
[]
]
}
}
},
{
"$match": {
"$expr": {
"$in": [
"$$prid",
"$prices"
]
}
}
}
],
"as": "product_tbl"
}
}
])
Here, we recompute the prices and set it to empty array, if it's missing, before the $match. Playground link.
I have two collections, tennis matches with two players and players.
matches looks like this:
{
"_id" : ObjectId("5ce51febc6dd820a820f20a5"),
"players" : [
ObjectId("5ce51c1af3cd6009a171a5b3"),
ObjectId("5ce51c1af3cd6009a171a350")
],
"result" : "4:6 6:3 7:6(7) 7:6(8)"
},
{
"_id" : ObjectId("5ce51febc6dd820a820f20a6"),
"players" : [
ObjectId("5ce51c1af3cd6009a171a005"),
ObjectId("5ce51c1af3cd6009a171a16c")
],
"result" : "6:2 4:6 6:3"
},
[...]
and players like this:
{
"_id" : ObjectId("5ce51c1af3cd6009a171a5b3"),
"name" : "Serena Williams",
"country" : "USA"
},
{
"_id" : ObjectId("5ce51c1af3cd6009a171a350"),
"name" : "Garbiñe Muguruza",
"country" : "Spain"
},
[...]
I need all matches where players[0] is equal to a name and players[1] to another name.
I've tried this without success:
db.matches.aggregate([
{
$unwind: "$players"
},
{
$lookup: {
from: "players",
localField: "players",
foreignField: "_id",
as: "tmp_join"
}
},
{
$match: {
"tmp_join.name": ["Serena Williams","Garbiñe Muguruza"]
}
}
])
You have to first $unwind the tmp_join array and then you can use $in to find the documents contain name.
db.matches.aggregate([
{ "$lookup": {
"from": "players",
"localField": "players",
"foreignField": "_id",
"as": "tmp_join"
}},
{ "$unwind": "$tmp_join" },
{ "$match": {
"tmp_join.name": {
"$in": ["Serena Williams","Garbiñe Muguruza"]
}
}}
])
Use below aggregation if you are using mongodb 3.6 and above
db.matches.aggregate([
{ "$lookup": {
"from": "players",
"let": { "players": "$players" },
"pipeline": [
{ "$match": {
"$expr": { "$in": ["$_id", "$$players"] },
"name": { "$in": ["Serena Williams", "Garbiñe Muguruza"] }
}}
],
"as": "tmp_join"
}},
{ "$match": { "$expr": { "$gt": [{ "$size": "$tmp_join" }, 1] }}}
])
I want all the values returned from "Items" and when there is a match I want "isActive" to be true.
exports.listUserItems = function (req, res) {
// Aggregate results
User.aggregate([{
"$match": {
"username": req.params.username
}
}, {
"$lookup": {
"from": "Items",
"localField": "itemIds",
"foreignField": "_id",
"as": "items"
}
}, {
"$unwind": {
"path": "$items"
}
}, {
"$project": {
"item": "$items.item_name",
"isActive": '1',
"_id": 0
}
}], (err, result) => res.json(result));
};
What is the best way to go about accomplishing this?
I was going to return all the items and users items seperately, then compare them and object.value them etc. etc... that seems like overkill. Can it be done on the model side?
I'm unable to post the document structure because stackoverflow doesn't let me, but you should get the idea.
Edit:
User Document
{
"username" : "anonuser",
"items" : [
ObjectId("5ba8345f1e56fe8e6caaaa07"),
ObjectId("5ba706d64e82292e72e9ae71")
]
}
Then I have an "Items" collection which has 3 documents like this.
{
"_id" : ObjectId("5ba706d64e82292e72e9ae71"),
"item_name" : "Salary"
}
My expected json api output is to be.
[{"_id":"5ba706d64e82292e72e9ae71","item_name":"Salary","isActive":true},{"_id":"5ba8345f1e56fe8e6caaaa07","item_name":"Fulltime","isActive":true},{"_id":"5ba9af6c1e56fe8e6cab521e","item_name":"Advisor","isActive":false}]
You can try below aggregation
Items.aggregate([
{ "$lookup": {
"from": "users",
"let": { "itemsId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$$itemsId", "$items"] }}},
{ "$project": { "isActive": { "$literal": true }}}
],
"as": "items"
}},
{ "$addFields": {
"isActive": {
"$ifNull": [{ "$arrayElemAt": ["$items.isActive", 0] }, { "$literal": false }]
}
}},
{ "$project": { "items": 0 }}
])
I have the following collections;
users, user_roles, market_managers, program_markets, programs
I can export the data from mongoDB in CSV format and construct relational tables and run the following SQL statement to retrieve the data I need;
select u.name, ur.type, pm.code, p.program_name, p.shortnam, p.enddate, p.description
from users u, user_roles ur, market_managers mm, program_markets pm, programs p
where u.roles_0 = ur.id
and ur.type = 'CountryManager'
and u.id = mm.userid
and pm.programid = mm.programid
and pm.id = mm.marketid
and p.id = pm.programid;
How can I achieve the same results by writing native mongo syntax?
Here's a list of collections in scope:
users collection
{
"_id" : ObjectId("5b3f59c96e1c20d84e2b5ce5"),
"name" : "some_country_manager",
"roles" : [
"5b2a8df52b3a6f945d4e85fe"
]
}
user_roles collection
{
"_id" : ObjectId("5b430f9981f6a7382a24995b"),
"type" : "countryManager",
"name" : "Country Manager"
}
market_managers collection
{
"_id" : ObjectId("5894bcf60418700b70745fc9"),
"programId" : "5862c1d43b1a1b113a8a841f",
"marketId" : "5862c1d43b1a1b113a8a84a9",
"userId" : "5b3f59c96e1c20d84e2b5ce5"
}
program_markets collection
{
"_id" : ObjectId("5b43588689c117241c171e8c"),
"code" : "de",
"startDate" : "2018-07-09",
"endDate" : "2019-07-09"
}
programs collection
{
"_id" : ObjectId("5862c1d43b1a1b113a8a841f"),
"name" : "Test Program",
"shortname" : "TestP",
"status" : "planned",
"startDate" : "2018-07-09",
"endDate" : "2019-07-09",
"description" : "Test Program"
}
You can try below aggregation if you have mongodb 3.6 and above
Users.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(id.id) } },
{ "$lookup": {
"from": UserRoles.collection.name,
"let": { "roles_0": "$roles_0" },
"pipeline": [
{ "$match": {
"$expr": { "$eq": [ "$_id", "$$roles_0" ] },
"type": "CountryManager"
}}
],
"as": "role"
}},
{ "$lookup": {
"from": MarketManagers.collection.name,
"let": { "user_id": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$user_id" ] }}},
{ "$lookup": {
"from": Programs.collection.name,
"let": { "programid": "$programid" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$programid" ] }}}
],
"as": "programs"
}},
{ "$lookup": {
"from": ProgramManagers.collection.name,
"let": { "marketId": "$marketId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$marketId" ] }}},
{ "$lookup": {
"from": Programs.collection.name,
"let": { "id": "$id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$id" ] }}}
],
"as": "programs"
}}
],
"as": "programManagers"
}}
],
"as": "marketManagers"
}}
])
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.