在单个查询中从Mongo DB中删除最新文档

时间:2021-12-11 18:02:15

I want to delete the latest document from my MongoDB in a single query.

我想在一个查询中删除MongoDB中的最新文档。

I have tried some commands but they doesn't seem to work.

我尝试了一些命令,但它们似乎没有用。

1 个解决方案

#1


2  

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.

您正在寻找的基本操作是mongoose中的findOneAndDelete(),它是一个原子操作,返回带有响应的“已删除”文档。这只会影响单个文档,您可以通过在选项中应用排序规范来获得“最后一个”。

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:

您基本上有两个“最后”选项,或者是一个包含BSON“date”属性的字段,您存储在可以排序的文档中:

Model.findOneAndDelete(
   { "field": "a" },
   { "sort": { "date": -1 } }
)

Or by using the _idfield where an ObjectId was used, as without any other intervention this value will "always increase" with every inserted document:

或者通过使用使用ObjectId的_idfield,无需任何其他干预,此值将随着每个插入的文档“始终增加”:

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.

如果您没有使用BSON日期在文档中存储字段作为确定“最新插入”或“最后修改”的方法,那么这通常是您的选择。如果你想要“最后修改”,那么你真的没有其他选择在文档中记录这样的BSON日期属性,因为_id本身是不可变的并且不会改变,并且充其量只是“创建日期”的“后备”当您没有明确存储任何其他字段来记录此类信息时。

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:

下面是一个完整的示例,演示了如何将多个文档添加到集合中,然后仅“删除”符合所提供查询条件的“最后”文档。使用存储日期和_id字段都进行了演示:

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.

注意:对于实际的节点驱动程序,它的findOneAndDelete()基本相同,是mongoose对服务器的实际调用,但旧版本的mongoose仅支持findOneAndRemove(),它在选项中几乎相同,但发出了findAndModify()请求通过核心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.

从技术角度来看,这些实际上都是findAndModify命令,但是通常首选使用现代API,因为这些方法在其预期目的中具有“清晰度”,并且还为更广泛的可用选项范围选择合理的“默认值”。服务器实际处理的“命令”。

#1


2  

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.

您正在寻找的基本操作是mongoose中的findOneAndDelete(),它是一个原子操作,返回带有响应的“已删除”文档。这只会影响单个文档,您可以通过在选项中应用排序规范来获得“最后一个”。

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:

您基本上有两个“最后”选项,或者是一个包含BSON“date”属性的字段,您存储在可以排序的文档中:

Model.findOneAndDelete(
   { "field": "a" },
   { "sort": { "date": -1 } }
)

Or by using the _idfield where an ObjectId was used, as without any other intervention this value will "always increase" with every inserted document:

或者通过使用使用ObjectId的_idfield,无需任何其他干预,此值将随着每个插入的文档“始终增加”:

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.

如果您没有使用BSON日期在文档中存储字段作为确定“最新插入”或“最后修改”的方法,那么这通常是您的选择。如果你想要“最后修改”,那么你真的没有其他选择在文档中记录这样的BSON日期属性,因为_id本身是不可变的并且不会改变,并且充其量只是“创建日期”的“后备”当您没有明确存储任何其他字段来记录此类信息时。

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:

下面是一个完整的示例,演示了如何将多个文档添加到集合中,然后仅“删除”符合所提供查询条件的“最后”文档。使用存储日期和_id字段都进行了演示:

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.

注意:对于实际的节点驱动程序,它的findOneAndDelete()基本相同,是mongoose对服务器的实际调用,但旧版本的mongoose仅支持findOneAndRemove(),它在选项中几乎相同,但发出了findAndModify()请求通过核心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.

从技术角度来看,这些实际上都是findAndModify命令,但是通常首选使用现代API,因为这些方法在其预期目的中具有“清晰度”,并且还为更广泛的可用选项范围选择合理的“默认值”。服务器实际处理的“命令”。