使用GraphQL与mongoose findByIdAndUpdate时未修改字段变为null
这个问题我之前也踩过坑,根源在于你当前的更新逻辑里,不管mutation有没有传入对应的字段,都把所有字段(name/age/interests)硬塞进了更新对象中。当某个参数没被传入时,args里对应的属性值是undefined,Mongoose执行更新操作时会把这些undefined的字段直接设为null(甚至直接删除字段,取决于Schema配置),最终导致原文档中未修改的字段丢失。
解决方案:构建仅包含传入字段的更新对象
我们需要先过滤掉args中未传入的字段(也就是值为undefined的属性),只把实际传入的字段放到更新对象里,这样Mongoose就只会更新指定字段,其余字段保留原值。
修改你的GraphQL resolve函数如下:
updateProfile: { type: profileType, args: { id: { type: new GraphQLNonNull(GraphQLID) }, name: { type: GraphQLString }, age: { type: GraphQLString }, interests: { type: GraphQLString }, }, resolve(parent, args) { // 构建仅包含传入字段的更新对象,排除id字段 const updateObj = Object.fromEntries( Object.entries(args).filter(([key, value]) => key !== 'id' && value !== undefined) ); // 执行更新,加上runValidators确保字段符合Schema规则 return Profile.findByIdAndUpdate(args.id, updateObj, { new: true, runValidators: true }) .catch(err => { // GraphQL中通过抛出错误处理异常,不要直接操作res对象 throw new Error(`更新失败:${err.message}`); }); } }
关键细节说明
过滤未传入的字段:
用Object.fromEntries和filter组合,把args中除了id之外、值不为undefined的字段筛选出来,这样updateObj里只有你在mutation中实际传入的字段。移除res.status的使用:
GraphQL的resolve函数不能直接操作res对象,应该通过抛出错误来处理异常,GraphQL会自动把错误信息返回给客户端的errors数组中。添加runValidators选项:
加上runValidators: true可以确保更新操作时遵循你定义的Mongoose Schema验证规则(比如如果以后把某个字段设为必填,这个选项会验证传入值是否符合要求)。
这样修改后,再执行你之前的mutation:
mutation { updateProfile(id: "5b05b4cfd53486377061eab3", name: "dave") { id name age interests } }
返回结果中age和interests就会保留原文档中的值,而不会变成null了。
内容的提问来源于stack exchange,提问作者FiliBoii

