【问题标题】:Mongoose document references with a one-to-many relationship具有一对多关系的 Mongoose 文档引用
【发布时间】:2016-05-01 08:43:42
【问题描述】:

我正在为一个新项目设计数据库结构,而且我对 MongoDB 很陌生,显然是 Mongoose。

我读过 Mongooses population 文档,其中它具有一对多关系,一个 Person 文档与多个 Story 文档,但让我感到困惑的部分是 @987654324 而不是@ 文档引用了它所属的 Person 文档,Person 架构对其进行了设置,因此它具有 Story 它“拥有”的文档的数组。

我正在设置与此非常相似的东西。但是我一直认为在创建新的Story 文档以具有Person 文档ID 时会更容易。但也许那只是因为我更熟悉使用连接的 MySQL 关系。

如果这是最好的方法(我确信它是,因为它在文档中),当创建新的 Story 文档时,更新关联中的故事数组的最佳方法是什么? People 属于它的文件吗?我查看但找不到任何更新现有文档以添加对其他文档的引用(或就此删除它们)的示例

我确定这是一个我刚刚忽略的简单解决方案,但任何帮助都会很棒。谢谢!

【问题讨论】:

  • 你想要什么样的更新?
  • 我不知道有不止一种类型的更新?我试图通过 Mongoose 模型更新这些,但我猜这不是你的意思
  • 你能说出你到底想做什么吗?推入人物故事数组,删除某些内容,修改每个故事或其他内容?
  • 在这种情况下,创建一个新故事,然后将新故事添加到Person.stories

标签: node.js mongodb mongoose mongodb-query mongoose-schema


【解决方案1】:

参考population,这里摘录一个Mongoose的例子。

var mongoose = require('mongoose')
, Schema = mongoose.Schema

var personSchema = Schema({
  _id     : Schema.Types.ObjectId,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Schema.Types.ObjectId, ref: 'Person' },
  title    : String,
  fans     : [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});

var Story  = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);

所以关于Story 模型在Story._creator 中存储与Person._id 相关的示例。当找到Story的文档时,可以使用populate()方法定义同时要检索Person模型中的哪个属性,如:

Story.findOne({_id: 'xxxxxxx'}).populate('person', 'name age').exec(function(err, story) {
  console.log('Story title: ', story.title);
  console.log('Story creator', story.person.name);
});

我相信这就是您要找的。否则,您可以改用nested collections

【讨论】:

  • 好的,所以Story._creator 会自动引用父Person._id?或者您是否必须分别更新 Story._creatorPerson.stories 值?我实际上更关心的是,当您创建一个新故事时.. 更新父母stories 数组的最佳方法是什么?有没有办法通过单个查询将新 ID 推送到它?还是必须查询整个文档,然后将新故事推送到Person.stories 数组,然后更新Person 文档?
  • 您是否必须分别更新Story._creatorPerson.stories 值?或者有没有办法在您创建新的Story 文档时自动填充Person.stories
  • 是的,您必须同时更新两者。 mongoose populate 参考儿童部分
  • 这让我担心它们可能会不同步,如果我有其他脚本在使用该数据库,或者如果有人手动弄乱了它..
  • 您可以使用$push 更新故事来控制它。 atomicity doc 也可能会有所帮助。
【解决方案2】:

之前对这个问题的回答很有帮助,但查看更详细的代码可能会有所帮助。下面的代码来自我的应用程序的 Express.js 后端。我的应用程序允许用户撰写评论。在查询用户时,我会返回用户所做的所有评论。

user_model.js

import mongoose, { Schema } from 'mongoose';


const UserSchema = new Schema({
  firstname: String,
  lastname: String,
  username: { type: String, unique: true },
  reviews: [{ type: Schema.Types.ObjectId, ref: 'Review' }],
}, {
  toJSON: {
    virtuals: true,
  },
});

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

review_model.js

import mongoose, { Schema } from 'mongoose';

const ReviewSchema = new Schema({
  body: String,
  username: String,
  rating: Number,
}, {
  toJSON: {
    virtuals: true,
  },
});

const ReviewModel = mongoose.model('Review', ReviewSchema);
export default ReviewModel;

review_controller.js

// . . .
export const createReview = (req, res) => {
    const review = new Review();
    review.username = req.body.username;
    review.rating = req.body.rating;
    review.body = req.body.body;
    review.save()
      .then((result) => {
        User.findOne({ username: review.username }, (err, user) => {
            if (user) {
                // The below two lines will add the newly saved review's 
                // ObjectID to the the User's reviews array field
                user.reviews.push(review);
                user.save();
                res.json({ message: 'Review created!' });
            }
        });
      })
      .catch((error) => {
        res.status(500).json({ error });
      });
};

user_controller.js

 export const createUser = (req, res) => {
   const user = new User();
   user.username = req.body.username;
   user.email = req.body.email;
   user.save()
       .then((result) => {
            res.json({ message: 'User created!', result });
        })
        .catch((error) => {
          res.status(500).json({ error });
        });
    };

// . . .
// returns the user object associated with the username if any
// with the reviews field containing an array of review objects 
// consisting of the reviews created by the user
export const getUser = (req, res) => {
    User.findOne({ username: req.params.username })
      .populate('reviews')
      .then((result) => {
        res.json(result);
      })
      .catch((error) => {
        res.status(500).json({ error });
      });
  };

【讨论】:

  • 这么好的解释!谢谢
  • 感谢您的详细回答。你能把剩下的代码贴出来吗?我也想知道您是如何添加用户的。谢谢
  • @Eid,我添加了我的 createUser 函数。这有帮助吗?否则你可以在这里找到我的 user_controller.js 文件的代码hastebin.com/egesasutuk.coffeescript
  • 在这种情况下,只有评论 ID 被存储在用户权限中,而不是完整的数据?
  • 有史以来最好的答案非常感谢!
【解决方案3】:

population docs所说

var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });

aaron.save(function (err) {
  if (err) return handleError(err);

  var story1 = new Story({
    title: "Once upon a timex.",
    _creator: aaron._id    // assign the _id from the person
  });

  story1.save(function (err) {
    if (err) return handleError(err);
    // thats it!
  });
  //then add story to person
  aaron.stories.push(story1);
  aaron.save(callback);
});

【讨论】:

  • 如果story1被删除了,还需要aaron.stories.pop(story1)吗?
  • 是否可以用查询结果批量填充父数组? IE。故事 = Story.find({name}) aaron.stories = 故事
【解决方案4】:

单向或双向关系

您可能会考虑另一种可能性:您真的需要双向关联吗?或者只在每个Story 中存储_creator 就足够了。并且为每个Person 存储list of stories。仍然可以在搜索中查询故事列表:

let allStoriesOfOneCreator = Stories.find({_creator: person._id});

https://docs.mongodb.com/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/

这最终取决于您的应用程序的要求。您多久需要一次创作者的故事?

【讨论】:

    【解决方案5】:

    这是创建一对多关系的好方法。

    1. 首先,我们在 Comment.js 中定义 Comment 模型。
    const mongoose = require("mongoose");
    
    const Comment = mongoose.model(
      "Comment",
      new mongoose.Schema({
        username: String,
        text: String,
        createdAt: Date
      })
    );
    
    module.exports = Comment;
    
    1. 在 Tutorial.js 中,像这样添加 cmets 数组:
    
    
    const mongoose = require("mongoose");
    
    const Tutorial = mongoose.model(
      "Tutorial",
      new mongoose.Schema({
        title: String,
        author: String,
        images: [],
        comments: [
          {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Comment"
          }
        ]
      })
    );
    
    module.exports = Tutorial;
    
    1. 在 server.js 中,添加 createComment 函数。
    
    const createComment = function(tutorialId, comment) {
      return db.Comment.create(comment).then(docComment => {
        console.log("\n>> Created Comment:\n", docComment);
    
        return db.Tutorial.findByIdAndUpdate(
          tutorialId,
          { $push: { comments: docComment._id } },
          { new: true, useFindAndModify: false }
        );
      });
    };
    

    【讨论】:

      【解决方案6】:

      // 如果你是类型脚本用户,那么:

      import mongoose from 'mongoose';
      
      interface PromotionAttrs {
        price: number;
        startDate: Date;
        endDate: Date;
      }
      
      export interface PromotionDoc extends mongoose.Document {
        price: number;
        startDate: string;
        endDate: string;
      }
      
      interface PromotionModel extends mongoose.Model<PromotionDoc> {
        build(attrs: PromotionAttrs): PromotionDoc;
      }
      
      const promotionSchema = new mongoose.Schema({
        price: {
          type: Number,
        },
        startDate: {
          type: mongoose.Schema.Types.Date,
        },
        endDate: {
          type: mongoose.Schema.Types.Date,
        },
      });
      
      promotionSchema.statics.build = (attrs: PromotionAttrs) => {
        return new Promotion(attrs);
      };
      
      const Promotion = mongoose.model<PromotionDoc, PromotionModel>(
        'Promotion',
        promotionSchema
      );
      
      export { Promotion };
      
      import mongoose from 'mongoose';
      import { PromotionDoc } from './promotion';
      
      interface ProductAttrs {
        name: string;
        promotions?: PromotionDoc[];
      }
      
      interface ProductModel extends mongoose.Model<ProductDoc> {
        build(attrs: ProductAttrs): any;
      }
      interface ProductDoc extends mongoose.Document {
        name: string;
        promotions?: PromotionDoc[];
      }
      const productSchema = new mongoose.Schema({
        promotions: [
          {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Promotion',
          },
        ],
      });
      
      productSchema.statics.build = (attrs: ProductAttrs) => {
        return new Product(attrs);
      };
      const Product = mongoose.model<ProductDoc, ProductModel>(
        'Product',
        productSchema
      );
      
      export { Product };
      
      const product = await Product.findById(productId);
      
          if (!product) {
            throw new NotFoundError();
          }
      const promotion = Promotion.build({
              price,
              startDate,
              endDate,
            });
            await promotion.save();
            product.promotions?.push();
            await product.save();
      

      【讨论】:

        猜你喜欢
        • 2021-08-23
        • 2013-11-25
        • 2019-10-30
        • 2020-06-12
        • 1970-01-01
        • 2015-10-25
        • 2019-08-11
        • 1970-01-01
        • 2013-11-19
        相关资源
        最近更新 更多