简体   繁体   English

具有一对多关系的 Mongoose 文档引用

[英]Mongoose document references with a one-to-many relationship

I'm working on designing a database structure for a new project, and I'm pretty new to MongoDB, and obviously Mongoose.我正在为一个新项目设计数据库结构,我对 MongoDB 很陌生,显然是 Mongoose。

I've read Mongooses population documentation, where it has a one-to-many relationship, with one Person document to many Story documents, but the part that confuses me is where instead of the Story documents referencing what Person document it belongs to, the Person schema has it setup so it has an array of what Story documents it 'owns'.我读过 Mongooses population文档,它有一个一对多的关系,一个Person文档到许多Story文档,但让我感到困惑的部分是Story文档不是引用它所属的Person文档, Person schema 有它的设置,因此它有一系列它“拥有”的Story文档。

I'm setting up something very similar to this.我正在设置与此非常相似的东西。 But I keep thinking it would be easier when creating new Story documents to have the Person document ID.但我一直认为在创建新的Story文档时使用Person文档 ID 会更容易。 But maybe thats just because I'm more familiar with MySQL relationships using joins.但也许那只是因为我更熟悉使用连接的 MySQL 关系。

If this is the best way to do it (and I'm sure it is, since its in the docs), when new Story documents are created, whats the best way to update the array of stories in the associated People document it belongs to?如果这是最好的方法(我确信它是,因为它在文档中),当创建新的Story文档时,更新它所属的关联People文档中的故事数组的最佳方法是什么? I looked but couldn't find any examples of updating existing documents to add references to other documents (or deleting them for that matter)我看了但找不到任何更新现有文档以添加对其他文档的引用(或为此删除它们)的示例

I'm sure this is an easy solution that I just overlooked or something, but any help would be great.我确定这是一个我刚刚忽略的简单解决方案,但任何帮助都会很棒。 Thanks!谢谢!

Refer to population , here extract an example from Mongoose.参考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);

So the example about, Story model stores related Person._id in Story._creator .所以这个例子, Story模型将相关的Person._id存储在Story._creator中。 When you find a document of Story , you can use populate() method to define which attribute in Person model you want to retrieve at the same time, such as:当你找到一个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);
});

I believe this is what you looking for.我相信这就是您要找的。 Or else, you can use nested collections instead.或者,您可以改用嵌套集合

The previous answers to this question were helpful, but it may be useful to see more detailed code.之前对这个问题的回答很有帮助,但看到更详细的代码可能会有用。 The below code is from my Express.js backend for my application.下面的代码来自我的应用程序的 Express.js 后端。 My application allows users to write reviews.我的应用程序允许用户撰写评论。 When querying the user, I return all of the reviews that the user has made.当查询用户时,我返回用户所做的所有评论。

user_model.js用户模型.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 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 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用户控制器.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 });
      });
  };

As in population docs said正如人口文件所说

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);
});

One way or two way relation单向或双向关系

There is one more possibility you might think about: Do you really need the two way association?您可能会考虑另一种可能性:您真的需要双向关联吗? Or would it be enough to only store the _creator in each Story .或者仅将_creator存储在每个Story中就足够了吗? And do not store the list of stories for each Person .并且不要存储每个Person list of stories The list of stories can still be queried for in a search:仍然可以在搜索中查询故事列表:

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

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

In the end this depends on the requirements of your app.最后,这取决于您的应用程序的要求。 How often do you need the stories of a creator?您多久需要一次创作者的故事?

Here is a great way to create one-to-many relationships.这是创建一对多关系的好方法。

  1. First, we define Comment model in Comment.js.首先,我们在 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. In Tutorial.js, add comments array like this:在 Tutorial.js 中,像这样添加评论数组:


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. In server.js, add createComment function.在 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 }
    );
  });
};

// if you are type script user then: // 如果你是类型脚本用户那么:

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM