Mongodb match on a lookup table not working - mongodb

How can I match a condition on a column that belongs to a joined table?
For eg:
Consider two collections A and B:
Collection A:
id|name|status
++++++++++++++
1 |Rock|1
2 |Sam |1
3 |Jack|1
Collection B:
id|userId| loc | status
+++++++++++++++++++++++++++++++++++
1| 1 |[11.111,22.321] | -1
2| 1 |[16.22,33.213] | 1
3| 2 |[334.11,242.321]| 1
4| 3 |[1.111,224.321] | 1
I want to get all users with their current location based on status field in collection B, that is user's current location will have status 1 in B and previous location have status -1.
So, the result I want is like:
+++++++++++++++++
id:1, name: Rock, status: 1, userLocTable: [{id:2, userId:1, loc: [16.22,33.213], status: 1}]
What I am doing is:
db.collectionA.aggregate([
{
$lookup: {
from: collectionB,
localField: id,
foreignField: userId,
as: userLocTable
}
},
{
$match:{
userLocTable.status: 1
}
}
])
But I am getting all rows(both status 1 and -1) from the lookup table (B) instead of only records with status 1. What could be the problem. Any help wuld be appreciated. Thanks!

Try this one,
db.collectionA.aggregate([
{
"$lookup": {
"from": collectionB,
"localField": id,
"foreignField": userId,
"as": userLocTable
}
},
{
"$addFields": {
"userLocTable": {
"$filter": {
"input": "$userLocTable",
"as": "userLoc",
"cond": {
"$eq": [ "$$userLoc.status", "1" ]
}
}
}
}
}
])

Related

How to write a query that will return the count of objects in table 2 that have a certain field matching table 1's ID in MongoDB Atlas?

I have two tables, Users and Rides.
I want to return the first and last name of all Users that have 0 rides in the Rides table.
A User object is set up with the following fields:
_id:
StravaConnect:Object {
AthleteID: 123
}
FirstName:
LastName:
A Ride object is set up with the following fields:
_id:
AthleteID: 123
Length:
How can I aggregate this data to show a list of all users where there are 0 rides where Ride.AthleteID == User.StravaConnect.AthleteID?
Note: I am using MongoDB Compass
Try this pipeline:
$lookup to get users' rides
$match documents where $size of rides array if greater than 0
$project required fields
[
{
"$lookup": {
"from": "rides",
"localField": "StravaConnect.AthleteID",
"foreignField": "AthleteID",
"as": "rides"
}
},
{
"$match": {
$expr: {
"$gt": [
{
"$size": "$rides"
},
0
]
}
}
},
{
"$project": {
"FirstName": 1,
"LastName": 1
}
}
]
Mongo Playground

MongoError: PlanExecutor error during aggregation

I have tree records in mongodb but there could be many more, I'm getting shops by an ID coming from frontend
I need to get 20 records and group them by itemId and colorId, and get counts for every shop. the count of shops can be 1,2,3,....10etc..
this is output I need:
+--------+----------+-------+-------+-------+
| itemId | colorId | shop1 | shop2 | shop3 |
+========+==========+=======+=======+=======+
| 1 | colorId1 | 5 | 0 | 3 |
+--------+----------+-------+-------+-------+
| 2 | colorId2 | 3 | 0 | 0 |
+--------+----------+-------+-------+-------+
| 3 | colorId2 | 0 | 3 | 0 |
+--------+----------+-------+-------+-------+
| 2 | colorId1 | 0 | 5 | 0 |
+--------+----------+-------+-------+-------+
| 3 | colorId1 | 0 | 0 | 5 |
+--------+----------+-------+-------+-------+
here is my data and query - here shopId is string and it's work good.
but when I use this query on my local mashine, I'm getting this error:
MongoError: PlanExecutor error during aggregation :: caused by :: $arrayToObject requires an object with keys 'k' and 'v', where the value of 'k' must be of type string. Found type: objectId
but when I change shopId to the ObjectId I'm getting error.
ObjectId versoin
Per your request in the comments (if I got it right):
db.collection.aggregate([
{
"$match": {}// <-- Highly recommend you to use match due to the complexity of this query
},
{
$group: {
_id: 0,
data: {
$push: {
shopId: "$shopId",
shopItems: "$shopItems"
}
},
shopIds: {
"$push": {
shopId: "$shopId",
"count": 0
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data.shopItems"
},
{
$group: {
_id: {
itemId: "$data.shopItems.itemId",
colorId: "$data.shopItems.colorId"
},
data: {
$push: {
shopId: "$data.shopId",
count: "$data.shopItems.itemCount"
}
},
existing: {
$push: {
shopId: "$data.shopId",
"count": 0
}
},
shopIds: {
$first: "$shopIds"
}
}
},
{
"$addFields": {
"missing": {
"$setDifference": [
"$shopIds",
"$existing"
]
}
}
},
{
$project: {
data: {
$concatArrays: [
"$data",
"$missing"
]
}
}
},
{
$unwind: "$data"
},
{
$sort: {
"data.shopId": 1
}
},
{
$group: {
_id: "$_id",
counts: { // here you can change this key
$push: "$data"
},
totalCount: {
$sum: "$data.count" // if you want it
}
}
}
])
After the first $match, we $group in order to get all shopIds in each document.
Next we $unwind and $group by the group you wanted: by colorId and itemId. Then we are adding all the shops with count 0 and removing the ones that do have actual count. Last three steps are just for sorting, summing and formating.
You can play with it here.

how to connect multiple level collection in mongodb using aggregate?

i have 3 collection
vehicleModel
{
id_vehicleModel: {type:Number}
vehicleModelName: {type:String}
}
vehicleModel_vehicleBodyType
{
id_vehicleModel: {type:Number}
id_vehicleBodyType: {type:Number}
}
vehicleBodyType
{
id_vehicleBodyType: {type:Number}
vehicleBodyTypeName: {type:String}
}
i want to join that 3 collection so i can get result
{
id_vehicleModel: 1
vehicleModelName: "Tesla"
VModel_VBodyType: {
id_vehicleModel: 1,
id_vehicleBodyType: 4
VBodyType: {
id_vehicleBodyType: 4
vehicleBodyTypeName: "Sport"
}
}
}
i've try to aggregate with
schemaVehicleModel.aggregate([
{
$lookup: {
from: "vehicleModel_vehicleBodyType",
localField: "id_vehicleModel",
foreignField: "id_vehicleModel",
as: "VModel_VBodyType",
},
},
{ $unwind: "$VModel_VBodyType" },
])
but how can i aggregate the next collection?
You can refer to sample here: https://mongoplayground.net/p/np_4eGhIM3C
Your aggregation would look like below:
db.vehicleModel.aggregate([
{
"$lookup": {
"from": "vehicleModel_vehicleBodyType",
"localField": "id_vehicleModel",
"foreignField": "id_vehicleModel",
"as": "vehicleModel_vehicleBodyType_Join"
}
},
{
"$unwind": "$vehicleModel_vehicleBodyType_Join"
},
{
"$lookup": {
"from": "vehicleBodyType",
"localField": "vehicleModel_vehicleBodyType_Join.id_vehicleBodyType",
"foreignField": "id_vehicleBodyType",
"as": "vehicleBodyType_Join"
}
},
{
"$unwind": "$vehicleBodyType_Join"
}
])
Aggregation stages:
lookup: to join vehicleModel with vehicleModel_vehicleBodyType based on field id_vehicleModel
unwind: to convert joined array objects to objects
lookup: to join result with vehicleBodyType based on field id_vehicleBodyType
unwind: to convert joined array objects to objects
you can add project to get the desired fields in output.
Please note it does not matter how many lookups you want to do key here is you should now "localField" & "foreignField" and then just do the lookup the way you do for any first lookup stage

mongodb 2 level aggregation [duplicate]

This question already has answers here:
MongoDB nested lookup with 3 levels
(2 answers)
Closed 2 years ago.
I have 3 collections that are referred to each other such that
A -> B -> C
I want to filter data after matching that with B and from B to C
Collection 1
Products: [{
_id:ObjectId(),
name:"product1",
productCatalogue:[reference to productCatalogue collection]
},....]
Collection 2
productCatalogue: [{
_id:ObjectId(),
name:"catelgoue1",
category:{
cat:[reference to category table],
sub1:[reference to category table],
sub2:[reference to category table]
}
},...]
Collection 3
category: [{
_id:ObjectId(),
name:"cat1",
type:"parent"
},....]
I want to filter data such that products having catalog:catelgoue1 and category: cat1 will be filtered using aggregation.
you can using before every lookup for $match
example code:
const orderProcess = await Order.aggregate([
{
$match: {
userId: userId
}
},
{
$lookup: {
from: "products",
localField: "product_id",
foreignField: "_id",
as: "product",
}
},
{
$unwind: "$product"
},
{
$lookup: {
from: "prices",
localField: "product_id", // $product._id => like top lookup data
foreignField: "productId", // $product._id => like top lookup data
as: "price"
},
},
{
$unwind: "$price"
},
{
$project:{
// response all item
}
}])

How to get left join data using multiple condition in mongodb

I have two table
master_document_table document_table
id | title id | master_document_id | userId
1 | Profile
select * from master_document_table as md
left join document_table as d on md.id = d.master_document_id and d.userId = 2
Result:
id | title | master_document_id | userId
1 | Profile null null
how can this be achieved using mongodb i have tried & also did some research from stack overflow and did not got the expected result.
From mongo v3.2, it is possible to aggregate two or more than two collections.
But here one thing, you need to know is, you won't get the same structure as sql. You will get your data on nested document.
Your tables will look something like this in mongodb:
master_document_table (collection / table)
{
_id: ObjectId("5f28d53c00613e6ada45f702"),
title: "Profile",
}
document_table (collection / table)
{
_id: ObjectId("5f28d53c00613e6ada45e790"),
master_document_id: ObjectId("5f28d53c00613e6ada45f702"), // master_doc_table's ref id.
userId: ObjectId("5f28d53c00613e6ada327f231"), // user's ref id.
}
Your query will be:
db.document_table.aggregate([
{$match: {title: "Profile"}}, // you can give any condition here.
{
$lookup:
{
from: "master_document_table",
localField: "master_document_id",
foreignField: "_id",
as: "master_document_id"
}
}
])
Your query result will look something like:
[{
_id: ObjectId("5f28d53c00613e6ada45e790"),
master_document_id: {
_id: ObjectId("5f28d53c00613e6ada45f702"),
title: "Profile"
},
userId: ObjectId("5f28d53c00613e6ada327f231"),
}]
Now you can restructure it based on your requirement.
I would like to recommend you
https://docs.mongodb.com/manual/reference/operator/aggregation/match/
&
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
article from mongodb docs.
Hope this will help to resolve your problem.
Inorder to join two tables you can use the $lookup aggregation pipeline operator in version 3.6 and newer. Since you need the results like LEFT OUTER JOIN, you have to $unwind the array to objects and filter out the desired results using $match conditions (ie document_table.userId = 2). Dont forget to use preserveNullAndEmptyArrays to true when unwinding.
Sample code for reference:
db.master_document_table.aggregate([
{ "$lookup": {
"from": "document_table",
"localField": "id",
"foreignField": "master_document_id",
"as": "document_table"
}},
{ $unwind: { path: "$document_table", preserveNullAndEmptyArrays: true } },
{
$match:{
$or:[
{ "document_table": { $exists: false } },
{"document_table.userId" : 2 }
]
}
}
])
I some how managed to get desire result thanks for the help
db.master_document_table.aggregate([
{
$lookup: {
from: 'document_table',
localField: 'id',
foreignField: 'master_document_id',
as: 'users'
}
},
{
$project: {
title: 1,
users: {
$filter: {
input: '$users',
as: 'user',
cond: {
$eq: ['$$user.userId', 2]
}
}
}
}
}
]);