Getting Mongoose Error: Arguments must be aggregate pipeline operators - node.js

I'm trying to run this aggregations but I cannot get a result with aggregation option parameter, what is wrong ? Here is my aggregation:
var coupons = couponModel.aggregate(
[
{
"$lookup": {
from: "campaigns",
localField: "campaignId",
foreignField: "_id",
as: "campaigns"
}
},
{ "campaignId": { "$in": campaignsIds } },
],
{
allowDiskUse: true,
explain: true
}
);
Also Try Different Structure:
couponModel.aggregate(
[
{
"$lookup": {
from: "campaigns",
localField: "campaignId",
foreignField: "_id",
as: "campaigns"
}
},
{ "campaignId": { "$in": campaignsIds } },
],
function(err,result) {
if (err) return handleError(err);
// Result is an array of documents
console.log(result);
}
)

When using mongoose you cannot pass an object as the second parameter of aggregate(). Instead you must use mongoose functions. Also, in the second object of your pipeline you forgot to specify the aggregation stage. So your code would look like this:
var coupons = couponModel.aggregate(
[
{
"$lookup": {
from: "campaigns",
localField: "campaignId",
foreignField: "_id",
as: "campaigns"
}
},
{ $match: { "campaignId": { "$in": campaignsIds } } }
]
)
.allowDiskUse(true)
.explain(true);

Related

Nodejs localField $lookup as Array of _ids

i need to join two documents where in the first there a property as array of _ids and in the second documents to join
db={
"tipo_pratica": [
{
"_id": "618981a4c1b8b3bc67ff80b6",
"descrizione": "anticipata",
"modulo": [
"628015cd2fd9dfee86ac6820",
"62801a4c2fd9dfee86ac6821",
"6278f8d9d4aa4f4cef1a8266"
]},
{
"_id": "628238d6f97b57efcb1fc504",
"descrizione": "Supporto",
"modulo": [
"6278f8d9d4aa4f4cef1a8266",
"628015cd2fd9dfee86ac6820",
"62801a4c2fd9dfee86ac6821"
]}
]};
db={
"moduli": [
{
"_id": "6278f8d9d4aa4f4cef1a8266",
"tipo": "Incentivi auto",
"documento": [
"1652095190015_la_scuola_2021_copia.pdf"
],
"contenuto": "<p>Inserire il documento allegato</p>"
},
{
"_id": "628015cd2fd9dfee86ac6820",
"tipo": "Mandato di assistenza e rappresentanza",
"documento": [
"1652561335432_Mandato_di_rappresentanza_privacy_0.pdf"
],
"contenuto": "<p>no</p>"
},
{
"_id": "62801a4c2fd9dfee86ac6821",
"tipo": "Modello red da far... ",
"documento": [
"1652562502599_Modello_REX.pdf"
],
"contenuto": null
}]
};
as documentation said:
Use $lookup with an Array
I tried:
const doc = await collection.aggregate([
{
$lookup: {
from: "moduli",
localField: "modulo",
foreignField: "_id",
as: "moduls"
}
}
])
with no success
so i tested the script on mongoplayground
and there it seems to work well.
I think the problem reside in array of Ids, also
i have tried many option, i have often read the documentation and serching on the web, but many results are specific to mongoose drive, while i use native drive.
I would like the same return as the playground example.
So, any help is largely apprecciate.
below the snippet i use in node for make call
app.post('/admin/moduli/join/', (req, res, error) => {
async function run() {
try {
await client.connect();
var ObjectId = require('mongodb').ObjectId;
const db = client.db("admin");
const collection = db.collection("tipo_pratica");
// replace IDs array with lookup results passed to pipeline
const doc = await collection.aggregate([
{
$lookup: {
from: "moduli",
localField: "modulo",
foreignField: "_id",
pipeline: [
{ $match: { $expr: {$in: ["$_id", "$$modulo"] } } },
{ $project: {_id: 0} } // suppress _id
],
as: "productObjects"
}
}
]);
// doc not work!
// Unwind
const doc2 = await collection.aggregate([
// Unwind the source
{ "$unwind": "$modulo" },
// Do the lookup matching
{ "$lookup": {
"from": "moduli",
"localField": "modulo",
"foreignField": "_id",
"as": "productObjects"
}
}
// doc2 not work!
const doc3 = await collection.aggregate([
{
$facet: {
moduli: [
{
$lookup: {
from: "moduli",
localField: "modulo",
foreignField: "_id", // also tried ObjectId()
as: "plugin"
}
},
{
$match: {
plugin: {
$not: {
$size: 0
}
}
}
}
]
}
},
{
$project: {
tipoPratiche: {
"$concatArrays": [
"$moduli"
]
}
}
},
{
$unwind: "$tipoPratiche"
},
]).toArray();
// doc3 not work!
res.send(doc4);
} finally {
await client.close();
}
}
run().catch(console.dir);
});
Thank you in advance for your time
One way to do it is using a $map before the $lookup:
db.tipo_pratica.aggregate([
{
$set: {
modulo: {
$map: {
input: "$modulo",
as: "item",
in: {$toObjectId: "$$item"}
}
}
}
},
{
$lookup: {
from: "moduli",
localField: "modulo",
foreignField: "_id",
as: "moduls"
}
}
])
Playground
Another option is to use a $lookup pipeline and cast the string to ObjectId inside it.
The proble reside in the formatting text. So belove the solution
const doc1 = await db.collection("tipo_pratica").aggregate([
{
'$set': {
'modulo': {
'$map': {
'input': '$modulo',
'as': 'item',
'in': {
'$toObjectId': '$$item'
}
}
}
}
}, {
'$lookup': {
'from': 'moduli',
'localField': 'modulo',
'foreignField': '_id',
'as': 'moduls'
}
}
]).toArray();

lookup with add extra field in mongodb

My OBJ
[{
_id:XXXXXXXXXX,
role:admin
},
{
_id:XXXXXXXXXX,
role:superUser
}]
and need results using aggregation how to solve this using aggregation
[{
name:'username'
role:'test'
}
]
I suppose you need the following
let db1 = db.get().collection(`temp1`);
let db2 = db.get().collection(`temp2`);
await db1.aggregate([
{
$lookup: {
from: "temp2",
localField: "_id", // field in the orders collection
foreignField: "_id", // field in the items collection
as: "users"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ["$users", 0] }, "$$ROOT"] } }
},
{ $project: { users: 0 } }
]).toArray()

How to lookup in another collection with objectID of main collection in mongodb

I am trying to get the data from another collection via lookup
collection "users"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"name":"Dave",
"center":"THGJ556",
}
collection "addresses"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"userId":"60bf4bb31f45d98903d1851f",
}
collection "applications"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"userId":"60bf4bb31f45d98903d1851f",
"centerId":"THGJ556",
},
{
"_id":{
"$oid":"60bf4bb31f45d98903d3647j"
},
"userId":"60bf4bb31f45d98903d1851f",
"centerId":"JHGJ5476",
}
Now I need data from all the tables.
here is my code:
users.aggregate([
{
$lookup: {
from: "addresses",
localField: "_id",
foreignField: "userId",
as: "addressData"
}
},
{
$lookup: {
from: "applications",
pipeline: [
{ $match:
{ userId:"$_id", centerId: "JHGJ5476"}
},
],
as: "applicationData"
}
},
] ,function(err, result) {
if (err) {
console.log(err)
} else {
console.log(result)
}
});
I am doing something wrong while using aggregate and match in pipeline.
I am getting addressData correctly, but I get nothing [] in applicationData because I suspect something is wrong with userId:"$_id"
As docs explain:
A $match stage requires the use of an $expr operator to access the variables. $expr allows the use of aggregation expressions inside of the $match syntax.
So you have to use $expr into $match stage and also a let stage.
let stage is to define variable to use into $expr: id: $_id.
$expr used with $and and $eq is to get both conditions.
db.users.aggregate([
{
$lookup: {
from: "addresses",
localField: "_id",
foreignField: "userId",
as: "addressData"
}
},
{
$lookup: {
from: "applications",
let: {"id": "$_id"},
pipeline: [
{
$match: {
"$expr": {
"$and": [
{"$eq": ["$userId","$$id"]},
{"$eq": ["$centerId","JHGJ5476"]}
]
}
}
}
],
as: "applicationData"
}
}
])
Check this example.

How to lookup inside lookup in MongoDB Aggregate?

I have a simple 3 collections. This bellow is their pseudocode. I want to get all shipments and for each shipment, I want to have all bids for that shipment and in each bid, I need userDetails object.
User: {
name: string,
}
Shipment: {
from: string,
to: string
}
Bid: {
amount: number,
shipmentId: Ref_to_Shipment
userId: Ref_to_User
}
This is what I have tried:
const shipments = await ShipmentModel.aggregate([
{
$lookup: {
from: "bids",
localField: "_id",
foreignField: "shipmentId",
as: "bids"
}
},
{
$lookup: {
from: "users",
localField: "bids.userId",
foreignField: "_id",
as: "bids.user"
}
}
])
And I got the following result:
[
{
"_id": "5fad2fc04458ac156531d1b1",
"from": "Belgrade",
"to": "London",
"__v": 0,
"bids": {
"user": [
{
"_id": "5fad2cdb4d19c80d1b6abcb7",
"name": "Amel",
"email": "Muminovic",
"password": "d2d2d2",
"__v": 0
}
]
}
}
]
I am trying to get all Shipments with their bids and users within bids. Data should look like:
[
{
"_id": "5fad2fc04458ac156531d1b1",
"from": "Belgrade",
"to": "London",
"__v": 0,
"bids": [
{
"_id": "5fad341887c2ae1feff73402",
"amount": 400,
"userId": "5fad2cdb4d19c80d1b6abcb7",
"shipmentId": "5fad2fc04458ac156531d1b1",
"user": {
"name": "Amel",
}
"__v": 0
}
]
}
]
Try with the following code:
const shipments = await ShipmentModel.aggregate([
{
$lookup: {
from: "bids",
localField: "_id",
foreignField: "shipmentId",
as: "bids"
}
},
{
$unwind: {
path: "$bids",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "users",
localField: "bids.userId",
foreignField: "_id",
as: "bids.user"
}
}
])
If you want to prevent null and empty arrays then set
preserveNullAndEmptyArrays: false
Try this query and chek if works and the behaviour is as you expected:
db.Shipment.aggregate([
{
$lookup: {
from: "Bid",
localField: "id",
foreignField: "shipmentId",
as: "bids"
}
},
{
$lookup: {
from: "user",
localField: "id",
foreignField: "id",
as: "newBids"
}
},
{
$project: {
"newBids.id": 0,
"newBids._id": 0,
}
},
{
$match: {
"bids.userId": 1
}
},
{
$addFields: {
"newBids": {
"$arrayElemAt": [
"$newBids",
0
]
}
}
},
{
$set: {
"bids.user": "$newBids"
}
},
{
$project: {
"newBids": 0
}
}
])
This query do your double $lookup and then a $project to delete the fields you don't want, and look for the userId to add the field user. As $lookup generate an array, is necessary use arrayElemAt to get the first position.
Then $set this value generated into the object as bids.user and remove the old value.
Note that I have used a new field id instead of _id to read easier the data.
Try this
I figured out it based on MongoDB $lookup on array of objects with reference objectId and in the answer from J.F. (data organization). Note that he used id instead of _id
The code is
db.Shipment.aggregate([
{
$lookup: {
from: "Bid",
localField: "id",
foreignField: "shipmentId",
as: "bids"
}
},
{
$lookup: {
from: "user",
localField: "bids.userId",
foreignField: "id",
as: "allUsers"
}
},
{
$set: {
"bids": {
$map: {
input: "$bids",
in: {
$mergeObjects: [
"$$this",
{
user: {
$arrayElemAt: [
"$allUsers",
{
$indexOfArray: [
"$allUsers.id",
"$$this.userId"
]
}
]
}
}
]
}
}
}
}
},
{
$unset: [
"allUsers"
]
},
// to get just one
//{
// $match: {
// "id": 1
// }
// },
])

Mongoose compare with a third level nested field value

I have the following collections:
and I would like to do a find from collection1 all the way to collection 3 and 4 to compare name in one query.
example:
collection1.find({
collection2.collection3.name: req.body.name3,
collection2.collection4.name: req.body.name4
}).exec()
You need to use lookup, unwind and match, here is untested solution of your problem
Model.aggregate([
{
$match: {
_id: req.params.id
// for object id use
// _id: mongoose.Types.ObjectId(req.params.id)
}
},
{
$lookup: {
from: "collection2",
localField: "collection2",
foreignField: "_id",
as: "Collection2"
}
},
{
$unwind: "$ColelctionTwo"
},
{
$lookup: {
from: "collection3",
localField: "CollectionTwo.collection3",
foreignField: "_id",
as: "Collection3"
}
},
{
$lookup: {
from: "collection4",
localField: "CollectionTwo.collection4",
foreignField: "_id",
as: "Collection4"
}
}
]).exec(function(err, result) {
if(err) {
// handle here
}
if(result) {
// do something here...
}
}
You can use mongodb aggregate framework's $lookup or $graphlookup stages for multi collection queries. Mongoose docs for aggregation https://mongoosejs.com/docs/api.html#Aggregate
You cannot do a simple find query for multi collection lookups.

Resources