I have the following schema:
var ListingSchema = new Schema({
creatorId : [{ type: Schema.Types.ObjectId, ref: 'User' }],//LISTING CREATOR i.e. specific user
roommatePreference: { //preferred things in roommate
age: {//age preferences if any
early20s: { type: Boolean, default: true },
late20s: { type: Boolean, default: true },
thirtys: { type: Boolean, default: true },
fortysAndOld: { type: Boolean, default: true }
},
gender: {type:String,default:"Male"}
},
roomInfo: {//your own location of which place to rent
address: {type:String,default:"Default"},
city: {type:String,default:"Default"},
state: {type:String,default:"Default"},
zipcode: {type:Number,default:0},
},
location: {//ROOM LOCATION
type: [Number], // [<longitude>, <latitude>]
index: '2d' // create the geospatial index
},
pricing: {//room pricing information
monthlyRent: {type:Number,default:0},
deposit: {type:Number,default:0},
},
availability:{//room availability information
durationOfLease: {
minDuration: {type:Number,default:0},
maxDuration: {type:Number,default:0},
},
moveInDate: { type: Date, default: Date.now }
},
amneties : [{ type: Schema.Types.ObjectId, ref: 'Amnety' }],
rules : [{ type: Schema.Types.ObjectId, ref: 'Rule' }],
photos : [{ type: Schema.Types.ObjectId, ref: 'Media' }],//Array of photos having photo's ids, photos belong to Media class
description: String,//description of room for roomi
status:{type:Boolean,default:true}//STATUS OF ENTRY, BY DEFAULT ACTIVE=TRUE
},
{
timestamps:true
}
);
The application background is like Airbnb/Roomi app, where users can give their rooms/places on rent. Now i want to implement a filter for a user finding the appropriae listing of room.
Here creatorId, rules, amneties are refIds of other schemas. I want to write a query which will give me listings based on several parameters,
e.g. user can pass rules, pricing info, some amneties, gender etc in req queries.
The query parameters depends upon user's will.
Is there any way to do nested query like thing for this?, like the way we did in SQL.
Well, mongodb is not made to be used as relational DB.
instead, i would suggest transforming amenities array into an array of objects with the amenities embeded inside the Listings schema.
so you can query as follows:
// Schema
ListSchema = mongoose.Schema({
....
amneties: [{aType: 'shower'}]
// or you can make it a simple array of strings:
// amneties: ['shower']
....
})
// query
Listings.find({'amneties.aType' : <some amenity>})
there are no joins in mongodb, you can still make "joins" as mongoose calls them populate, but they are happening on your server, and every populations requires a round trip to the server.
if you still wish to use references to the amneties collection, you should query it first and populate the Listing object on them.
Related
I'm working on an e-commerce project in Express and MongoDB. I'm confused with architecture on how to make relationship between two models such that if I delete one element from a table, all of it's associations should be deleted. CASCADE delete, if I'm using the correct term.
I'm not a database designer, just learning Express and MongoDB. So apologies if my schema is not that good.
I have two tables (or documents in MongoDB). One is Order with schema below:
const orderSchema = new mongoose.Schema({
shippingInfo : {
type: mongoose.Types.ObjectId,
ref: 'Address'
},
user : {
type: mongoose.Types.ObjectId,
ref: 'User',
required: true
},
orderItems: [
{
type: mongoose.Types.ObjectId,
ref:'OrderItem'
}
],
totalPrice: {
type: Number,
required: true,
default: 0.0
},
status: {
type: String,
enum: ['processing', 'shipped', 'delivered','cancelled'],
default: 'processing'
},
deliveredAt: {
type: Date,
}
})
and OrderItems
const orderItemSchema = new mongoose.Schema({
product: {
type: mongoose.Types.ObjectId,
ref: 'Product'
},
name: {
type: String,
required: true
},
quantity: {
type: Number,
required: true
},
price: {
type: Number,
required: true
},
image: {
type: String,
required: true
},
})
I want if I delete an Order, all of its OrderItems should be deleted right away (using remove middleware in Order).
I know that Django has something called on_delete=model.CASCADE when we create relationships, but I'm unaware of such thing in Mongoose.
I don't want to explicitly make another API request to search for and delete all OrderItems that are referenced in orderItems array in an Order, once it is deleted. There has to be a better approach for this.
Another post on Stack Overflow suggested that in remove middleware of Order I should do something like
OrderItem.find({ order_id: this._id }).remove().exec();
That would require me to refer order_id in OrderItem right?
And this would create circular dependency since OrderItem would require Order to be created first and vice versa.
What should I do here? Should I change the schema for both tables i.e. remove orderItems entry from Order and instead add order_id in OrderItem? Or is there a Mongoose way to overcome this situation?
I have two mongodb model as following.
const CompanySchema = new Schema(
{
sections: [{
name: { type: String },
budgets: [{ // indicates from CalcSchema
index: { type: Number },
title: { type: String },
values: [Number],
sum: { type: Number, default: 0 },
}],
}]
},
{ timestamps: true }
);
const CalcSchema = new Schema({
budget: {
type: Schema.Types.ObjectId, // I want to populate this field. this indicates budget document in Company model
ref: "Company.sections.budgets" //it's possible in mongoose?
},
expense: {
type: Number,
default: 0
}
});
budget field indicate one of budgets field in CompanySchema.
So I want to populate when get Calc data.
But I don't how to populate embedded document.
I tried set ref value to ref: "Company.sections.budgets". but it's not working.
Please anyone help.
Finally, I found answer myself.
There is useful plugin for it.
https://github.com/QuantumGlitch/mongoose-sub-references-populate#readme
And I learned that my schema structure was wrong. It's anti-pattern in mongodb.
Task model
const TaskSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
ref: 'User'
},
title: {
type: Schema.Types.String,
required: true
},
description: Schema.Types.String,
createdDate: {
type: Schema.Types.Date,
default: Date.now()
},
position: {
type: Schema.Types.Number,
default: 0
},
categoryId: [{
type: Schema.Types.ObjectId,
ref: 'Category'
}]
});
Category model
const CategorySchema = new Schema({
title: {
type: Schema.Types.String,
required: true
},
description: {
type: Schema.Types.String,
},
categoryThumbnail: {
type: Schema.Types.String,
default: ''
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
createdDate: {
type: Schema.Types.Date,
default: Date.now()
}
});
When creating a task, the user can assign a category. Do I need to check the category owner before adding the task to Mongodb. If so, what is the best way to do this? Options:
1. Make a request to the database for all categories and check the user id.
2. Store the category id in the user document and, upon receipt of the request, check this list.
So if the User can create multiple categories and each category is only accessible by the User who created it you have a one-to-many association. In this situation it seems your Option 1 is what you want. Keep the user id on the category and then query all categories that have the user id you're looking for.
Edit:
If possible, I would recommend that you limit the categories the user sees when creating a task to only be categories created by that user. If that is not possible, then you could do one query to grab all the categories from the list of category IDs sent to the server and loop through the results checking if the user IDs are the same.
Category.find({
'id': { $in: [
mongoose.Types.ObjectId('4ed3ede8844f0f351100000c'),
mongoose.Types.ObjectId('4ed3f117a844e0471100000d'),
mongoose.Types.ObjectId('4ed3f18132f50c491100000e')
]}
}, function(err, docs){
docs.forEach(item => {
return item.userId === userId; //compare to userId that sent the request
}
});
I want to implement an aspect of chatting application, i.e number of unseen / new messages for a particular chat.
Here's my chat Schema :
var chatSchema = new Schema({
name: { type: String, default: "friend_chat" },
chat_type: { type: String, default: "group" },
lastMessage: {
type: Schema.Types.ObjectId,
ref: 'Message',
default: ObjectId("000000000000000000000000")
},
participants: [{
_id: { type: Schema.Types.ObjectId, ref: 'Member' },
//used to keep track of new messages/messages count
lastMessageId: { type: String, default: "000000000000000000000000" },
//status can be admin,blocked,member
status: { type: String, default: "member" }
}],
isDeleted: { type: Boolean, default: false }
})
Here's my Message Schema :
var messageSchema = new Schema({
senderId: { type: Schema.Types.ObjectId, ref: 'Member' },
type: String,
content: String,
timeCreated: { type: Date, default: Date.now() },
chatId: { type: Schema.Types.ObjectId, ref: 'Chat' },
isRead: { type: Boolean, default: false },
isDeleted: { type: Boolean, default: false }
})
So the lastMessageId key in the participants arrays basically informs of the Id of the last Message the participant has seen. Therefore, to quickly retrieve the number of unseen Messages for a "single" chat window,how can I do this:
Message.aggregate([{$match:{chatId:INPUT_CHAT_ID,
senderId:PARTICIPANT'S_ID/req.session.userId,_id:
{$gt:participants.index.lastMessageId}}}])
For the above what should be the approach for getting the index.
Currently I have just implemented a linear search at frontend which gives me the lastMessageId as a parameter in the request to get around the problem but I want to know what should be the approach here.
Also suppose by linear search at the frontend,I am able to get the index,since frontend also has the copy of chat document(mongodb document) for displaying. Now I want to query the same thing but for multiple chats. Of course using Message.aggregate for each chatId is inefficient so what is the recommended way here.Currently from the front end I receive a JSONObject "chats" of this form:
`{ids:CHAT_IDs_array,last_Message_Ids :LAST_MESSAGE_IDS_array} `
in order meaning that the first index of CHAT_IDs array corresponds to first index of LAST_MESSAGE_IDS array
My attempted,incomplete,appraoch for this problem is as below :
`Message.aggregate([{chatId:{$in:chats.id},senderId:req.session.userId, _id:{$gt : ????}}])`
How to possibly make chats.id array index correspond with chats.last_Message_Ids index.
Also I would be appreciative if the answer doesn't impose any changes to the "Schema". Thanks
I recently started a project using mongodb and nodejs to build a restful web service. Unfortunately mongodb is very new to me, and coming from the relational databases world I'm asking my self a lot of questions.
Let me explain you my problem :
The goal is to build a sort of content management system with social features like a user can post topics that can be shared and commented.
I have 2 possibilities to do this the one using a reference to get topics posted by a user, the second using topics as embedded document of user instead of reference.
So basically I can have these 2 schemas :
var UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
name: {
type: String
},
first_name: String,
phone: String,
topics: [Topic.schema]
});
var TopicSchema = new Schema({
_creator: {
type: String,
ref: 'User'
},
description: String,
comments: [Comments.schema],
shared_with: [{
type: Schema.ObjectId,
ref: 'User'
}] //[{ type: String, ref: 'User'}]
});
var CommentSchema = new Schema({
_creator: {
type: String,
require: true
},
text: {
type: String,
required: true
},
});
and
var UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
name: {
type: String
},
first_name: String,
phone: String,
topics: [{ type: Schema.ObjectId, ref: 'Topics'}]
});
var TopicSchema = new Schema({
_creator: {
type: String,
ref: 'User'
},
description: String,
comments: [Comments.schema],
shared_with: [{
type: Schema.ObjectId,
ref: 'User'
}] //[{ type: String, ref: 'User'}]
});
var CommentSchema = new Schema({
_creator: {
type: String,
require: true
},
text: {
type: String,
required: true
},
});
So the first schema uses 1 collection of user document and the second use 1 collection for the user and 1 collection for the topics, this implies to make for example, 2 finds queries to retrieve a user and it's topics but it is also easyer to query directly the topics.
Here is the request I use to retrieve a specific topic with some user info with the first schema :
User.aggregate([
{$match: {
"topics._id":{$in:[mongoose.Types.ObjectId('56158c314861d2e60d000003')]}
}},
{ $unwind:"$topics" },
{$match: {
"topics._id":{$in:[mongoose.Types.ObjectId('56158c314861d2e60d000003')]}
}},
{ $group: {
_id: {
_id:"$_id",
name:"$name",
first_name:"$first_name"
},
topics:{ "$push": "$topics"}
}}
]);
So the question is, what do youh think ? Which is the good schema in your opinion ?
Thanks in advance.
Better solution: using a reference to get topics posted by a user
For this database use, one typically needs to consider the MMAPV1 document size limit (16MB). Putting user, topic, and comments in one document allows the document to grow without bound. If each topic is a page of text (1K), then each user could have about 16,000 topics before the limit is reached. That seems huge, but what happens if you decide to put images, videos, sounds in the topic as the product matures? Converting from an embedded to a normalized schema later would be a lot more work than a simple design choice today.
Similarly, if the comments could grow to cause a topic to exceed the 16MB limit, they should be in a separate collection. Unlikely? Probably. But if you are writing something that will become, say, the Huffington Post - check out comments on their popular articles.
Here is mongo's advice on data model design