I want to delete the latest document from my MongoDB in a single query.
I have tried some commands but they doesn\'t seem to work.
The basic operation you are looking for is findOneAndDelete() in mongoose which is an atomic operation returning the "removed" document with the response. This only ever affects a single document and you get the "last" by applying a sort specification in the options.
You basically then have two options for "last", either being by a field containing a BSON "date" property you have stored in the documents which you can sort on:
Model.findOneAndDelete(
{ "field": "a" },
{ "sort": { "date": -1 } }
)
Or by using the _id
field where an ObjectId
was used, as without any other intervention this value will "always increase" with every inserted document:
Model.findOneAndDelete(
{ "field": "a" },
{ "sort": { "_id": -1 } }
)
That's generally your option if you did not store a field within the document with a BSON Date as a means of determining the "latest inserted" or "last modified". If you want "last modified" then you really have no other option that to record such a BSON date property within the document since the _id
itself is immutable and does not change, and at best is a "fallback" for a "created date" when you did not explicitly store any other field to record such information.
A full example follows, which demonstrates adding multiple documents to a collection and then "removing" only the "last" document meeting the supplied query criteria. Both using a stored date and the _id
field are demonstrated:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
const testSchema = new Schema({
field: String,
other: String,
date: Date
});
const Test = mongoose.model('Test', testSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
const now = Date.now();
const today = now - (now % (1000 * 60 * 60 * 24));
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await Test.insertMany([
...[ ...Array(4)].map((e,i) =>
({
field: "a",
...(i === 3) ? { other: "last" }
: (i === 2) ? { other: "second last" } : {},
date: new Date(today + (i * 1000 * 60 * 60 * 24))
})
),
{ field: "b", date: new Date(today + (5 * 1000 * 60 * 60 * 24)) }
]);
let removed = await Test.findOneAndDelete(
{ field: "a" },
{ sort: { "date": -1 } }
);
log({ removed });
let remaining = await Test.find();
log({ remaining });
let next_removed = await Test.findOneAndDelete(
{ field: "a" },
{ sort: { "_id": -1 } }
);
log({ next_removed });
let still_remaining = await Test.find();
log({ still_remaining });
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
And this returns the expected output of:
Mongoose: tests.remove({}, {})
Mongoose: tests.insertMany([ { _id: 5b0cb4a60cf8000c7ebd4402, field: 'a', date: 2018-05-29T00:00:00.000Z, __v: 0 }, { _id: 5b0cb4a60cf8000c7ebd4403, field: 'a', date: 2018-05-30T00:00:00.000Z, __v: 0 }, { _id: 5b0cb4a60cf8000c7ebd4404, field: 'a', other: 'second last', date: 2018-05-31T00:00:00.000Z, __v: 0 }, { _id: 5b0cb4a60cf8000c7ebd4405, field: 'a', other: 'last', date: 2018-06-01T00:00:00.000Z, __v: 0 }, { _id: 5b0cb4a60cf8000c7ebd4406, field: 'b', date: 2018-06-03T00:00:00.000Z, __v: 0 } ], {})
Mongoose: tests.findOneAndDelete({ field: 'a' }, { sort: { date: -1 } })
{
"removed": {
"_id": "5b0cb4a60cf8000c7ebd4405",
"field": "a",
"other": "last",
"date": "2018-06-01T00:00:00.000Z",
"__v": 0
}
}
Mongoose: tests.find({}, { fields: {} })
{
"remaining": [
{
"_id": "5b0cb4a60cf8000c7ebd4402",
"field": "a",
"date": "2018-05-29T00:00:00.000Z",
"__v": 0
},
{
"_id": "5b0cb4a60cf8000c7ebd4403",
"field": "a",
"date": "2018-05-30T00:00:00.000Z",
"__v": 0
},
{
"_id": "5b0cb4a60cf8000c7ebd4404",
"field": "a",
"other": "second last",
"date": "2018-05-31T00:00:00.000Z",
"__v": 0
},
{
"_id": "5b0cb4a60cf8000c7ebd4406",
"field": "b",
"date": "2018-06-03T00:00:00.000Z",
"__v": 0
}
]
}
Mongoose: tests.findOneAndDelete({ field: 'a' }, { sort: { _id: -1 } })
{
"next_removed": {
"_id": "5b0cb4a60cf8000c7ebd4404",
"field": "a",
"other": "second last",
"date": "2018-05-31T00:00:00.000Z",
"__v": 0
}
}
Mongoose: tests.find({}, { fields: {} })
{
"still_remaining": [
{
"_id": "5b0cb4a60cf8000c7ebd4402",
"field": "a",
"date": "2018-05-29T00:00:00.000Z",
"__v": 0
},
{
"_id": "5b0cb4a60cf8000c7ebd4403",
"field": "a",
"date": "2018-05-30T00:00:00.000Z",
"__v": 0
},
{
"_id": "5b0cb4a60cf8000c7ebd4406",
"field": "b",
"date": "2018-06-03T00:00:00.000Z",
"__v": 0
}
]
}
NOTE: For the actual Node Driver it's findOneAndDelete() is essentially identical and is the actual call made by mongoose to the server, but older versions of mongoose only support findOneAndRemove() which is almost identical in options but instead issues a findAndModify() request through the core API.
From a technical standpoint these are all actually the findAndModify command, however it is generally preferred to use the modern API since the methods have "clarity" in their intended purpose, and also choose reasonable "defaults" to the range of available options to the broader "command" which the server actually processes.
very simple :
Model.findOneAndDelete({},{"sort": { "_id": -1 }})