首页 > 解决方案 > 猫鼬:在 X 集合中创建新元素,在 Y 集合中更新另一个元素

问题描述

我正在尝试开发一个 CRUD 应用程序供用户存储、添加、删除和更新食谱。它建立在 MEVN 堆栈上。由于我需要向用户展示他们创建了哪些食谱,我正在尝试基于此模型创建一个食谱:

const RecipeSchema = new Schema({
    title: {
        type: String,
        required: [true, 'Title of the recipe is required'],
    },
    category: {
        type: Array,
        required: [true, 'Category is required'],
    },
    description: {
        type: String,
        required: [true, 'Description is required'],
    },
    imgUrl: {
        type: String,
        required: [true, 'Image is required'],
    },
    ingredients: {
        type: Array,
        required: [true, 'Ingredients are required'],
    },
    timeOfPreparation: {
        type: String,
        required: true,
    },
    preparation: {
        type: String,
        required: true,
    },
    sourceName: {
        type: String,
        required: true,
    },
    sourceUrl: {
        type: String,
        required: true,
    },
    author: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});

const Recipe = mongoose.model('Recipe', RecipeSchema);

module.exports = Recipe;

同时更新用户模型,基于此:

const UserSchema = Schema({
    googleId: String,
    name: String,
    favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
    authoredRecipes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
});

const User = mongoose.model('User', UserSchema);

module.exports = User;

在控制器中,我有这个方法(根据@Stock Overflaw 评论):

exports.create_new_recipe = (req, res, next) => {
Recipe.create(req.body)
    .then(recipe => {
        User.update(
            { _id: req.body.author },
            {
                $push: { authoredRecipes: recipe.id },
            }
        );
        res.send(res.status);
    })
    .catch(error => {
        res.status(500).json({ error });
    });

};

当我转到 /create 端点时会调用此方法。但是,即使我确实获得了所有正确的 ID(req.body.author 和 recipe.id),我也无法让它工作。在我的 mLab 配方集合中,配方正确显示(我使用 authorId 插入的所有数据),但是在用户集合中,authoredRecipes 数组保持为空。

我怎样才能让猫鼬既在一个集合中创建一个对象,又根据它们的 id 更新另一个对象?

标签: node.jsmongodbmongoose

解决方案


文档需要findByIdAndUpdate_id字段作为其值,而不是对象:

User.findByIdAndUpdate(req.body.author, {
  $push: { authoredRecipes: recipe.id }
});
// equivalent to the more general method:
User.findOneAndUpdate({ _id: req.body.author }, {
  $push: { authoredRecipes: recipe.id }
});
// and if you don't need the modified document in your callback, this should be faster:
// EDIT: this is advised against (we should use a user object, not the collection)
User.update({ _id: req.body.author }, { // or updateOne
  $push: { authoredRecipes: recipe.id }
});

编辑:一个工作的,最小的例子

介意{new: true}吗?取决于你如何测试它是否有效......

const mongoose = require('mongoose');
const fs = require('fs');

const userIdFile = './tmp.txt'; // just for this test

mongoose.connect('mongodb://localhost/meuh', {
  useNewUrlParser: true, // removes a deprecation warning
  useFindAndModify: false // removes another deprecation warning
});

// make schemas/models
const RecipeSchema = mongoose.Schema({
  title: { type: mongoose.Schema.Types.String }
});
const UserSchema = mongoose.Schema({
  name: { type: mongoose.Schema.Types.String },
  data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }]
});
const RecipeModel = mongoose.model('Recipe', RecipeSchema);
const UserModel = mongoose.model('User', UserSchema);

// user precreation
// UserModel.create({
//   name: 'me, myself and I'
// }).then((user) => {
//   fs.writeFile(userIdFile, user.id, console.log.bind(null, 'error writing file:'));
//   mongoose.connection.close();
// });
// return;

// fetch user
const userId = fs.readFileSync(userIdFile);

let pushedRecipeId; // to test everything went smooth
RecipeModel.create({
  title: 'pasta solo'
}).then((recipe) => {
  console.log('created recipe:', recipe);
  pushedRecipeId = recipe.id;
  return UserModel.findOneAndUpdate(
    { _id: userId },
    { $push: { data: recipe.id } },
    { new: true } // forces callback to be passed a fresh object
  );
}).then((user) => {
  console.log('updated user:', user);
  console.log('izok:', !!~user.data.indexOf(pushedRecipeId));
  mongoose.connection.close();
}).catch((err) => {
  console.log('error', err);
  mongoose.connection.close();
})

我得到的示例输出:

# creating user (uncommented this part)
ubuntu@ubuntu-VirtualBox:~/web/test$ node .
error writing file: null
# calling for $push (re-commented user creation)
ubuntu@ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72be7032bd2f1acad37c95, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95 ],
  _id: 5c72be6a8143fd1aa9416d85,
  name: 'me, myself and I',
  __v: 0 }
izok: true
# again $push
ubuntu@ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c020c2ac7a1b8c65fa36, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95, 5c72c020c2ac7a1b8c65fa36 ],
  _id: 5c72be6a8143fd1aa9416d85,
  name: 'me, myself and I',
  __v: 0 }
izok: true
# and again
ubuntu@ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c023bf62331b97ef096b, title: 'pasta solo', __v: 0 }
updated user: { data: 
   [ 5c72be7032bd2f1acad37c95,
     5c72c020c2ac7a1b8c65fa36,
     5c72c023bf62331b97ef096b ],
  _id: 5c72be6a8143fd1aa9416d85,
  name: 'me, myself and I',
  __v: 0 }
izok: true
# end
ubuntu@ubuntu-VirtualBox:~/web/test$ 

我看不出你的代码有什么问题,但至少你有一些东西可以比较......希望这会有所帮助!


推荐阅读