I have an app with MongoDB (Mongoose) in NodeJs.
In a collection I have this type of documents, defined by weeks:
{
"_id":
{"$oid":"617f3f51f883fab2de3e7260"},
"endDate":{"$date":"2021-11-07T23:59:59.000Z"},
"startDate":{"$date":"2021-11-01T00:00:00.000Z"},
"wastes":[
{"timestamp":{"$date":"2021-11-01T01:00:58.000Z"},"duration":780},
{"timestamp":{"$date":"2021-11-01T01:00:58.000Z"},"duration":1140},
{"timestamp":{"$date":"2021-11-01T03:00:58.000Z"},"duration":540},
{"timestamp":{"$date":"2021-11-01T07:00:58.000Z"},"duration":540},
{"timestamp":{"$date":"2021-11-01T09:00:58.000Z"},"duration":960},
{"timestamp":{"$date":"2021-11-01T09:00:58.000Z"},"duration":1140},
{"timestamp":{"$date":"2021-11-01T15:00:58.000Z"},"duration":180},
{"timestamp":{"$date":"2021-11-01T15:00:58.000Z"},"duration":540}
...
]}
I have a function that finds wastes with the same timestamp, for example "2021-11-01T01:00:58.000Z"
, gives the longest duration for this timestamp.
I want to delete all entries with that timestamp:
{"timestamp":{"$date":"2021-11-01T01:00:58.000Z"},"duration":780},
{"timestamp":{"$date":"2021-11-01T01:00:58.000Z"},"duration":1140}
And insert only the one with the highest duration:
{"timestamp":{"$date":"2021-11-01T01:00:58.000Z"},"duration":1140}
I'm using updateOne with $pull and $push, but it doesn't work.
let query = {
startDate: new Date(startDayWeek),
};
let deleteProjection = {
$pull: {
wastes: { timestamp: new Date(timestampDeleteInsertion) },
},
};
let insertProjection = {
$push: { wastes: insertRegisterForTimestamp },
};
//Delete
await coleccion.updateOne(query, deleteProjection);
//Insertion
await coleccion.updateOne(query, insertProjection);
I have also tried with {upsert: false}, {multi: true}
.
If I use the same commands in the MongoDB Compass shell, it works without problems:
//Delete
db.coleccion.updateOne({startDate: ISODate('2021-11-01T00:00:00')}, {$pull: {'wastes': {timestamp: ISODate('2021-11-01T01:00:58.000Z')}}})
//Insertion
db.coleccion.updateOne({startDate: ISODate('2021-11-01T00:00:00')}, {$push: {'wastes': {'timestamp':ISODate('2021-11-01T01:00:58.000Z'), 'duration': 1140}}})
CodePudding user response:
You can achieve expected behaviour with Updates with Aggregation Pipeline
The aggregation will consists of 3 steps:
- find out the max duration using
$reduce
; stored the result into a field $filter
thewastes
array by keeping only elements not equal to the selected timestamp or the duration is not the max duration$unset
the helper field created in step 1
db.collection.update({},
[
{
$addFields: {
maxDuration: {
"$reduce": {
"input": "$wastes",
"initialValue": null,
"in": {
"$cond": {
"if": {
$and: [
{
$eq: [
"$$this.timestamp",
{
"$date": "2021-11-01T01:00:58.000Z"
}
]
},
{
$gt: [
"$$this.duration",
"$$value"
]
}
]
},
"then": "$$this.duration",
"else": "$$value"
}
}
}
}
}
},
{
$set: {
wastes: {
$filter: {
input: "$wastes",
as: "w",
cond: {
$or: [
{
$ne: [
"$$w.timestamp",
{
"$date": "2021-11-01T01:00:58.000Z"
}
]
},
{
$eq: [
"$$w.duration",
"$maxDuration"
]
}
]
}
}
}
}
},
{
"$unset": "maxDuration"
}
])
Here is the Mongo playground for your reference.