New beginner of MongoDB here, I'm using sails.js(based on expressjs)/waterline(ORM)/MongoDB for this project. I have 2 collections, message and user. In any documents in message, there's this field called author that stores a reference/objectId of a user model:
module.exports = {
autoCreatedAt: true,
autoUpdatedAt: true,
schema: true,
tableName: 'message',
attributes: {
from: {
model: 'user',
required: true
},
to: {
model: 'user',
required: true
},
content: {
type: 'string',
maxLength: 288,
required: true
}
}
};
My user collection goes like this:
module.exports = {
autoCreatedAt: true,
autoUpdatedAt: true,
schema: true,
tableName: 'user',
attributes: {
email: {
type: 'string',
email: true,
unique: true,
required: true
},
name: {
type: 'string',
maxLength: 30
},
username: {
type: 'string',
unique: true,
required: true,
maxLength: 80
},
role: {
type: 'string',
required: true
},
......
},
};
Now I want to do a search query to all messages, given a search keyword, I want to show the paginated results of messages which the author's username and content of the message contains the keyword, how can I achieve that?
By the way, I know I can simply store username instead of user reference id in message documents, but says from Mongodb docs, objectId gives way better performance than string so...
Related
My service uses MongoDB and Mongoose. I have two DBs: Users and Posts. In Posts schema I have parameters:
"author", that contains userId from Users DB
"anonymous", a boolean-parameter that shows if the post is anonymous or not.
I can't solve the problem: when I request data from Posts DB I want to populate author in the "author" parameter only for non-anonymous posts, for anonymous ones I'd like to return null or not to return this parameter at all.
I've tried to use "match", but it doesn't work.
How can I solve this problem?
Thank you.
Code example:
const postSchema = mongoose.Schema(
{
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
anonymous: {
type: Boolean,
required: true,
default: false,
},
content: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
},
{
timestamps: true,
}
);
For population I use pre:
postSchema.pre(/^find/, function (next) {
this.populate({
path: 'author',
select: '_id login',
});
next();
});
I'm currently working on a Library Management System and I'm using MongoDB as my Database so in there I have 4 schemas
User 2) Book 3) Review 4) bookIssue (which handles all the book
issuing)
I'll mention just my User and Book Issue schemas here coz I only want help regarding these two Schemas,
bookIssueHistory: { type: Array, default: null, }
Whenever a book is issued via "bookIssue" Schema I want to store that book's "id" in to the "bookIssueHistory" array (mentioned above) which is in my "userSchema", so I've mentioned both of my schemas below:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please enter your name'],
},
email: {
type: String,
required: [true, 'Please enter your email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please enter a valid email'],
},
photo: String,
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please enter your password'],
minlength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Re-Enter your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Entered password and confirmed password do not match',
},
},
passwordChangedAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
noOfBooksIssued: {
type: Number,
default: 0,
},
currentlyIssuedBooks: {
type: Number,
max: [3, 'You are only allowed to issue 3 books at a time'],
default: 0,
},
bookIssueHistory: {
type: Array,
default: null,
},
active: {
type: Boolean,
default: true,
select: false,
},
});
my book issue schema looks like this:
const bookIssueSchema = mongoose.Schema({
issuedAt: {
type: Date,
default: Date.now,
},
totalIssues: {
type: Number,
default: 0,
},
book: {
type: mongoose.Schema.ObjectId,
ref: 'Book',
required: [true, 'issue must belong to a book.'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'issue must belong to a user.'],
},
});
You can use mongoose middleware, in particular the pre-save hook to run some logic before bookIssue get inserted into the database.
bookIssueSchema.pre('save', function () {
// you can access the current document to be saved by `this`
if (this.isNew) { // apply to new bookIssue only
await this.model('User').findByIdAndUpdate(this.user, {
$addToSet: { bookIssueHistory: this.book } // use $addToSet to ensure distinct values, otherwise use $push
})
}
})
Important: The pre-save hook will be run only when you use BookIssue.create() or bookIssue.save() and not when you run BookIssue.insertMany()
I'm using mongoose and I have users collection shown below, but I now want to allow the user to save a number of articles, an article has a title, subtitle, and body, One user can have many articles.
How can I restructure the users collection to allow the articles to be added
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
timestamps: true,
}
);
I'm using the below to set new data to the user's collection, how do I adapt it to allow me to set and get the new articles detailed above?
const confirmed = await userModel
.findOneAndUpdate(
{ email },
{
$set: { password },
}
)
.exec();
You can set the option strict: false and add(save) new fields to your schema.
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
strict: false,
timestamps: true,
}
);
Here is the docs
I've created two models with associations:
User.js
attributes: {
fullname: {
type: 'string',
required: true
},
username: {
type: 'string',
unique: true,
required: true
},
email: {
type: 'email',
unique: true,
required: true
},
mothertongue: {
type: 'string'
},
passports: {
collection: 'Passport',
via: 'user'
},
words: {
collection: 'Words',
via: 'owners',
dominant: true
}
}
Words.js
attributes: {
word: {
type: 'string',
unique: true,
required: true
},
language: {
type: 'string',
required: true
},
owners: {
collection: 'User',
via: 'words'
}
}
When I'm searching about words owner someone he return back empty array
Words
.find({
owners: req.session.passport.user
})
.exec(function(err, data){
if (err) {
return res.send('error');
}
res.send(data);
})
Also I used .populate('owners') but don't work too.
To find the words a user has:
User.find(1).populate('words')
To find which users own a particular word, use
Word.find(id).populate('owners', {id : ...})
Currently my two models look like this
module.exports = {
tableName: 'accounts',
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
name: {
type: 'string',
required: true
},
password: {
type: 'string',
required: true
},
email: {
type: 'string',
required: true
},
gang_name: {
type: 'string',
required: true
},
family_id: {
type: 'string',
required: true
},
world: {
type: 'string',
required: true
},
messages: {
collection: 'Messages',
via: 'for'
}
}
}
And my Messages model
module.exports = {
tableName: 'messages',
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
title: {
type: 'string',
required: true
},
text: {
type: 'string',
required: true
},
for: {
model: 'Accounts',
required: true
},
by: {
type: 'integer',
required: true
}
}
};
I want to associate the for field of a message with an account so if 'for' field is = 11 load account with id 11... Currently im trying this way
Accounts.find({ id: req.session.accountid }).populate('Messages').exec(function(err, data) {
console.log(data, err);
});
But Im getting an error
Attempting to populate an attribute that doesnt exist
You've got to use the populate method with the attribute name ('messages'), not the model name ('Messages').