在Mongoose中,通过字段将对象添加或更新到数组中

t3irkdon  于 12个月前  发布在  Go
关注(0)|答案(1)|浏览(109)

我的模型

const mongoose = require('mongoose');

const TelemetrySchema = new mongoose.Schema({
  MQTT: {
    Id: { type: String, trim: true, required: true },
  },
  Presets: [{
    Id: Number,
    Message: String,
  }],
});

module.exports = mongoose.model('Telemetry', TelemetrySchema);

字符串
我想做的是:
如果具有指定ID的对象已存在,则覆盖它。如果对象尚不存在,则使用指定ID创建一个新对象。
我收到一个Preset对象,以及模型中的预设应该是什么样子:
t=0:{Id: 1, Message: 'A'} -> [{Id: 1, Message: 'A'}]
t=1:{Id: 2, Message: 'B'} -> [{Id: 1, Message: 'A'}, {Id: 2, Message: 'B'}]
t=3:{Id: 1, Message: 'Z'} -> [{Id: 1, Message: 'Z'}, {Id: 2, Message: 'B'}]
有没有可能只用一个findOneAndUpdate来完成这个任务?我有一个可行的解决方案,但我真的不喜欢它.

client.on('message', async (topic, payload) => {
  const s = payload.toString();
  const p = JSON.parse(s);
  const id = topic.split('/');

  const existingTelemetry = await Telemetry.findOne({
    'MQTT.Id': id[1],
    'Presets.Id': p.Id,
  });

  if (existingTelemetry) {
    // If an object with the same id exists, update the existing object
    await Telemetry.findOneAndUpdate(
      {
        'MQTT.Id': id[1],
        'Presets.Id': p.Id,
      },
      {
        $set: {
          'Presets.$': p,
        },
      },
      {
        new: true,
        omitUndefined: true,
      },
    );
  } else {
    // If no object with the same id exists, add the new object
    await Telemetry.findOneAndUpdate(
      {
        'MQTT.Id': id[1],
      },
      {
        $addToSet: {
          Presets: p,
        },
      },
      {
        upsert: true,
        new: true,
        omitUndefined: true,
      },
    );
  }
});

svdrlsy4

svdrlsy41#

这就是我在类似场景中执行更新的方式,我将解释为什么我这样做,而不是聚合管道。
首先,一个聚合管道当然可以做到这一点。然而,当你在3/6/12/x个月的时间内更新代码时,你需要花一些脑力阅读管道,弄清楚你写了什么。使用聚合管道,在相当长的时间之后,它们很难阅读,如果你的模式改变,它们很难调整,至少我是这么觉得的。
其次,当你看到下面的代码时,你和你的团队很容易就能清楚地看到发生了什么。
最后,这是与您的代码的唯一区别,在下面的代码中,某些时间您只需要访问数据库一次,因为if(!doc)可能只是true很少。而在上面的代码中,您将总是访问数据库两次(1x findOne + 1x findOneAndUpdate)。
现在,根据这些更新第一次获得匹配的频率,可能是60/70/80%的时间,因此if(!doc)中的第二个findOneAndUpdate永远不会被触发或仅触发10/20/30%的时间。这可能是一个可接受的公差。

// Assign the return value to 'doc'
const doc = await Telemetry.findOneAndUpdate(
    {
       'MQTT.Id': id[1],
       'Presets.Id': p.Id,
    },
    {
       $set: {
          'Presets.$': p,
       },
    },
    {
       new: true,
       omitUndefined: true,
    },
);
if(!doc){ //< There was no match and no update so $addToSet
   await Telemetry.findOneAndUpdate(
      {
         'MQTT.Id': id[1],
      },
      {
         $addToSet: {
            Presets: p,
         },
      },
      {
         upsert: true,
         new: true,
         omitUndefined: true,
      },
   );
}

字符串

相关问题