Mongo DB Schema Design patterns
Use the aggregation framework to apply the inheritance pattern
Book store app, where books can be audio book, printed book, and ebook.
{
...
"title": "", // common
"author": "", // common
"authorId": "", // common
"rating": 4,
"genres": [""], // common
"pages": 0,
"price": 0, // common
"publisher": "", // common
...
"product_type":"ebook", // Defines the type of document
"download_url": "",
}
Audiobook where there are audiobook specific fields.
{
...
"download_url":""
...
"product_type": "audiobook",
"narrators": [""],
"duration": "",
"time_by_chapter": [{"chapter": "","end":""}]
}
{
...
"product_type": "printedbook",
// no requirement of download_url so remove it.
}
Consider if there is no product type is not present in the books collection but all three types of books are present in the collection then aggregation pipeline can be applied to add product type field.
var apply_inheritance_pattern_to_books_pipeline = [
$project: {
_id: "$_id",
product_id:: "$product_id",
product_type: {
$ifNull: ["$product_type", "unspecified"],
}, // just adding product type field with unspecified value.
description: {
$ifNull: [
"$desc",
"$description",
"$details",
"Unspecified",
]
},
authors: {
$ifNull: [
"$authors",
["$authors"],
"Unspecified",
],
},
publisher: "$publisher",
...
},
{
$merge: {
into: "books",
on: "_id",
whenMatched: "replace",
whenNotMatched: "discard",
},
},
]
db.books.aggregate(apply_inheritance_pattern_to_books_pipeline)
var cleanup_audiobook_entries_in_book_pipeline = [
{
$match: {
$and: [{product_type: "Unspecified"},{length_minutes: {$gte: 0}}],
},
},
{
$set: {product_type: "audiobook"},
},
{
$merge: {
into: "books",
on: "_id",
whenMatched: "replace",
whenNotMatched: "discard",
},
},
];
Common computations:
Mathematical:
For example rating
NewAverage = (avgRating*reviewCount + newRating)/(reviewCount+1)
Roll-up:
Merging data together. Allows view data as a whole.
use bookstore;
var rollup_product_type_and_number_of_authors_pipeline = [
{
$group: {
_id: "$product_type",
count: {
$sum: 1,
},
averageNumberOfAuthors: {
$avg: {
$size: "$authors",
},
},
},
},
]
db.books.aggregate(rollup_product_type_and_number_of_authors_pipeline )
Embed data from other documents into main document to reduce lookup
(equivalent of joins in SQL) operation on each query.
This pattern generates statistically valid number that is not exact