Aggregate return empty array with custom variable - mongodb

Here is a link to understand the structure : https://mongoplayground.net/p/TUsBjiboKLk
I'm basically looking for the communities that the user is following which work just fine in mongoplayground.
But replacing the "userId1" with a custom javascript variable is not working.
pipeline: [
{
$match: {
userId: "userId1", // <================
$expr: {
$eq: [
"$userId",
"$$userId"
],
},
},
},
],
pipeline: [
{
$match: {
userId: myCustomVarible, // <================
$expr: {
$eq: [
"$userId",
"$$userId"
],
},
},
},
],
Here is the full code for further explanation:
app.get("/communities/following", (req, res) => {
const userId = req.query.userId;
Community.aggregate(
[
{
$lookup: {
from: "follows",
localField: "communityId",
foreignField: "communityId",
as: "follows",
},
},
{
$unwind: "$follows",
},
{
$lookup: {
from: "users",
let: {
userId: "$follows.userId",
},
pipeline: [
{
$match: {
userId: userId,
$expr: {
$eq: ["$userId", "$$userId"],
},
},
},
],
as: "users",
},
},
{
$unwind: "$users",
},
{
$project: {
name: 1,
avatar: 1,
},
},
],
(err, data) => {
if (err) res.status(500).send(err);
else res.status(201).send(data);
}
);
});
If i replace const userId = req.query.userId; width const userId = "myUserId"; it's working

Ok, i found the problem, i was testing this using :
http://localhost:9000/communities/following?userId="7arvEbQx3wVttqnbwmOionRsPBz1"
where it should be without the quotes
http://localhost:9000/communities/following?userId=7arvEbQx3wVttqnbwmOionRsPBz1

Related

Mongo multi-level lookup pipeline variable access

I'm not sure this has been asked before, I tried looking for similar questions here on SO but unfortunately I don't find anything - perhaps for a lack of better googling skills or a lack of better wording on my part.
I have a multi-lookup query that looks like this:
const auction_id = "94a3cfb7b05c73fb5e746e21";
const record = await AuctionModel.aggregate([
{ $match: { _id: new Types.ObjectId(auction_id) } },
{
$lookup: {
from: "lots",
as: "lots",
let: { LOTID: "$_id" },
pipeline: [
{
$match: { auction_id: new Types.ObjectId(auction_id) },
},
{
$lookup: {
from: "bids",
as: "bids",
pipeline: [
{
$match: { lot_id: "$$LOTID" },
},
],
},
},
],
},
},
]);
I want to access that variable LOTID inside the inner pipeline.
Is this possible? I also tried this:
{
$lookup: {
from: "lots",
...
let: { LOTID1: "$_id" },
pipeline: [
...
{
$lookup: {
from: "bids",
...
let: { LOTID2: "$$LOTID1" },
pipeline: [
{
$match: { lot_id: "$$LOTID2" },
},
],
},
},
],
},
},
I also read here in the Mongo Docs:
https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#std-label-lookup-concise-correlated-subquery-let
A $match stage requires the use of an $expr operator to access the variables. The $expr operator allows the use of aggregation expressions inside of the $match syntax.
{
$lookup: {
from: "lots",
...
let: { LOTID1: "$_id" },
pipeline: [
...
{
$lookup: {
from: "bids",
...
let: { LOTID2: "$$LOTID1" },
pipeline: [
{
$match: { $expr: { $eq: ["$lot_id", "$$LOTID1"] } },
},
],
},
},
],
},
},
Also not working.
Is there any other way? Any assistance would be greatly appreciated!

MongoDB conditional $lookup with aggregration framework

I'm trying to do a conditional lookup with aggregration framework. In my local and foreign collections I have two fields: fieldA and fieldB. If fieldA != 0 my $lookup should be:
{ from: 'collectionA', localField: 'fieldA', foreignField: 'fieldA', as: 'agg' }
Otherwise, if fieldA = 0 my $lookup should be:
{ from: 'collectionA', localField: 'fieldB', foreignField: 'fieldB', as: 'agg' }
Is it possible to combine these conditions with a single $lookup?
It's not really possible OOB, but you can work around this in multiple ways.
for example add a new "temporary" field based on this condition:
db.colleciton.aggregate([
{
$lookup: {
from: 'collectionA',
let: {
fieldA: "$fieldA", fieldB: "$fieldB"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
$cond: [
{
$eq: [
'$$fieldA',
0,
],
},
'$$fieldB',
'$$fieldA',
],
},
{
$cond: [
{
$eq: [
'$$fieldA',
0,
],
},
'$fieldB',
'$fieldA',
],
}
],
},
},
},
],
as: 'agg',
},
}
])
The issue with this approach is that indexes won't be utilized for the lookup for older Mongo versions, which in some cases can be crucial.
You can work around for performance purposes like so:
db.collection.aggregate([
{
$facet: {
one: [
{
$match: {
fieldA: { $ne: 0 },
},
},
{
$lookup: { from: 'collectionA', localField: 'fieldA', foreignField: 'fieldA', as: 'agg' },
},
{
$match: {
'agg.0': { $exists: true },
},
},
],
two: [
{
$match: {
fieldA: { $eq: 0 },
},
},
{
$lookup: { from: 'collectionA', localField: 'fieldB', foreignField: 'fieldB', as: 'agg' },
},
{
$match: {
'agg.0': { $exists: true },
},
},
],
},
},
{
$addFieldS: {
combined: {
$concatArrays: [
'$one',
'$two',
],
},
},
},
{
$unwind: '$combined',
},
{
$replaceRoot: {
newRoot: "$combined"
},
},
]);
While there is some overhead here it will still work faster than an unindexed lookup.

MongoDB join/lookup not working when using a pipeline

using a join in MongoDb I can get the values I want
(await this.ideasCollection).aggregate([
{
$match: { userId, deleted: { $ne: true } },
},
{
$lookup: {
from: 'teams',
localField: '_id',
foreignField: `ideas.daily.ideaId`,
as: 'teams',
},
},
])
but when I try to use a pipeline in order to get fewer fields(using $project) it doesn't work
(await this.ideasCollection).aggregate([
{
$match: { userId, deleted: { $ne: true } },
},
{
$lookup: {
from: 'teams',
let: { ideaId: '$_id' },
pipeline: [
{ $match: { $expr: { $eq: ['$ideas.daily.ideaId', '$$ideaId'] } } },
{ $project: { _id: 1, name: 1 } },
],
as: 'teams',
},
},
])
Is there some kind of trick to make the second option work?
Here's the structure of teams
{
"_id": { "$oid": "58948652ab5f580010faeb44" },
"name": "My team",
"ideas": {
"daily": [
{ "ideaId": { "$oid": "5b6d913e3e552800260904e1" }, "ordinal": 0 },
{ "ideaId": { "$oid": "5bbbc93459914f0013e3c522" }, "ordinal": 1 },
]
}
}
Thank you

$lookup to iterate specific Fields

I am trying to join specific fields from product while performing all request in addtocart. I don't know how to update lookup for this requirement. below I have updated my product collection and add to cart collection. Can anyone suggest me how to do this?
Add to Cart Collection:
add_to_cart_products: [
{
product: ObjectId('5f059f8e0b4f3a5c41c6f54d'),
product_quantity: 5,
product_item: ObjectId('5f4dddaf8596c12de258df20'),
},
],
add_to_cart_product_total: 5,
add_to_cart_discount: 50,
Product Collection:
{
_id: ObjectId('5f059f8e0b4f3a5c41c6f54d'),
product_name: 'La Gioiosa Prosecco',
product_description: 'No Description',
product_overview: 'No Overview',
product_items: [
{
product_item_number: '781239007465',
product_price: 14.99,
product_images: ['pro03655-781239007465-1.png'],
product_item_is_active: true,
_id: ObjectId('5f4dddaf8596c12de258f021'),
},
{
product_item_number: '850651005110',
product_price: 12.99,
product_images: ['default.png'],
product_item_is_active: true,
_id: ObjectId('5f4dddaf8596c12de258df20'),
},
],
product_created_date: ISODate('2020-07-08T10:29:05.892Z'),
product_status_is_active: true,
},
In my AddToCart Schema Lookup
lookups: [
{
from: 'shop_db_products',
let: { productId: '$add_to_cart_products.product', purchaseQuantity: '$add_to_cart_products.product_quantity' },
pipeline: [
{
$match: { $expr: { $in: ['$_id', '$$productId'] } },
},
{
$lookup: {
from: 'shop_db_products',
localField: 'product_id',
foreignField: '_id',
as: 'product',
},
},
{
$project: {
product_id: '$$productId',
product_purchase_quantity: '$$purchaseQuantity',
product_name: true,
},
},
{
$unwind: '$product_id',
},
{
$unwind: '$product_purchase_quantity',
},
],
as: 'add_to_cart_products',
model: 'ProductModel',
},
],
Current Result:
"add_to_cart_products": [
{
"product_name": "Avery Coconut Porter",
"product_id": "5f059f8e0b4f3a5c41c6f54d",
"product_purchase_quantity": 5
}
],
"add_to_cart_product_total": 5,
"add_to_cart_discount": 50,
Expected Result:
"add_to_cart_products": [
{
"product_name": "Avery Coconut Porter",
"product_id": "5f059f8e0b4f3a5c41c6f54d",
"product_item":[
"product_price": 12.99,
"product_images": ["default.png"],
],
"product_purchase_quantity": 5
}
],
"add_to_cart_product_total": 5,
"add_to_cart_discount": 50,
You can try,
$unwind deconstruct add_to_cart_products array
$lookup with shop_db_products collection pass required fields in let
$match productId equal condition
$project to show required fields, and get product item from array product_items using $filter to match product_item_id, and $reduct to get specific fields from product_item
$unwind deconstruct add_to_cart_products array
$group by _id and get specific fields and construct add_to_cart_products array
db.add_to_cart.aggregate([
{ $unwind: "$add_to_cart_products" },
{
$lookup: {
from: "shop_db_products",
let: {
productId: "$add_to_cart_products.product",
purchaseQuantity: "$add_to_cart_products.product_quantity",
product_item_id: "$add_to_cart_products.product_item"
},
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$productId"] } } },
{
$project: {
product_name: 1,
product_id: "$_id",
product_purchase_quantity: "$$purchaseQuantity",
product_item: {
$reduce: {
input: {
$filter: {
input: "$product_items",
cond: { $eq: ["$$product_item_id", "$$this._id"] }
}
},
initialValue: {},
in: {
product_price: "$$this.product_price",
product_images: "$$this.product_images"
}
}
}
}
}
],
as: "add_to_cart_products"
}
},
{ $unwind: "$add_to_cart_products" },
{
$group: {
_id: "$_id",
add_to_cart_discount: { $first: "$add_to_cart_discount" },
add_to_cart_product_total: { $first: "$add_to_cart_product_total" },
add_to_cart_products: { $push: "$add_to_cart_products" }
}
}
])
Playground

How do I include a document that has a field is null in aggragation framework in mongodb?

I have a problem, where I search for documents using Aggregation Framework, and one document has a field "place" set to null. As such, it is not returned from the database with my current code. My code is:
router.get("/search", async (req, res) => {
const items = await Item.aggregate([
{
$lookup: {
from: "places",
localField: "place",
foreignField: "_id",
as: "placeData",
},
},
{
$unwind: "$placeData",
},
{
$match: {
$or: [
{ title: new RegExp(req.query.searchQuery, "i") },
{ sku: new RegExp(req.query.searchQuery, "i") },
{ "place.title": new RegExp(req.query.searchQuery, "i") },
],
},
},
{
$project: {
title: 1,
sku: 1,
place: {
$ifNull: ["$placeData", "Digital"],
},
},
},
]);
res.send(items);
});
I figured it out. I tweaked the "unwind" stage a bit, as follows:
{
$unwind: {
path: "$placeData",
preserveNullAndEmptyArrays: true,
},
},