Aggregate and update with mongoose - mongodb

I want to use the aggregate method to make my querys and modify my database by a value, I tried with $set but my database is not modified.
Here is how I do my query:
var filter = req.body.filter
var search = [ { $match: filter }, { $set: {item: "2"} }, { $sample: { size: 1 } }]
const result = await dataModel.aggregate(search)
I know there is also findOneAndUpdate but I would like to keep aggregate because I also want to use $project in my pipelines
thanks in advance !

You can use FindOneAndUpdate for change your db

Related

Mongoose: search for ObjectID by Array

I want to filter my collection by aggregation for one of many ObjectIDs.
Because of some DocumentDB restrictions I can not build a single pipeline with uncorrelated subqueries. So my fix is to do it in two queries.
for example: I have an aggregation that returns all teamIds, for some conditions as an array of Object with the IDs.
[{_id: ObjectID("abcdef")}, {_id: ObjectID("ghijkl")}, {_id: ObjectID("vwxyz")}, ...]
I now want to have a second aggregation filter another collection using the ObjectIDs.
This would work in Mongo Compass:
{
"team": {
"$in": [ObjectId("60aabcb05c7462f42b3d7zyx"), ObjectId("60aabc7b05c7462f42b3dxyz")]
},
....
}
My issue is that i can not find the correct syntax for JS to generate such a pipeline.
What ever I try, JS always converts my Array of ObjectIDs to something like this:
{
"team": {
"$in": [{
"_id": "60aabcb05c7462f42b3d7zyx"
},{
"_id": "60aabc7b05c7462f42b3dxyz"
}]
},
I fixed it like this. I am not 100% why this syntax works because it is still just an array of objects, formatted like before, but I guess there is some stuff mongoose does, that is opaque to me.
let teams = await TeamMgmt.getTeamsAggregatedByFilter( teamFilter )
// make an array of ObjectIds so we can filter for them.
let idArray = []
Object.keys( teams ).map( function ( key, index ) {
idArray.push( new mongoose.Types.ObjectId( teams[ index ]._id.toString() ) )
} );
const shiftFilter = [
{
'$match': {
'team': {
"$in": idArray
},
....
}

Display object list with a parameter on Mongoose

I have a find query that returns me a list of objects:
{
"_id": "5fb94fda487b9348c4291450",
"name": [
{
"NewConfirmed": 642686,
"TotalConfirmed": 49315431,
"NewDeaths": 9555,
"TotalDeaths": 1242785,
"NewRecovered": 288131,
"TotalRecovered": 32473892
},
{
"NewConfirmed": 116262,
"TotalConfirmed": 6014461,
"NewDeaths": 4640,
"TotalDeaths": 371913,
"NewRecovered": 77575,
"TotalRecovered": 2492884
},
{
...
Its all fine but I'm trying to make a new query with a status parameter with the value NewConfirmed or TotalConfirmed or NewDeaths to display only that specific field. So the endpoints would look like /something/status/:status.
I already tried an aggregation with filter and a simple find but still havent figured nothing out.
Anyone has any idea?
First of all, you need a query with this estructure:
db.collection.aggregate([
{
/**Your match object*/
},
{
"$project": {
"YourStatus": {
"$first": "$name.YourStatus"
}
}
}
])
Example here.
Using mongoose you need to create the object query in this way:
var query = {}
query[status] = {"$first": "$name."+status}
And do the mongoose query replacing the object by query object.
var aggregate = await model.aggregate([
{
//Your $match stage here
},
{
"$project": query
}
])
Also, I've tested in local but my mongo version (I think) doesn't recognize $first so I've used $arrayElemAt. According to mongo docs is the same as $first.
var status = "NewConfirmed"
var query = {}
query[status] = { $arrayElemAt: ["$name."+status, 0]}
Also you can add _id: 0 into $project aggregate to not return this field.
var query = {_id:0} //Here add _id: 0 to project object
query[status] = { $arrayElemAt: ["$name."+status, 0]} //And the rest of the stage

Aggregation query with $set in findOneAndUpdate doesn't update document

I am using node 11.6, mongodb 4.2.5, mongoose 4.3.17. I am trying to update a field by adding a string to the end of it. I first updated to mongo 4.2 which I apparently needed to use aggregation pipelines in updates.
I tried following this post like this:
var update = [{$set: {slug: {$concat: ['$slug', '-rejected']}}}];
Content.findOneAndUpdate({_id: id}, update, {new: true}, (err, doc) => {
//
});
but when I ran it I got no error, no document returned, and it was not updated.
So I removed the outer [], and passed just an object like this:
var update = {$set: {slug: {$concat: ['$slug', '-rejected']}}}
Content.findOneAndUpdate({_id: id}, update, {new: true}, (err, doc) => {
//
});
And I receive this error message:
`Cast to string failed for value "{ '$concat': [ '$slug', '-rejected' ] }" at path "slug"`,
What does this mean? How can I accomplish this? (without two separate calls)
Running the same function but replacing update with:
var update = {slug: 'test-slug'}
successfully updates the slug to 'test-slug'.
But trying to do the same simple thing with an aggregation acts much like my previous attempt, no error or change:
var update = [{$set: {slug: 'test-sluggy'}}]
Using updateOne() instead of findOneAndUpdate() doesn't change anything either.
The only thing I can think that could cause it is the mongoose version, but it seems like there's a lot of changes between 4 and 5 and I don't want to update unless I have to, but I can't find anything that says it would change anything.
The pipeline form requires that the update be an array of pipeline stages.
Try wrapping your existing update in [] like
var update = [{$set: {slug: {$concat: ['$slug', '-rejected']}}}]
we can use something like that
var update = [{ $set: { slug: { $concat: ['$slug', '-rejected'] } } }]
starting from mongo versions > 4.2, the update operations can accept an aggregation pipeline, so we are able to update some field based on its current value:
the whole query may be something like that
Content.findOneAndUpdate(
{ _id: id }, // the find criteria
[{ $set: { slug: { $concat: ['$slug', '-rejected'] } } }], // this is the update aggregation pipeline, see the square brackets
{ multi: true }, // this should be set to true if you have more than one document to update,
// otherwise, only the first matching document will be updated,
// this is in case you use update rather than findOneAndUpdate,
// but here we have only one document to update, so we can ignore it
(err, doc) => {
//
});

MongoDB update using limit and skip

In Mongoose, I would need to do the following in the quickest possible way:
find documents by a query
use limit() and skip()
update a field in all the found records with the same value
what do you suggest?
you can get records and then get id's of all the records into one array and then
update them using $in
async function someAsyncFunction(){
let foundData= await collection.find(query).skip().limit();
let IDs=[];
foundData.forEach(element=>{
IDs.push(element._id);
});
return Collection.update(
{_id: { $in: IDs}},
{ $set: {"fieldToUpdate": "value"}},
{ multi: true }
);
}

I am building a mongoose aggregate for search mongodb documents

I built an aggregate for searching according to a filter. The things that the user can search for are optional, So is there any way to make the match optional - for example the user can select a date, If he didn't select, I want the aggregate function not to use match date
db.articles.aggregate(
[ {
$match : { date : userSelectedDate }, { else : elseSelection}
} ]
);
If no date is selected => cancel the date matching and match anothers
I would try to build the query document dynamically.
var query = []
if (userSelectedDate){
query.push({ $match : { date : userSelectedDate }})
db.articles.aggregate(query)
The solution was in mongoose documentation using: Aggregate#append(ops)
Appends new operators to this aggregate pipeline
after having the aggregate
aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
so now I can use if(condition) before appending
mongoose documentation for append