I am creating a model for my users. Each user has a property isVerified which is a boolean. I want to be able to call Model.find on the mongoose model and exclude all documents with isVerified === false without having to specify this during the query.
I want to set it in the schema such that whenever Model.find is called those documents are automatically excluded. Any help is appreciated
User model:
const UserSchema:Schema = new Schema({
name: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
index: true,
validate: {
validator: (value:string) => validator.isEmail(value),
message: (props:any) => "Invalid Email Address"
},
},
password: {
type: String,
trim: true,
required: true,
select: false,
minlength: 6,
validate: {
validator: (value:string) => !validator.contains(value, "password"),
message: (props:any) => "Your password cannot contain the word 'password'"
}
},
phoneNumber: {
type: String,
trim: true,
required: true,
validate: {
validator: (value:string) => validator.isMobilePhone(value, 'any', {strictMode: true}),
message: (props:any) => "Please include country code (e.g. +233 for Ghana +44 for the United Kingdom) to phone number"
}
},
isActive: {
type: Boolean,
default: false
}
,
tokens: [
{
token: {
type: String,
required: true
}
}
]
},{
strict: "throw",
timestamps: true
})
Edit:
I did some digging around and it appears I can overwrite the base methods to reimplement the query returned. I attempted to this is as seen below :
UserSchema.statics.find = function () {
let query = UserModel.find.apply(this, arguments);
query.where('isActive').ne(false)
return query;
}
However I obtain the following error
RangeError: Maximum call stack size exceeded
Finally figured it out. Needed to apply the change to the Model object and not the instance UserModel as seen below:
UserSchema.statics.find = function () {
let query = Model.find.apply(this, arguments);
query.where('isActive').ne(false)
return query;
}
Now the find method skips inactive users
Related
I needed a property of date/time which would allow to me get the time at which a certain task was created, I added timestamp property and set it to be true,
But I m not able to compile my code.
The code is perfectly running fine without the timestamp property
const mongoose = require("mongoose");
const Task = mongoose.model(
"Task",
({
title: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: true,
trim: true,
minLength: 100,
},
completed: {
type: Boolean,
default: false,
},
},
{ timestamps: true })
);
module.exports = Task;
I needed a property of date/time which would allow to me get the time at which a certain task was created, I added timestamp property and set it to be true,
But I m not able to compile my code.
The mongoose.model() function of the mongoose module is used to create a collection of a particular database of MongoDB. The name of the collection created by the model function is always in plural format mean GFG to gfss and the created collection imposed a definite structure.
Syntax:
mongoose.model(<Collectionname>, <CollectionSchema>)
Parameters: This function accepts the following two parameters:
Collection name: It is the name of the collection.
Collection Schema: It is the schema of the collection.
Return type: This function returns the Mongoose object.
You need to pass a valid schema for the second argument like below
const mongoose = require("mongoose");
const TodoModel = mongoose.model(
"Task",
new mongoose.Schema(
{
title: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: true,
trim: true,
minLength: 100,
},
completed: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
)
);
module.exports = TodoModel;
More about what is a valid schema refer below
https://mongoosejs.com/docs/schematypes.html
What is the difference between this code:
const userSchema = new Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: false, unique: true },
address: [{ type: mongoose.Types.ObjectID, required: true, ref: "Address" }],
},
{
timestamps: true,
}
);
And this code:
const userSchema = new Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: false, unique: true },
address: { type: [mongoose.Types.ObjectID], required: true, ref: "Address" },
},
{
timestamps: true,
}
);
NOTICE:
In the first code, I surrounded the whole address object with square brackets.
In the second code, I only surrounded the type property of the address with square brackets.
What I want to know is how that will impact the app's behavior.
Is there any difference?
Thanks.
They both declare an array-of-references, but there are some subtle differences.
Let's take this simple schema:
const userSchema = mongoose.Schema({
address: { type: [ String ], required: true, default: undefined }
});
const User = mongoose.model('User', userSchema);
This will apply the options (required and default) to the address array-of-strings as a whole:
// This will fail validation:
// ValidatorError: Path `address` is required
const doc = new User({});
// This will pass validation:
const doc = new User({ address : [] });
Now change the position of the brackets in the schema:
const userSchema = mongoose.Schema({
address: [{ type: String, required: true, default: undefined }]
});
This will apply the options to the elements of the address array:
// This will pass validation, `address` itself isn't required:
const user = new User({});
// This will also pass validation, for the same reason:
const user = new User({ address : [] });
// This will fail validation, because elements of the array are required to have a proper value
// ValidatorError: Path `address.0` is required
const user = new User({ address : [ '' ] });
EDIT: if you want to enforce that the address field is always defined and has at least one element, you have to use a custom validator. For your schema, it would look like this:
address: {
type: [ mongoose.Schema.Types.ObjectID ],
ref: 'Address',
required: true,
default: undefined,
validate: a => Array.isArray(a) && a.length > 0
}
I would like to retrieve with a search field the channels according to the user's first name.
const UserSchema = new mongoose.Schema({
email: {
type: String,
require: true,
trim: true,
unique: true
},
password: {
type: String,
require: true,
trim: true,
unique: true
},
_profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Profile',
require: true,
unique: true
}
})
const ProfileSchema = new mongoose.Schema({
name: {
type: String,
require: true,
trim: true,
unique: true
},
firstname: {
type: String,
require: true,
trim: true,
unique: true
}
})
const ChannelSchema = new mongoose.Schema({
title: {
type: String,
require: true,
trim: true,
unique: true
},
_user: {
type: String,
require: true,
trim: true,
unique: true
}
})
const User = mongoose.model('User', UserSchema)
const Profile = mongoose.model('Profile', ProfileSchema)
const Channel = mongoose.model('Channel', ChannelSchema)
I used to populate function to retrieve the data from the joins
let ChannelSearch = await Channel
.find()
.populate({
path: '_user',
select:'-password',
populate: {
path: '_profile'
}
})
but where I block is how to retrieve the channels based on the user's first name
I can't seem to find how it is possible to make a like request through the populate function
Thank you in advance for your help !! =)
you can your regex to find your data.
let ChannelSearch = await Channel
.find({firstname: /regex/})
.populate({
path: '_user',
select:'-password',
populate: {
path: '_profile'
}
})
or use $regex in yout mongo query.
https://docs.mongodb.com/manual/reference/operator/query/regex/
I believe this is not possible, in the mongoose documentation it is put this:
In general, there is no way to make populate() filter stories based on properties of the story's author. For example, the below query won't return any results, even though author is populated.
const story = await Story.
findOne({ 'author.name': 'Ian Fleming' }).
populate('author').
exec();
story; // null
If you want to filter stories by their author's name, you should use denormalization.
I have a database named "reviews" with a 9.7GB size. It has a collection name products. I was able to optimize the READ request using indexing technical by running the command db.products.ensureIndex({product_name: 1}); When I run the following command db.products.find({product_name:"nobis"}).explain("executionStats"); in MongoDB terminal, it shows that my execution time reduces from 28334ms to 3301ms.
I have the following 2 questions:
1) How do I use explain("executionStats"); on CREATE, PUT and DELETE requests? For example, I got this following error [thread1] TypeError: db.products.insert(...).explain is not a function when I tried to use the following insert function
db.products.insert({"product_id": 10000002,"product_name": "tissue","review": [{"review_id": 30000001,"user": {"user_id": 30000001,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000001,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}, {"review_photo_id": 60000002,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}, {"review_id": 30000002,"user": {"user_id": 30000002,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000003,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}]}).explain("executionStats");
2) Is there any performance Optimization method I can use for the CREATE, PUT and DELETE requests? For example, I am able to use POSTMAN to get the response time of a DELETE request, but the response time takes 38.73seconds.
const deleteReview = (request, response) => {
const id = parseInt(request.params.id);
Model.ProductModel.findOneAndDelete({ "review.review_id": id}, (error, results) => {
if (error) {
response.status(500).send(error);
} else {
response.status(200).send(results);
}
});
};
This is my MongoDB schema:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/reviews', { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
const Schema = mongoose.Schema;
const productSchema = new Schema({
product_id: { type: Number, required: true, unique: true },
product_name: { type: String, required: true, unique: true },
review: [{
review_id: { type: Number, required: true, unique: true },
user: {
user_id: { type: Number },
firstname: { type: String },
lastname: { type: String },
gender: { type: String, enum: ['Male', 'Female', 'Other'] },
nickname: { type: String },
email: { type: String, required: true },
password: { type: String, required: true },
},
opinion: { type: String, required: true },
text: { type: String },
rating_overall: { type: Number, min: 1, max: 5, required: true },
doesRecommended: { type: Boolean, required: true },
rating_size: { type: String, enum: ['a size too small', '1/2 a size too small', 'Perfect', '1/2 a size too big', 'a size too big'], required: true },
rating_width: { type: String, enum: ['Too narrow', 'Slightly narrow', 'Perfect', 'Slightly wide', 'Too wide'], required: true },
rating_comfort: { type: String, enum: ['Uncomfortable', 'Slightly uncomfortable', 'Ok', 'Comfortable', 'Perfect'], required: true },
rating_quality: { type: String, enum: ['Poor', 'Below average', 'What I expected', 'Pretty great', 'Perfect'], required: true },
isHelpful: { type: Number, required: true, default: 0 },
isNotHelpful: { type: Number, required: true, default: 0 },
created_at: { type: Date, required: true },
review_photo_path: [{
review_photo_id: { type: Number },
review_photo_url: { type: String }
}]
}]
});
const ProductModel = mongoose.model('product', productSchema);
module.exports = { ProductModel };
If you do not have one, ensure you have an index of review.review_id on your products collection. You're using that to look up what to delete so it should be indexed.
I read your deleteReview function as deleting the product document that contains the review, not just removing the individual review -- is that what you expect?
You should be able to just $pull the review from the reviews array to get rid of it.
You can use explain on an update like so:
db.products.explain().update({...}, {...});
See: https://docs.mongodb.com/manual/reference/method/db.collection.explain/
You can explain:
aggregate()
count()
find()
remove()
update()
distinct()
findAndModify()
In my user document, I want to individually index email and username as unique fields, so that a duplicate is never entered.
The problem is all of the documentation I have found for multiple unique indexes is for "Compound Indexing", which appears to somehow tie the second index to the first. I don't want that.
All I want is for in my signup step 1 if a user submits an email that already exists for MongoDB to return an error that it already exists, and exact same for step 2, when they set their username.
So I want them indexed and unique separate from each other. I'm using this now and it is tieing the 2 together somehow which is not what I want:
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true
},
username: {
type: String,
required: false,
trim: true
}
})
UserSchema.index({
email: 1,
username: 1
}, {
unique: true
});
Mongoose doesn't have a built-in validation for unique fields. I recommend the package (with this you can use the unique validator on the email and username fields): mongoose-unique-validator. Extend your code with:
let uniqueValidator = require('mongoose-unique-validator');
email: {
type: String,
required: true,
trim: true,
unique: true,
index: true
},
username: {
type: String,
required: false,
trim: true,
unique: true,
index: true
}
UserSchema.plugin(uniqueValidator, {message: 'is already taken.'});
Add unique: true to each field's definition in the schema to create separate, unique indexes for those fields:
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
trim: true,
unique: true
},
username: {
type: String,
required: false,
trim: true,
unique: true
}
})
See the Indexes section on this page for the documentation.