MongoDB Aggregate - Match documents with id and give result which id not exist on collection B - mongodb

Collection A:
[{
"_id": 1,
"operation":"SEC",
"name":"x"
},{
"_id": 2,
"operation": "SEC",
"name": "y"
},
{
"_id": 3,
"operation": "SEC",
"name": "z"
}]
Collection B:
[{
"user": 1,
"operation":"SEC",
"name":"x",
"date": "2022-10-25"
},{
"user": 2,
"operation":"SEC",
"name":"y",
"date": "2022-10-25"
}
]
Expected output:
[
{
"_id": 3,
"operation": "SEC",
"name": "z"
}
]
I have two collections and I want to match from the first collection to the second collection by date and want to get only those that are not in the second collection.

You can use the following aggregation pipeline in order to achieve your desired outpu:
[
{
"$lookup": {
"from": "collectionB",
"localField": "_id",
"foreignField": "user",
"as": "collectionB"
}
},
{
$match: {
collectionB: {
$size: 0
}
}
},
{
$project: {
collectionB: 0
}
}
]
Please note that this is an efficient solution. You probably should add a $match step at the beginning in order to limit your results.

Related

mongodb $lookup with findOne mode

I want to join two mongodb collections, collectionA and collectionB.
For each document in collectionA I want to check if exists a coincidence in collectionB.
If I do it in a $lookup, it returns all the documents joined, but I would like the search in collectionB stops as soon as one coincidence ​is found (kind of a mongodb findOne). My concern is the performance, I know I could get just the element 0 from the array.
Is there a way to do it using the mongodB aggregation framework?
Example:
collectionA:
[
{
"_id": 1,
"item": "almonds"
},
{
"_id": 2,
"item": "pecans"
}
]
colectionB:
[
{
"_fid": 1,
"date": "2021-01-10"
},
{
"_fid": 1,
"date": "2021-01-11"
},
{
"_fid": 1,
"date": "2021-01-12"
},
{
"_fid": 2,
"date": "2021-01-03"
}
]
$lookup mongoDb
db.colectionA.aggregate([
{
"$lookup": {
"from": "colectionB",
"localField": "_id",
"foreignField": "_fid",
"as": "matches"
}
}
])
Result
[
{
"_id": 1,
"item": "almonds",
"matches": [
/* I don't want this array, with 1 element would be enough */
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000002"),
"date": "2021-01-10"
},
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000003"),
"date": "2021-01-11"
},
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000004"),
"date": "2021-01-12"
}
]
},
{
"_id": 2,
"item": "pecans",
"matches": [
{
"_fid": 2,
"_id": ObjectId("5a934e000102030405000005"),
"date": "2021-01-03"
}
]
}
]
You can test on this mongo playground.
Thanks in advance
If you're using at least MongoDB 3.6, you can execute an aggregation pipeline on a joined collection. It might look like this:
db.colectionA.aggregate([
{
"$lookup": {
"from": "colectionB",
"as": "matches",
"let": {
"fid": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_fid",
"$$fid"
]
}
}
},
{
"$limit": 1
}
]
}
}
])
Working Mongo playground

How to get count by order in mongodb aggregate?

I have two collections name listings and moods.
listings sample:
{
"_id": ObjectId("5349b4ddd2781d08c09890f3"),
"name": "Hotel Radisson Blu",
"moods": [
ObjectId("507f1f77bcf86cd799439010"),
ObjectId("507f1f77bcf86cd799439011")
]
}
moods sample:
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "Sports"
},
{
"_id": ObjectId("507f1f77bcf86cd799439010"),
"name": "Spanish Food"
},
{
"_id": ObjectId("507f1f77bcf86cd799439009"),
"name": "Action"
}
I need this record.
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "Sports",
"count": 1
},
{
"_id": ObjectId("507f1f77bcf86cd799439010"),
"name": "Spanish Food",
"count": 1
},
{
"_id": ObjectId("507f1f77bcf86cd799439009"),
"name": "Action",
"count": 0
}
I need this type of record. I have no idea about aggregate.
You can do it using aggregate(),
$lookup to join collection listings
$match pipeline to check moods _id in listings field moods array
db.moods.aggregate([
{
"$lookup": {
"from": "listings",
"as": "count",
let: { id: "$_id" },
pipeline: [
{
"$match": {
"$expr": { "$in": ["$$id", "$moods"] }
}
}
]
}
},
$addFields to add count on the base of $size of array count that we got from above lookup
{
$addFields: {
count: { $size: "$count" }
}
}
])
Playground
did this work:
db.collection.aggrate().count()
Try to combine the functions, it might work.

Mongodb many-to-many get related items

I have 3 mongodb collection with many to many relation,
productfeatures collection is the relation collection.
db={
"products": [
{
"id": 1,
"name": "product 1"
}
],
"features": [
{
"id": 101,
"name": "width"
},
{
"id": 102,
"name": "length"
},
{
"id": 103,
"name": "height"
}
],
"productfeatures": [
{
"productId": 1,
"featureId": 101,
"value": "3"
},
{
"productId": 1,
"featureId": 102,
"value": "4"
},
{
"productId": 1,
"featureId": 103,
"value": "5"
}
]
}
I want to get a product's all features like the following, how can this be done?
[
{
"id": 1,
"name": "product 1",
"productfeatures": [
{
"feature": "width",
"value": "3"
},
{
"feature": "length",
"value": "4"
},
{
"feature": "height",
"value": "5"
}
]
}
]
I know how to get the productfeatures with lookup stage:
db.products.aggregate([
{
$match: {
id: 1
}
},
{
$lookup: {
from: "productfeatures",
localField: "id",
foreignField: "productId",
as: "productfeatures"
}
}
])
But I also want to join to the features collection to get the feature name.
https://mongoplayground.net/p/J-20zxxFW5q
You can use below aggregation
db.products.aggregate([
{ "$match": { "id": 1 }},
{ "$lookup": {
"from": "productfeatures",
"let": { "productId": "$id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$$productId", "$productId"] }}},
{ "$lookup": {
"from": "features",
"let": { "featureId": "$featureId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$id", "$$featureId"] }}}
],
"as": "productfeatures"
}},
{ "$project": {
"value": 1,
"feature": { "$arrayElemAt": ["$productfeatures.name", 0] }
}}
],
"as": "productfeatures"
}}
])
MongoPlayground
You can use mongodb aggregation pipeline with multiple $lookup stage to achieve this.
You were going on the right path, You needed to add one $unwind stage(to unwind the result of first lookup stage) and $lookup stage, and finally to group all the results, you would need a final $group stage, to combine all features in one array.
Try this :
db.products.aggregate([
{
$match: {
id: 1
}
},
{
$lookup: {
from: "productfeatures",
localField: "id",
foreignField: "productId",
as: "productfeatures"
}
},{
$unwind : "$productfeatures"
},{
$lookup : {
from : "features",
local : "productfeatures.featureId",
foreignField : "id",
as : "feature"
}
},{
$unwind : "$feature"
},{
$group : {
_id : "$_id",
name : {$first : "$name"},
productfeatures : {$push : "$feature"}
}
}
])
Please read about $group, $unwind, $lookup for more details.

How to combine multiple collections and merge the joined documents into a single document

Here is the problem, I am unable to get the following result. Please look into the piece of json and help me out.
This is my data:
[
{
"user_id": "65asdfksadjfk3u4",
"lat": 23.4343,
"long": 15.2382
}
]
Currently my result is:
[
{
"_id": "65asdfksadjfk3u4",
"name": "Srini",
"age": 26,
"some other key": "some other values"
}
]
I need to get the collection from the user_id and add it to the same array object. As you can notice both lat and long are being removed in my current result.
[
{
"_id": "65asdfksadjfk3u4",
"name": "Srini",
"age": 26,
"some other keys": "some other values",
"lat": 23.4343,
"long": 15.2382
}
]
You can append the $lookup stage to join the current pipeline results with the users collections by the user_id fields and then use $mergeObjects in the $replaceRoot to merge the joined documents from users and the current results:
db.collection.aggregate([
/* current pipeline here */
{ "$lookup": {
"from": "users",
"localField": "_id",
"foreignField": "user_id",
"as": "user"
} },
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
{ "$arrayElemAt": [ "$user", 0 ] },
"$$ROOT"
]
}
} },
{ "$project": { "user": 0, "user_id": 0 } }
]);

How to use mongoose lookup child match? [duplicate]

This question already has an answer here:
Aggregation filter after $lookup
(1 answer)
Closed 5 years ago.
Sorry my English is bad...
How to use mongoose lookup child match?
My query is
categorys.aggregate([{
"$sort": {
"order": 1,
"id": 1
}
},
{
"$lookup": {
"localField": "id",
"from": "categorys",
"foreignField": "parentId",
"as": "child"
}
},
{
"$match": {
see: true,
depth: 1
}
}
]).exec(function(err, Categorys)
is result
{
"_id": "596e237c414f2137b0c4e9c2",
"id": 3,
"name": "DB",
"address": "DB",
"parentId": 0,
"depth": 1,
"see": true,
"__v": 0,
"child": [{
"_id": "596e24701e1bd30dc415b894",
"id": 5,
"name": "Mongodb",
"address": "Mongodb",
"parentId": 3,
"depth": 2,
"see": true,
"__v": 0
},
{
"_id": "596e24821e1bd30dc415b895",
"id": 6,
"name": "mssql",
"address": "mssql",
"parentId": 3,
"depth": 2,
"see": false,
"__v": 0
}
]
}]
I don't look result in mssql (see:false) how about match?
Help me!
You can use $filter in $project stage if you want to match in child array. like bellow.
categorys.aggregate([
{
"$sort": {
"order": 1,
"id": 1
}
},
{
"$lookup": {
"localField": "id",
"from": "categorys",
"foreignField": "parentId",
"as": "child"
}
},
{
$project: {
name: 1,
address: 1,
// ... as you need
child: {
$filter: {
input: "$child",
as: "item",
cond: {
$and: [
{$eq: ["$$item.see", true]},
{$eq: ["$$item.depth", 2]}
]
}
}
}
}
}
])
N.B: If you want to use match condition for parent field like "depth":1,"see":true, then you should use that $match stage before $lookup
categorys.aggregate([
{$match:{//parent field match condition first}},
{$sort:{}},
{$lookup:{}},
{$project:{//for child}}
]);