How to write a $match on a $lookup document in MongoDB - mongodb

I have two MongoDB collections that look like this:
Products Specials
---------- ----------
_id _id
name product_id
country zip
price percent_discount
out_of_stock
I'm using GraphQL as well so I wrote up an aggregate pipeline that returns the data in this structure:
specials {
_id
product {
_id
name
country
price
}
zip
percent_discount
out_of_stock
}
This aggregate pipeline I wrote works great and looks like this:
let response = await Specials.aggregate([
{
$lookup: {
from: 'products',
localField: 'product_id',
foreignField: '_id',
as: 'product'
}
},
{
$unwind: '$product'
},
{
$match: {
zip: zip
}
}
])
return response;
Now I'm trying to add a filter into this. The filter should match the name or country in the product collection preferably with regex. So I tried writing something like this but it's yielding over 8000 results when there should be only 2-3:
let response = await Specials.aggregate([
{
$match: { zip: zip }
},
{
$lookup: {
from: "products",
let: { product_id: "$product_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{
_id: "$$product_id"
},
{
$or: [
{
name: filter
},
{
country: filter
}
]
}
]
}
}
}
],
as: "product"
}
},
{
$unwind: "$product"
}
])

If you're using Mongo version 4.2+ you can use $regexMatch
let response = await Specials.aggregate([
{
$lookup: {
from: "products",
let: { product_id: "$product_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: ["$$product_id", "$_id"]
},
{
$or: [
{
$regexMatch: { input: "$name", regex: filterRegex }
},
{
$regexMatch: { input: "$country", regex: filterRegex }
}
]
}
]
}
}
}
],
as: "product"
}
},
{
$unwind: "$product"
},
{
$match: {
$and: [
{
zip: zip
}
]
}
}
])
return response;

This is the following solution that I was able to get working:
let response = await Specials.aggregate([
{
$lookup: {
from: "products",
let: { product_id: "$product_id" },
pipeline: [
{
$match: {
$and: [
{
$or: [
{
name: { $regex: filter }
},
{
country: { $regex: filter }
}
]
},
{
$expr: {
$eq: ["$$product_id", "$_id"]
}
}
]
}
}
],
as: "product"
}
},
{
$unwind: "$product"
},
{
$match: {
$and: [
{
zip: zip
}
]
}
}
])
return response;
** Updated with the working $regex implementation

Related

MongoDB aggregate match by array

is there some way to match by an array, using aggregation on mongodb?
I have the following structure:
students
{
_id: 67264264726476,
name: "Student 1",
email: "student1#email.com"
}
followups
{
_id: 65536854685,
student: 67264264726476,
message: "This is a student follow up"
}
I've made the following query:
const followups = await this.modelFollowup.aggregate([
{
$lookup: {
from: 'students',
localField: 'student',
foreignField: '_id',
as: 'student',
},
},
{ $unwind: { path: '$student' } },
{
$match: {
$or: [
{ 'student.name': { $regex: term, $options: 'i' } },
{ 'student.email': { $regex: term, $options: 'i' } },
],
},
},
]);
How can I make it possible to match using an array, instead of the term variable?
If you want to match the complete list of student names and email you can check this query
Query
db.followups.aggregate([
{
$lookup: {
from: "students",
localField: "student",
foreignField: "_id",
as: "student",
},
},
{
$unwind: {
path: "$student"
}
},
{
$match: {
$or: [
{
"student.name": {
"$in": [
"abc",
"def"
]
}
},
{
"student.email": {
"$in": [
"xyz#email.com"
]
}
},
],
},
},
])
Here is the link to the playground to check the query

MongoDb return records only if $lookup cond is occur

I have a class model which has field ref.
I'm trying to fetch only records that match the condition in lookup.
so what i did:
{
$lookup: {
from: 'fields',
localField: "field",
foreignField: "_id",
as: 'FieldCollege',
},
},
{
$addFields: {
"FieldCollege": {
$arrayElemAt: [
{
$filter: {
input: "$FieldCollege",
as: "field",
cond: {
$eq: ["$$field.level", req.query.level]
}
}
}, 0
]
}
}
},
The above code works fine and returning the FieldCollege if the cond is matched.
but the thing is, i wanted to return the class records only if the FieldCollege is not empty.
I'm totally new to mongodb. so i tried something like this:
{
$match: {
'FieldCollege': { $exists: true, $ne: [] }
}
},
Obv this didn't work.
does mongodb support something like this or am i complicating things?
EDIT:
the result from the above code:
"Classes": [
{
"_id": "613245664c6ea614e001fcef",
"name": "test",
"language": "en",
"year_cost": "3232323",
"FieldCollege":[] // with $unwind
}
],
expected Result:
"Classes": [
// FieldCollege is empty
],
I think the good option is to use lookup with pipeline, and see the final version of your query,
$lookup with fields collection and match your both conditions
$limit to result one document
$match FieldCollege is not empty []
$addElemAt to get first element from result FieldCollege
[
{
$lookup: {
from: "fields",
let: { field: "$field" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $eq: ["$$field", "$_id"] } },
{ level: req.query.level }
]
}
},
{ $limit: 1 }
],
as: "FieldCollege"
}
},
{ $match: { FieldCollege: { $ne: [] } } },
{
$addFields: {
FieldCollege: { $arrayElemAt: ["$FieldCollege", 0] }
}
}
]

Mongo Db How to detect error in pipeline?

I need to join two collection with two conditions.
products:
{
...
sku: "4234",
organizationId: ObjectId("asdasdasd);
...
};
order_history:
{
...
itemSku: "4234",
organizationId: ObjectId("asdasdasd);
...
}
I chose pipeline approach:
$lookup: {
from: 'order_history',
let: { foreign_sku: "$itemSku", foreign_organizationId: "$organizationId" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$organizationId", "$$foreign_organizationId"] },
{ $eq: ["$sku", "$$foreign_sku" ] }
]
}
}
}
],
as: 'order_history'
}
I wrote it on base of Mongo Documentation, but my conditions are ignored.
I have scalar multiplication in result. Where is my mistake?
I already mention in the comment and the code is below
db.products.aggregate([
{
$lookup: {
from: "order_history",
let: {
foreign_sku: "$sku",
foreign_organizationId: "$organizationId"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$organizationId",
"$$foreign_organizationId"
]
},
{
$eq: [
"$itemSku",
"$$foreign_sku"
]
}
]
}
}
}
],
as: "order_history"
}
}
])
Working Mongo playground

I need left anti join in mongodb with lookup and match aggregate function

I want to convert this query in MongoDB
Select * from Question where not exists (select * from Solved_question where questionid = id and username=username)
Collections Schema
question:{
_id:ObjectId,
title:String,
desc:string,
Author:string
},
User:{
_id:ObjectId,
Email:string,
Password:string
},
Solvedquestioncollection:{
Id:_id,
QuestionId:{ type:mongoose.Schema.ObjectId, ref:"Question" },
UserId:{ type:mongoose.Schema.ObjectId, ref:"User" }
}
Sample Document
Question:[
{
_id:ObjectId('1'),
title:"main component of computer",
desc:"some desc for this"
author:"ashick"
},
{
_id:ObjectId('2'),
title:"Advantage of CPU",
desc:"some desc for this"
author:"ashick"
},
]
User:[
{
_id:ObjectId('1'),
email:"as#g.com",
password:"12345"
},
{
_id:ObjectId('2'),
email:"df#g.com",
password:"345"
}
]
solvedquestion:[
{
_id:ObjectId('1'),
question:ObjectId('2'),
userId:ObjectId('1')
},
{
_id:ObjectId('2'),
question:ObjectId('2'),
userId:ObjectId('2')
}
]
I want to Fetch the Question in Question Collection Which does not solved by a particular user
I try this
question = await Question.aggregate([
{
$lookup: {
from: "solvedquestions",
let: { userId: "$userId" },
pipeline: [
{
$match: {
$expr: {
$eq: ["ObjectId('60ebc6b9980b8e1f8cffe34b'"), "$$userId"],
},
},
},
],
as: "resultingArray",
},
},
]);
but its Return the Empty Array
Thank you for your answer
try {
let aggregate = Question.aggregate([
{
$lookup: {
from: 'solvedquestions',
let: {
question_id: '$_id',
question_userId: '$user'
},
pipeline: [ {
$match: {
$expr: {
$and: [
{
$eq: ['$QuestionId', '$$question_id']
},
{
$eq: ['$userId', '$$question_userId']
}
]
}
}
}],
as: 'results'
}
}
]);
return await aggregate.exec();
} catch (error) {
console.error('Error ->',error);
}
I hope this helps, Let me know if you have any doubt. Happy to help :)

Full Outer join in MongoDB

I want to do a Full Outer Join in MongoDB by lookup mongoDB query. Is this possible? Is a Full Outer Join supported by MongoDB by any other alternative?
[Update:]
I want to achieve result from Collection1 & Collection2 as following attachment:
Example: Result Required
In above result column there may be different arithmetic operations and will be further used in calculations.
You can use $unionWith (starting 4.4)
Something like this:
db.c1.aggregate([
{$set: {
mark1: "$marks"
}},
{$unionWith: {
coll: 'c2',
pipeline: [{$set: {mark2: "$marks"}}]
}},
{$group: {
_id: "$name",
result: {
$sum: "$marks"
},
mark1: {$first: {$ifNull: ["$mark1", 0]}},
mark2: {$first: {$ifNull: ["$mark2", 0]}}
}}])
I have named the collections as coll1 and coll2 then just use this query it will give you the required output.
db.getCollection('coll1').aggregate([
{
$facet: {
commonRecords: [{
$lookup: {
from: "coll2",
localField: 'name',
foreignField: 'name',
as: "coll2"
}
},
{
$unwind: {
path: '$coll2',
preserveNullAndEmptyArrays: true
}
}
]
}
},
{
$lookup: {
from: "coll2",
let: {
names: {
$map: {
input: '$commonRecords',
as: 'commonRecord',
in: '$$commonRecord.name'
}
}
},
pipeline: [{
$match: {
$expr: {
$eq: [{
$indexOfArray: ['$$names', '$name']
}, -1]
}
}
}, ],
as: "coll2"
}
},
{
$addFields: {
coll2: {
$map: {
input: '$coll2',
as: 'doc',
in: {
coll2: '$$doc'
}
}
}
}
},
{
$project: {
records: {
$concatArrays: ['$commonRecords', '$coll2']
}
}
},
{
$unwind: '$records'
},
{
$replaceRoot: {
newRoot: '$records'
}
},
{
$project: {
_id: 0,
name: {
$ifNull: ['$name', '$coll2.name']
},
marks1: {
$ifNull: ['$marks', 0]
},
marks2: {
$ifNull: ['$coll2.marks', 0]
}
}
},
{
$addFields: {
result: {
$add: ['$marks1', '$marks2']
}
}
}
])
This is a sample:
{
$lookup:
{
from: [collection to join],
local_Field: [field from the input documents],
foreign_Field: [field from the documents of the "from" collection],
as: [output field]
}
}
show this link