MongoDB Schema optimization for saving users contacts - javascript

I want to design a schema for storing Contacts of Users.
Here are my existing schemas:
User Schema
_id : ObjectId("5c53653451154c6da4623a77"),
name : “something”,
email : “something”,
password : “something”,
Profile Schema
"_id" : ObjectId("5c53653451154c6da4623a88"),
user_id - ref
mobile : “something”,
company” : “something”,
designation : “something”,
website : “something”,
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
}
I can think of two approaches to the Contact schema but both have some cons:
First approach
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id - ref,
"contacts": [
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
...
]
The problem with the above structure is that when the User updates their Profile the Contact fields can not get the updated value. But in this approach, it is easy to query and retrieve all Contacts of a particular User and send the response back.
Second approach
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id : ref,
contacts: [
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
...
]
In this structure Contacts have the updated User value when the User updates their Profile. But the problem here is while querying I have to fetch the profile id from the Contact schema, then query the Profile schema and return the value to the client as a response.
What happens when there are 30K-50K contacts - do I need to query the DB 50K times? Or is there a better approach?
Building on node.js, using mongoose.

Basically you have a scenario where relational database will be required. But You can also achive this in mongo .
You need to use populate of mongoose. With your second approach. Where you storing profile ids.
User.find({_id: '5c53653451154c6da4623a77'}).populate({
path:'profiles',
options: {
limit: 10,
skip: 0
}}).exec();
Mongoose populate
This query will return related profiles. If you have data like 50K. You must limit the data in one request.
NOTE: Mongodb limit for per document is 16mb. It is not possible to store that much data.
So, Just rethink your database.

If I understand you correctly I think you've correctly identified some pros/cons of each option. Now you have to decide what makes sense for your specific case. Option 1 will be easy for fetching but tedious for updating and keeping in sync with Profiles. Option 2 has more normalized data and will be better for updating but will require more queries to retrieve. So you have to ask yourself some questions.
How important is having normalized data to you?
How will the size of your Profile vs Contact schemas compare? Will you have significantly more Profiles? Significantly less? Orders of magnitude?
What happens more often - that someone updates their profile or that someone queries for contacts? How much more? Orders of magnitude?
To dive deeper into your last two answers - do some estimates, even rough ones if you have to, and then do some math to see what might make more sense.
For example, if you will have 10,000 contacts per user then option #1 will give you a much larger document that can gather all contacts and profiles in a single query. If your users only update their profiles say, on average, once a month but you need to query contacts several times a day this might be the better option. However, if you have users who like to update their profile daily and you need to query contacts maybe once a week then option #2 could make more sense.
At the end of the day it's a design choice that is very specific to your scenario. Good luck!

There are 2 schemas one is for the user and another for orders. One user can create multiple orders like your contacts.
user-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");
const userSchema = new Schema(
{
firstName: {
trim: true,
type: String,
required: [true, "firstName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("firstName is invalid!");
}
}
},
lastName: {
trim: true,
type: String,
required: [true, "lastName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("lastName is invalid!");
}
}
},
email: {
unique: [true, "Email already registered"],
type: String,
required: [true, "Email is required"]
},
password: {
trim: true,
type: String,
require: [true, "Password is required"],
validate(value) {
if (value.length < 6) {
throw new Error("Password should be atleast 6 characters");
}
}
},
mobile: {
trim: true,
unique: [true, "Mobile Number already available"],
type: String,
required: [true, "Mobile Number is required"],
validate(value) {
if (value.length !== 10) {
throw new Error("Mobile Number is invalid!");
}
}
},
gender: {
trim: true,
type: String,
enum: [
"Male",
"Female"
],
required: [true, "Password is required"],
},
dob: {
trim: true,
type: Date,
required: [true, "DOB is required"],
},
Address: {
address: {
trim: true,
type: String,
require: [true, "Owner Address is required"]
},
city: {
trim: true,
type: String,
require: [true, "Owner Address City is required"]
},
state: {
trim: true,
uppercase: true,
type: String,
require: [true, "Owner Address State is required"]
},
pin: {
trim: true,
uppercase: true,
type: Number,
require: [true, "Owner Address Pin is required"],
validate(value) {
if (!(value >= 100000 && value <= 999999)) {
throw new Error("Pin is invalid!");
}
}
}
}
},
{
timestamps: true
}
);
const Users = mongoose.model("Users", userSchema);
module.exports = Users;
Order-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const orderSchema = new Schema({
userId: {
trim: true,
type: Schema.Types.ObjectId,
ref: "users",
required: [true, "User ID is required"]
},
dateOfOrder: {
trim: true,
type: Date,
required: [true, "Date of Order is required"],
},
dateOfMeasurement: {
trim: true,
type: Date,
},
dateOfTrail: {
trim: true,
type: Date,
},
dateOfDelivery: {
trim: true,
type: Date,
},
},
{
timestamps: true
});
const Orders = mongoose.model("Orders", orderSchema);
module.exports = Orders;

The problem you have is actually why mongodb is not the best choice for this scenario. Your data is relational, but you are using a non-relational database.
But for your situation there are some steps to make it better:
First approach
You can choose the first approach and when a user data is updated, also update all contacts. Easier query but hard to keep data in sync.
Second approach
Or you can choose the second approach, but instead of just saving the ref_id also save the contact name (the field is mostly gonna be used for querying). this way it would be easier to keep the data in sync and if search is done by name, you could just do a normal find.

Related

Nodejs app with mongo database tables schema

I want to do a big project API where people can login with Google, send the token and when logged in do some actions saved to Mongo DB.
The problem is the tables structure, or schema. So, I want to do a good app scalable.
I have some tables like Users (with users loggin information) and I want user save his tasks, memories, works and more for him self, and when logged in in another device, get this information and modify.
Do I need to do every table for every user or use same table filtered by user?
For example, I have now this model for product:
const ProductoSchema = Schema({
nameOfProduct: {
type: String,
require: [ true, 'Required name' ],
unique: true
},
state: {
type: Boolean,
default: true,
required: true
},
userOwner: {
type: Schema.Types.ObjectId,
ref: 'Usuario',
required: true
},
priece: {
type: Number,
default: 0
},
category: {
type: Schema.Types.ObjectId,
ref: 'Category',
required: true
},
description: {
type: String
},
disponibility: {
type: Boolean,
default: true
},
img: {
type: String
},
});
Is this correct or this is not scalabe and I need to do all tables for every user?

sort mongodb data based on array express react

i have record in mongoose which kinda look like this(demo) -
{
name:"son",
email:"final#gmail.com",
score:[40,100,30,5]
}
my model is -
const uModel = new Schema({
name: {
type: String,
required: true,
max: 20,
},
email: {
type: String,
required: true,
unique: true,
max: 50,
min: 6
},
password: {
type: String,
required: true,
min: 6
},
profile:{
type: String,
default: ''
},
score:{
type: Array
}})
and here is my post method in express -
const getAllData = (req, res)=>{
userModel.find().sort({score: -1})
.then(user =>{
res.status(200).json(user)
})
.catch(err=>{
res.status(200).json(err)
})}
i have a dashboard and i want to show user with highest score(score is dynamic, changes all the time)
what i want is to sort score array(descending order ) and then sort user based on highest score(descending order). two thing what i want but i have no idea how to do it.
after sorting 0 index of score array should have the higher value and last index lowest.
find().sort({}) does not work for me.
is it possible or should i change my strategy if i should change then what could be better one ??
thanks in advance

How can I display the username of a forum?

I want to find out how I can display the username of a user. For example user admin posts a forum, then I would see Created By: admin on the forum page, instead I can only fish out the ID.
I don't know a much about mongoose and I need someone who is familiar with it.
My Forum Model:
You see I have only ref: 'user' and this is grabbing the ObjectId("") from the User.
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
my userModel:
const UserSchema = mongoose.Schema({
userID: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
isAdministrator: {
type: Boolean,
deafult: false,
},
});
Front-end :
As you can see only in {forum.user} there I can see the id from the user but I want his name not the id
<footer className="blockquote-footer">
Created by:{forum.user}
Created on:{forum.published_on.substring(0,300)}
</footer>
Since you are using 'ref' to reference the user table. When you are fetching a forum document, use the populate() function in mongoose to get all the user details as a sub-object to the forum document.
example: forumShcema.find({_id:<forum_id>}).populate('user').exec()

Want to implement "friend connect request" in React Project, don't know how to design MongoDB Model

When I am working on my React social media project, I want to add a "Friend connect request" feature. I use MongoDB, and here is my "User Model" below.
Now my "Friends" is nested inside UserSchema, but feels like this is not right.
What if I want more stats, for example pending, rejected, block sender...
Let's say: User A sent connect request to B, then B should know, and when B try to send out requests to friends, A should be excluded.
const UserSchema = new Schema({
username: {
type: String,
required: [true, 'User Name is required'],
lowercase: true,
},
email: {
type: String,
required: [true, 'Email or Phone number is required'],
unique: true,
lowercase: true,
},
password: {
type: String,
required: [true, 'Password is required'],
},
thumbnail: {
type: String,
default: 'https://cdn.pixabay.com/photo/2016/08/09/17/52/instagram-1581266_960_720.jpg',
},
createAt: {
type: Date,
default: Date.now,
},
friends: [
{
type: Schema.Types.ObjectId,
ref: 'user',
}
],
}),
I am newbee, first time meet this kind of question, and don't know the design logic behind this feature. Could anyone has experience give me some advances (welcome all of frontend and backend, database design)?

Mongoose: Join Operation, populate isn't possible

I'm currently struggling with a project of mine.
I've got a collection called "Games" and one "Participation".
When a user loggs in, he/she should be able to see the games and the individual participation status.
Therefore, I want to join these two collections but I can't use .populate() because I can't enter the neccessary Participation ID in the Games collection due to the fact, that I don't have the participation ID at the time I create a game. (So I would need to save the participation, remember the created ID and insert THIS id in the games collection afterwards)
The other way round would be a good solution (to populate Games in Participation) but initially, there are no participations in these collection, until a user clicks "Participate" or "Not Participate".
Basically I need a SQL query like that:
select * from games g, participation p where g.gamesId = p.gamesId AND p.username = [...]
Is there any possibility to achieve this?
Otherwise I would need to save every participation as a "Game", having the dependent username and his/her participation status in it.
Wouldn't prefer this solution at all.
Thanks in advance!
In case it helps:
Here are my two collections:
Game:
var gameSchema = mongoose.Schema(
{
isHome: { type: Boolean, required: true },
time: { type: String, required: true, max: 100 },
uzeit: { type: String, required: true, max: 100 },
clubId: { type: mongoose.Types.ObjectId, required: true },
enemy: { type: String, required: true, max: 100 },
place: { type: String, required: true, max: 100 },
(participation: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Teilnahme' }])
});
Participation:
var participationSchema = mongoose.Schema(
{
playerAvailable: { type: Boolean, required: true },
clubId: { type: mongoose.Types.ObjectId, required: true },
gameId: { type: mongoose.Types.ObjectId, required: true, ref: 'Game' },
memberName: { type: String, required: true },
}
);

Categories