更新子文档中的数据而不覆盖猫鼬中的另一个字段

问题描述 投票:0回答:4

数据库中的数据

fieldA : 'aaaaa',
fieldB : {
      en : 'aaaaaaaaaa',
      de : 'bbbbbbbbbb'
}

新数据

val = {
fieldA : 'aaaaa11',
fieldB : {
            en : 'aaaaa1111'
         }
}

我试过这个代码

Model.findOneAndUpdate({fieldA : val.fieldA},{ $set : val})

当我运行此命令时,“fieldB.de”丢失。我想知道如何更新,结果如下所示

fieldA : 'aaaaa11',
fieldB : {
  en : 'aaaaa1111',
  de : 'bbbbbbbbbb'
}
node.js mongoose
4个回答
1
投票

您必须更改传递给

findOneAndUpdate()
的对象,这样您就不会将
fieldB
作为对象传递,而只传递要更新的字段。 可以用这样的东西来完成:

let val = {
  fieldA: 'aaaaa11',
  fieldB: {
    en: 'aaaaa1111',
  },
};

if (val.fieldB) {
  const newFieldB = Object.entries(val.fieldB).reduce(
    (a, [key, value]) => ({ ...a, ['fieldB.' + key]: value }),
    {}
  );
  delete val.fieldB;
  val = { ...val, ...newFieldB };
}

Model.findOneAndUpdate({fieldA : val.fieldA},{ $set : val})

这样,传递给

val
findOneAndUpdate()
的值就是:

{
  fieldA: 'aaaaa11',
  'fieldB.en': 'aaaaa1111',
}

代替:

{
  fieldA: 'aaaaa11',
  fieldB: {
    en: 'aaaaa1111',
  },
}

...以及您未在

fieldB
中指定但在数据库中的
val
属性不会被覆盖。


0
投票

试试这个:

Model.findOneAndUpdate({fieldA : val.fieldA},{
  "$set": {
    "fieldA": val.fieldA,
    "fieldB.en": val.fieldB.en
  }
})

在您的解决方案中,您将文档与其他文档完全更新。要仅更新特定字段,您必须在“$set”对象中指定此字段


0
投票

我通过首先找到文档并手动编辑对象来解决此问题。

/* Recursive function to update manually an object */
const update = (obj, newValues) => {
  Object.entries(newValues).forEach( ([key, value]) => {
    if(typeof value === 'object'){
      update(obj[key], value);
    } else {
      obj[key] = value;
    }
  });
}
/* Find document, update and save */
const infoToUpdate = {};
const doc = await Model.findOne({ field: valueToFind });
if (doc) {
  object.update(doc, infoToUpdate);
  doc.save();
}

0
投票

这里有一个解决方案,可以在不覆盖 Mongoose 中其他字段的情况下更新子文档中的数据。此方法区分

PUT
PATCH
请求,方法是将请求主体扁平化到名为 flatten 的新字段(对于
PATCH
请求)并保持原样对于
PUT
请求。

首先,为

middleware
请求设置
flatten
PATCH
请求正文:

// flatten-mongoose.middleware.ts <---------------- File
import { Request, Response, NextFunction } from 'express';
import flattenizer './flatten-mongoose.helper'

// Middleware to flatten the request body for PATCH requests
export default function flatten(
  req: Request,
  res: Response,
  next: NextFunction
) {
  req.body = flatten(req.body);
  next();
}
// flatten-mongoose.helper.ts   <---------------- File
export function flattenizer(obj: any) {
  const result: any = {};
  Object.keys(obj).forEach((key: string) => {
    if (obj.hasOwnProperty(key)) {
      if (typeof obj[key] === "object" && !Array.isArray(obj[key])) {
        const flatObject = flattenizer(obj[key]);
        for (const key2 in flatObject) {
          if (flatObject.hasOwnProperty(key2)) {
            result[`${key}.${key2}`] = flatObject[key2];
          }
        }
      } else {
        result[key] = obj[key];
      }
    }
  });

  return result;
}
// app.ts or someroute.ts <----------------------------- File
import express from 'express';
import flatten from './flatten-mongoose.middleware'
...
app = express(); // you can assign it to only some routes
app.patch("*", flatten) 
...

说明:

  1. 中间件设置: 使用
    app.patch("*", flatten) in the mail app file or routes
    将中间件扁平化应用于所有 PATCH 请求。
  2. Flattenizer: 扁平化函数是执行实际扁平化的辅助函数。它需要一个对象和一个父键(对于嵌套对象)并返回该对象的扁平版本。
  3. 分配扁平化主体:在调用 next() 将控制权传递给下一个中间件或路由处理程序之前,扁平化结果会被分配回 req.body。

用途:

  • PATCH
    请求:当发出
    PATCH
    请求时,请求正文会被展平,允许您更新子文档中的特定字段,而不会覆盖其他字段,但请记住使用
    req.body.flatten
  • PUT
    请求:对于PUT请求,请求正文保持不变,允许完全替换文档。 这种方法确保您可以更新子文档中的特定字段而不影响其他字段,为 Mongoose 中处理部分更新提供了灵活高效的方法。
router.patch('/:id', (req: Request, res: Response, next: NextFunction) {
    Transaction
      .findByIdAndUpdate(
        req.params.id,
        {
           ...req.body.flatten // here you need to use flatten
        },
        { new: true } // Return the updated document
      )
      .then((updatedTransaction) => {
        if (updatedTransaction) {
          res.status(202).json({
            ...res.locals,
            message: `transaction updated with id ${updatedTransaction._id}`,
            transaction: updatedTransaction,
          };
        } else {
          res.status(404).json({error: "Error : transaction not found});
        }
      })
      .catch(error => res.status(500).json(error));
  });
 
© www.soinside.com 2019 - 2024. All rights reserved.