Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hsyntes/data-modeling
A Backend application that provides Advanced Data Modeling and Schema Design with MongoDB, mongoose in Node.js & Express
https://github.com/hsyntes/data-modeling
data database datamodeling express modeling mongodb mongoose nodejs schema
Last synced: 25 days ago
JSON representation
A Backend application that provides Advanced Data Modeling and Schema Design with MongoDB, mongoose in Node.js & Express
- Host: GitHub
- URL: https://github.com/hsyntes/data-modeling
- Owner: hsyntes
- Created: 2023-09-12T00:35:26.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2023-09-12T18:02:39.000Z (about 1 year ago)
- Last Synced: 2024-10-03T18:40:24.149Z (about 1 month ago)
- Topics: data, database, datamodeling, express, modeling, mongodb, mongoose, nodejs, schema
- Language: JavaScript
- Homepage:
- Size: 43 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Data Modeling & Schema Design
**Data Modeling** allows how to relate data to other data in the same or different collection such as **embedded** or **referenced** while **Schema Design** allows determmine how data will be **organized**, **validated** and **structured**. It's very similiar to Classes in **Object Oriented Programming**.
Both Data Modeling and Schema Design are crucial in the development of databases and plays a significant role in ensuring data **integrity**, **efficiency**, and **usability**.
## Schema Design
mongoose npm package runs top of MongoDB like a framework and it ensures to implement both **data modeling** & **schema design**.
```javascript
// User Schema Design
const userSchema = new mongoose.Schema({
username: {
type: String,
minlength: [3, "@username cannot be shorter than 3 characters."],
required: [true, "@username is required."],
unique: true,
trim: true,
},// ...
password: {
type: String,
minlength: [8, "Password cannot be shorter than 8 characters."],
maxlength: [32, "Password cannot be longer than 32 characters."],
required: [true, "Password is required."],
trim: true,
select: false,
},// Password validation
passwordConfirm: {
type: String,
required: [true, "Please confirm your password."],
validate: {
validator: function (value) {
return value === this.password;
},message: "Password doesn't match.",
},
},active: {
type: Boolean,
default: true,
select: false,
},
});
```Fields in a **mongoose schema object** could be any data types like Int, Array, String, Boolean, or even ObjectId and can have various properties such as unique, required, max-min length, select etc.
```javascript
const userSchema = new mongoose.Schema({
...
// Referencing
followers: [
type: mongoose.Schema.Types.ObjectID
]
})```
## Types of Data Model
### One to One (1:1)
One to one relation, as the name suggests requires one entity to have an exclusive relationship with another entity and vice versa. In this case, 1 user can only have 1 username.
```javascript
{
"username": "hsyntes"
}
```### One to Many (1:Many)
One to many relation occurs when an instance of an entity has one or more related instances of another entity and keeps the documents as referenced by id or embedded.
```javascript
const userSchema = new mongoose.Schema({
...
// Embedded
"followers": [
{
"_id": ObjectId('618'),
"username": "xyz",
},
{
"_id": ObjectId('23'),
"username": "abc"
}
// ...
]// Referencing
"followers": [
ObjectId("618"),
ObjectId("23")
// ...
]
});```
### Many to Many
Many-to-many relation occurs when instances of at least two entities can both be related to multiple instances of another entity. In this case, 1 user can have many followers, but 1 follower can also be in many users.
```javascript
// Also known Two-Way Referencing
{
"_id": ObjectId('618'),
"username": "hsyntes",
"followers": [
ObjectId('67'),
]
},
{
"_id": ObjectId('67'),
"username": "xyz",
"followers": [
ObjectId('618')
]
},
{
"_id": ObjectId('23'),
"username": "abc",
"followers": [
ObjectId('618'),
ObjectId('67')
]
}
```## Virtual Referencing (Populating)
One document keeps another document(s) as a **virtual**. The referenced documents actually aren't there, but it will exist when the document is querired.
**Virtual Referencing** or **Populating** would be the best solution for higher performance.
### Generate Schema Key
```javascript
const userSchema = new mongoose.Schema({
username: {
type: String,
// ...
},
});// "User" will be the unique Schema Key for this collection
const User = mongoose.model("User", userSchema);
```### Populate Field(s)
The virtual field(s) will never exist under this document except for the document is queried with findOne.
```javascript
// User Schema
const userSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
// ...
},
},
// Enable virtuals
{ versionKey: false,
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);// Virtual Populating
userSchema.virtual("Posts", { // Keep documents under "posts" field
ref: "Posts" // Schema key in another collection for reference
foreginField: "postedBy",
localField: "_id"
});// Query Middleware
userSchema.pre('findOne', function (next) {
this.populate('posts');next();
})
```When the document is queried with 'findOne', the posts will be fetched that the user document related.
### Set the reference
```javascript
exports.createPost = async (req, res, next) => {
try {
const post = await Post.create({
title: req.body.title,
text: req.body.text,
// The current user that create this document will be settled as referenced
postedBy: req.user._id,
});
//...
}
}
```### Set the references as **unique**
$addToSet and $pull operators prevent duplicated fields when a field added/removed from an array in MongoDB.
```javascript
// Starting the transaction
const session = await mongoose.startSession();
session.startTransaction();
await Post.findById(id).session(session);await Post.findByIdAndUpdate(
id,
{
$addToSet: { likes: req.user._id },
},
{ new: true, runValidators: true }
);await Post.findByIdAndUpdate(id, {
$pull: { likes: req.user._id },
});
```Session transaction starts a multi-document transaction associated with the session. Multi-document transactions are available for both shared clusters and replica sets.
Want to see an example with a **real application**, please have a look at my [instamern](https://github.com/hsyntes/instamern-api) project. You can reach out advanced data modeling & schema design with aggregation pipeline, aggregation middleware, setting references with unique, etc.
[![InstaMERN](https://github.com/hsyntes/instamern/blob/main/public/logo.png)](https://instamern.netlify.app)
## 🔗 Contact me
[![linkedin](https://img.shields.io/badge/linkedin-0A66C2?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/hsyntes)