Reputation: 17711
I have designed a collection for some reviews.
The review collection schema contains both posts and topics.
Posts properties (key, phone, date, contents, author.name) are direct children of review collection.
Topics properties (key, title) are children of topic object, which is a child of review collection.
Each post belongs to a topic.
Each post key is unique, each topic key is unique.
If many posts belong to a topic, topic data is repeated for each review (NoSQL is not ACID, right? :-)
The question is: is the decision to duplicate topic properties correct, or I should use different collections for posts and topics?
This is my model:
var reviewSchema = new mongoose.Schema({
key: String,
phone: String,
date: Date,
contents: String,
author: {
name: String,
},
topic: {
key: String,
title: String,
},
});
reviewSchema.index({ 'key': 1 }, { unique: true });
reviewSchema.index({ 'phone': 1 }, { unique: false });
reviewSchema.index({ 'topic.key': 1 }, { unique: false });
Upvotes: 0
Views: 863
Reputation: 348
If you wish to avoid duplication, create a separate schema for a Topic
, then reference it in your Review
s:
var TopicSchema = new mongoose.Schema({
key: String,
title: String
});
var ReviewSchema = new mongoose.Schema({
key: String,
phone: String,
...
topic: {type: Schema.Types.ObjectId, ref: 'Topic'}
});
var Topic = mongoose.model('Topic', TopicSchema);
var Review = mongoose.model('Review', ReviewSchema);
From here, use the populate()
method for when you want to insert a Review
with a Topic
as a subdocument. Based on the fact that you are storing author
in its own document, you may consider following that same pattern there.
I'm curious about your use of key
. MongoDB creates a unique _id
by default on top level documents, a kind of primary key. If that was your intention for key
, you should probably let MongoDB handle it.
But at the end of the day, there is no "correct" solution to your problem, only a comparison of tradeoffs. An advantage of MongoDB is the ability to "store what you query for" with ease, and since Topic
s are quite small, it may be worth the duplication if you want topics every time you fetch a review. MongoDB is not ACID within a collection (I can't speak for other NoSQL options), so with this method, updating all the embedded topics at once could cause brief discrepancies for users.
// Get entire review in one go, including subdocuments!
Review.findOne( { "key": "myReview" }, (err, doc) => { /* do things */ } );
// On bulk topic updates, not all topics change at once (not ACIDic)
Review.update(
{ topic.title: 'foo' },
{ topic.title: 'bar' },
{ multi: true },
(err, doc) => {/* callback */ }
);
If you're coming from a SQL background, the populate()
paradigm described above will feel far more comfortable. And since MongoDB is ACIDic per document, updating a topic once will suffice for all other documents that reference it. Behind the scenes, this will require Mongoose to make at least two queries: once for the Review
, then again for the referenced Topic
.
// To replace refs with documents two queries behind the scenes
Review.findOne( { key: 'myReview' } )
.populate('topic').exec( (err, review) => { /* do things */ })
// But updating a single topic is ACIDic, since reviews only contain references
Topic.update( { key: 'foo' }, { title: 'sci-fi' }, (err, res) => {/* more stuff */ } )
In my experience, unless you're pushing your query pipeline to the limit and want to cut response times at all costs, separate schemas with populate()
is worth the tradeoff of extra queries.
Upvotes: 4