Async/await functionality for db.eval aggregate - javascript

I am trying to execute a db.collection.aggregate() query within a call to db.eval(). I am using eval() because I am making a dynamic number of lookups, so I generate the query by concatenating relevant strings. The query works perfectly when I manually remove the quotes from the string:
await db.collection('Products').aggregate([{
$lookup: {
from: 'Golomax',
localField: 'barcode',
foreignField: 'barcode',
as: 'Golomax'
}
}, {
$unwind: {
path: '$Golomax',
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: 'Masivos SA',
localField: 'barcode',
foreignField: 'barcode',
as: 'Masivos SA'
}
}, {
$unwind: {
path: '$Masivos SA',
preserveNullAndEmptyArrays: true
}
}, {
$out: 'Output'
}]).toArray();
Unfortunately, it does not work when I am using the string in a call to db.eval(). I put quotes around the code snippet above and set the string equal to the variable 'query' and tried this:
db.eval('async function(){' + query + ' return;}', function(err, result) {
console.log('the result is: ', result);
});
I've also tried removing the word "async," and this still has not worked. How do I ensure that the function will finish aggregating before returning? Thanks.
-- EDIT --
I just noticed that db.eval() is deprecated and planned for removal. The alternative is to "implement the equivalent queries/operations using the normal MongoDB query language and client driver API." How can I do this using a string query?

You don't need $eval for this. It sounds like you want to create a $lookup and $unwind for each item in an Array. That is exactly what map() is for. You can create the array of commands separate and then pass it to aggregate():
// Have a list of places
const thingsToUnwind = [
'Golomax',
'Masivos SA',
'Some Other Place',
'Yet Another Place'
];
const unwindables = thingsToUnwind
// Create a $lookup and $unwind for each place
.map(place => {
return [{
$lookup: {
from: place,
localField: 'barcode',
foreignField: 'barcode',
as: place
}
},
{
$unwind: {
path: `$${place}`,
preserveNullAndEmptyArrays: true
}
}
];
})
// Flatten the array of arrays
.reduce((acc, curr) => [...acc, ...curr], []);
// Add an $output node
unwindables.push({
$out: 'Output'
});
// Perform the aggregation
await db
.collection('Products')
.aggregate(unwindables)
.toArray();

I just solved my own problem using the Javascript eval() and removing the "await" at the beginning of the string. It executes perfectly now!

Related

how can i find user details using aggregate in node js with rest api

dbo.collection('user').aggregate([
{
$lookup:
{
from: 'usersettings',
localField: '_id',
foreignField: 'userId',
as: 'usersettings',
pipeline:[
$match=>[
_id=>reqUserId
]
]
}
}
]).toArray(function (err, resSettingData) {
if (err) { reject(err) } else { resolve(resSettingData) }
console.log("User Data Find By Id", resSettingData)
})
})
i want to find user detail using it's id in this code
When we add a user's data to the user collection, the user's settings data is also added to the usersettings collection
But when we look at the data from its ID, we get the data from both simultaneously by passing the ID once.
There is a syntax error only I mean a typo mistake I will saw you how it is
{ $match:{"_id": ObjectId(reqUserId)} }
type like that after $lookup Object

Use 'virtual' in an array of objects (Mongoose)

I have two schemas as the following:
const castListSchema = new Schema({
id: String,
roleInVideo: String
});
const videoSchema = new Schame({
castList: { type: [castListSchema], default: [] }
});
I want to get data by the following structure:
castList: [
{
details: { ... },
roleInVideo: 'director'
},
...
]
I want to use virtual not aggregate and etc...
Is it possible? How?
Yes, you can use the virtual function inside the array of objects.
Use the following code to use the virtual function for the array of objects.
videoSchema.virtual("castList.castData", {
ref: "new_assets",
localField: "castList.castListSchema",
foreignField: "_id",
justOne: true
});
In populate also use this "castList.castData" as key to retrieve the data.

Mongoose for/await/of loop is `sort` required in Query?

From documentation of Mongoose there is the following example about iterating with async/await:
// Works without using `cursor()`
for await (const doc of Model.find([{ $sort: { name: 1 } }])) {
console.log(doc.name);
}
Since I am quite new in MongoDB, I want to understand if [{ $sort: { name: 1 } }] is required to get the query working or it is only an example.
My goal is to iterate all the documents in a Collection, so I suppose that following code should be fine:
// Works without using `cursor()`
for await (const doc of Model.find()) {
console.log(doc.name);
}
Is it correct?
In Mongodb 4.2 and older passing an array of objects to find will trigger an exception. So if you run:
Model.find([{ $sort: { name: 1 } }]).then(...)
you will get:
ObjectParameterError: Parameter "filter" to find() must be an object
However, in MongoDB 4.4, [{ $sort: { name: 1 } }] can, in fact, be passed to find. However, $sort does not actually go into find params, but rather should be used like so Model.find().sort({name: 1}).
That makes me believe that [{ $sort: { name: 1 } }] in the link you supplied is just a placeholder and servers no purpose. So running
for await (const doc of Model.find()) {
is perfectly fine.

'InvalidPipelineOperator' with $lookup when adding .toArray()

I'm new to MongoDB, and working with aggregation. Here goes:
I have two collections 'video_card' and 'vendors'. I am fetching a video card document from the collection with the following structure:
_id:ObjectId(x),
vendorList:
0: ID1;
1: ID2;
I am trying to do a join between this document and this vendor collection:
_id: id:ObjectId(y)
name: "Amazon"
My aggregate is as follows so far:
const products = await db
.collection("video_card")
.aggregate([
{
$project: {
mappedVendors: {
$map: {
input: "$vendorList",
as: "vendorName",
in: {
$lookup: {
from: "vendors",
localField: "vendorList",
foreignField: "name",
as: "VendorNames"
},
},
},
},
},
},
]);
This returns a cursor object. However, when I attach .toArray() to the end of this, I get a code:168 'InvalidPipelineOperator'. Why is this?
To clarify, my intent is to return the data with vendorList ids swapped with names.
To follow up, I realized that my foreignField in the $lookup was incorrect, and changed it to _id. I hope this helps other people learning aggregate functions.

Aggregate pipeline from cloud code not returning anything

Aggregation calls that are made on parse cloud code are not providing the required result. I'm not sure if this is a problem with the syntax we are using or if there are some missing things we need to get pipeline aggregation calls to work correctly.
For the required aggregation call, we are building a pipeline that uses five different stages. Within the five stages we are using the following four functions: addFields, lookup, unwind, and group. This has been tested on the MongoDB compass application, and the result is displayed correctly. When the aggregations are exported, and converted to what we believe is the correct syntax, the query returns no results.
Simple aggregation pipelines that only use one stage are working fine. This has been tested for both group by and addField calls. It seems that as soon as multiple stages are added to the pipeline there is a failure.
The aggregation call that is produced directly from MongoDB Compass export to Node, is as follows
[
{
'$addFields': {
'user': {
'$substr': [
'$_p_pUser', 6, -1
]
}
}
}, {
'$lookup': {
'from': '_User',
'localField': 'user',
'foreignField': '_id',
'as': 'userobject'
}
}, {
'$addFields': {
'username': '$userobject.username'
}
}, {
'$unwind': {
'path': '$username'
}
}, {
'$group': {
'_id': '$username',
'total': {
'$sum': '$score'
}
}
}
]
The above call, when converted to the syntax provided here (https://docs.parseplatform.org/js/guide/#aggregate), is as follows:
var pipeline = {
addFields : { user: { $substr : ['$_p_pUser', 6, -1]} },
lookup : {
from: '_User',
localField: 'user',
foreignField: 'objectId',
as: 'userobject'
},
addFields : { username: '$userobject.username' },
unwind : { path: '$username' },
group : {
objectId: '$username',
total : {
$sum : '$score'
}
}
};
var pipelineResults = await gameTableQuery.aggregate(pipeline);
This provided no results. It was also tested using the specific field name (pUser) rather than the _p_pUser (required to get the query working in MongoDB Compass).
var pipeline = {
addFields : { user: { $substr : ['$pUser', 6, -1]} },
lookup : {
from: '_User',
localField: 'user',
foreignField: 'objectId',
as: 'userobject'
},
addFields : { username: '$userobject.username' },
unwind : { path: '$username' },
group : {
objectId: '$username',
total : {
$sum : '$score'
}
}
};
A possible issue could be the duplication of the functions addFields. I also attempted the same call, using one addFields call instead.
var pipeline = {
addFields :
{
user: { $substr : ['pUser', 6, -1]},
username: '$userobject.username'
},
lookup : {
from: '_User',
localField: 'user',
foreignField: 'objectId',
as: 'userobject'
},
unwind : { path: '$username' },
group : {
objectId: '$username',
total : {
$sum : '$score'
}
}
};
These calls were done with cloud code and they do not return the required results that were found in MongoDB Compass. No errors are thrown due to syntax, there are simply no results. Are there any restrictions within the parse aggregation calls that would explain why the call is failing?
It seems the issue was due to the substring call.
In MongoDB compass the pUser field was assigned a value that required trimming to access the objectId.
When trying to do the same thing from the parse cloud code aggregate, the trimming wasn't necessary as pUser contains objectId as a child element.
To access the objectId I am now using:
user : '$pUser.objectId'
instead of
user : { $substr : ['pUser', 6, -1]}

Categories