I have the following mongodb / mongoose Schema
var forumReplySchema = new Schema({
userid : String,
username: String,
forumid: {type: String, default: '1'},
message: {type: String, default: ''},
time: Number,
});
module.exports = mongoose.model('forum_replies', forumReplySchema);
with following query:
forum_replies.findOne({forumid: forumid}).then(function (reply) {
currentReply.username = user.username;
currentReply.rank = result.rank;
});
Currently username is assigned cause i have username property in the Schema.
Rank is not assigned cause its not in the Schema.
But is there a way for me to assign rank, without having it defined in the Schema?
Edit: aka, assign rank to the forum replies object without the need to save in Db.
you cannot add properties to mongoose document. If you want to do so, you will need to convert it into plain object first. There are couple of ways you could go about it. Following is one of them.
reply.toObject();
Then you can add properties to it.
//you have currentReply in your code, I just want to show general idea here
reply.rank = result.rank;
Is this what you are looking for?
Update:
Thanks for accepting answer :). Also look into lean() option, this returns you plain JS objects without you having to do the conversion manually. lean() also has performance benefit.
Related
I'm being in a case in which I don't know why or what causes it. I can't find any info on this weird problem. So I'm gonna ask here.
//mongoDB data schema
const dataSchema = mongoose.Schema({
userID: String,
userStocks: Array,
stockMarket: Array,
});
module.exports = mongoose.model('Data', dataSchema);
The problem then arrives when creating a new collection in the database:
var regUser = new Data({
});
console.log(regUser); return;
The console.log(regUser) returns both the stockMarket array and userStock array as empty arrays.
Why is this? And how do I prevent it?
It works fine with Number and String type. But with Array type it seems to auto insert it for some reason.
I've tried with delete regUser.stockMarket but it does nothing despite returning true when run. I've also tried removing them with an aggregate using $unset and $merge but still nothing.
So does anyone have any idea what could be causing this weird case? And how would I fix it?
-----------EDIT-----------
Based on the answer from #HuyPham I found out the problem was that I didn't correctly declare it as in array in the schema constructor. The correct way of doing it was:
const dataSchema = mongoose.Schema({
userID: String,
userstocks: {
type: Array,
default: undefined
},
stockMarket: {
type: Array,
default: undefined
}
});
You have defined Array type in mongoose.Schema in the wrong way. Take a look at Arrays in mongoose docs. In case you receive empty array for userStocks and stockMarket maybe it's take Array instance as default value.
In the correct way you found above, make an adjustment from type: Array to type:[] to prevent unpredictable issues when using JavaScript Array instance to set type.
const dataSchema = mongoose.Schema({
userID: String,
userstocks: {
type: [],
default: undefined
},
stockMarket: {
type: [],
default: undefined
}
});
The internet is full of resources for dealing with arrays, but often objects are a more natural fit for data and seemingly more efficient.
I want to store key-value objects under dynamic field names like this:
project['en-US'] = { 'nav-back': 'Go back', ... }
project['pt-BR'] = { 'nav-back': 'Volte', ... }
Doing this seems like it would be more efficient than keeping an array of all languages and having to filter it to get all language entries for a given language.
My question is: How can I insert a key-value pair into an object with a dynamic name using mongoose? And would the object need to exist or can I create it if it doesn't in one operation?
I tried this:
await Project.update(
{ _id: projectId },
{
$set: {
[`${language}.${key}`]: value,
},
});
But no luck regardless of if I have an empty object there to begin with or not: { ok: 0, n: 0, nModified: 0 }.
Bonus: Should I index these objects and how? (I will want to update single items)
Thanks!
In mongoose, the schema is everything. It describe the data you gonna read/store from the database. If you wanna add dynamically a new key in the schema it's gonna be hard.
In this particulary case I would recommend to use the mongodb-native-driver which is way more permissive about the data manipulation. So you could read the data in a specific format and dynamically add your field into it.
To resume my thought, how should your dynamic change happen :
Use mongodb-native-driver to insert the new key into the database data
Modify the mongoose schema you have in the code (push a new key into it)
Use mongoose to manipulate the data afterward
Do not forget to dynamically update your mongoose model or you won't read the new key at the next find.
I solved this using the original code snippet unchanged, but adding { strict: false } to the schema:
const projectSchema = new Schema({ ...schema... }, { strict: false });
While debugging a mongoose callback I found out that either underscore.js or the JavaScript language itself seems to have a problem with big numbers.
In the example below, I store a small number, and a String that contains an ObjectId from mongoose (24 digit number), in order to show the problem as clearly as possible.
var users = ["32", "300000000000000000000002"];
alert(_.contains(users, "32")); // true
alert(_.contains(users, (32).toString())); // true
alert(_.contains(users, "300000000000000000000002")); // true
alert(_.contains(users, (300000000000000000000002).toString())); // false - wait wat ?
My question is, of course, how to make the last call return true ?
I would prefer not to convert the array users to an array of numbers, because (1) the array might be huge in a production context, and (2) I may need to do other operations on the array.
The last returns "3e+23" which is not equals to "300000000000000000000002"
The problem was always that I get an input String which is automatically converted to a number (by a library). So I only had to make the 'foreign key' a smaller number. I eventually created a custom number autogenerate function to circumvent this problem:
var autoIncrement = require('mongoose-auto-increment');
var address = require('./Address');
var connection = mongoose.createConnection(address.mongo());
autoIncrement.initialize(connection);
var UserSchema = new mongoose.Schema({ /* property definitions */});
mongoose.model('User', UserSchema);
UserSchema.plugin(autoIncrement.plugin, {
model: 'User',
startAt: 10000001
});
As a result, I had to change the referencing mechanism:
var GroupSchema = new mongoose.Schema({
users: [{type: Number, ref: 'User'}],
/* other property definitions */
});
At a high level, I'm looking to remove a document from Mongo as well as its referenced documents. Take a look at the following example Schemas:
var studentSchema = mongoose.Schema({
email: String,
name: String,
_class: { type: String, ref: 'Class' },
books: [{ type: Schema.Types.ObjectId, ref: 'Book' }]
});
module.exports = mongoose.model('Student', studentSchema);
var classSchema = mongoose.Schema({
gradeLevel: Number,
students: [{ type: Schema.Types.ObjectId, ref: 'Student' }]
});
module.exports = mongoose.model('Class', classSchema);
var bookSchema = mongoose.Schema({
author: String,
subject: String,
pages: Number
});
module.exports = mongoose.model('Book', bookSchema);
Now, my question is two fold:
1. If I want to .remove() a Class document from my database AND all Student documents referenced to it, what is the best way of doing this? //currently looping through students with the given class id and individually removing, then finally removing the class document.
2. If I want to .remove() a Class document from the db AND all Students AND their Books, is there a way to do this simply through a special remove statement in mongo? //Currently finding all students, removing their books, then remove themselves, then remove their referenced class.
Ideally, I would like a statement the can remove a mongo document, and anything referenced to it, along with any sub-references that sub-document may have. (e.g: Remove a Class and have mongo auto-remove all Students and their Books) Is this possible?
there is no statement that does what you want in mongoDb or mongoose. MongoDB is not the best choice if such operations that span multiple collections are very important for your application ("no automatically supported joins and referential integrity in MongoDB"). You also might need to model your data more "mongo-like" to achieve what you want.
You can do it more efficiently than looping if you have backreferences. The model.find() function returns a query object, that has a .remove(cb) method
mongooose.model('Student').find({_class: myClassToRemove._id}).remove(function(err) {});
For your books this will not work, because Book does not have a reference to Student. But if you do not share books between different students, then you should just store the books within the student object as "embedded documents" instead of using a different model and references. Then they will be deleted automatically when you delete a student.
If you do share one book instance between multiple students, you can not automatically delete the book when you delete a student, because you don't know whether another student uses the same book instance.
Below is my Schema.
var UserModel = new Schema({
id: ObjectId
, email: String
, firstName: String
, lastName: String
, password: String
});
var MessageModel = new Schema({
id: ObjectId
, createdDate: { type: Date, default: Date.now }
, user: String // should this be [UserModel]?
, body: String
});
For my case, every message has a user but only one. Should I Embed UserModel or should I leave the user field as a string. One future use case would be to return a query that has the body of the message, creation date, and user (first name and last name concatenated). Thanks.
Short answer: No, you should not use the UserModel as a subdocument of MessgeModel.
Long answer: First, reconsider your naming. You are actually defining schemas here. Later, you will be associating a model with each of these schemas. So, UserSchema and MessageSchema would be more appropriate here.
But that's not germane. Regarding your question, you're MessageModel schema should not contain embedded documents representing users unless there is a 1-to-1 relationship. However, I expect that each user will be associated with many messages (hopefully). So, you don't want a new copy of the user (each with a new _id) for every message he creates. You only want one canonical document for each user, and a reference to that user in the MessageModel.
Now, using a string reference may be the right choice for you. But if you anticipate running a query on the MessageModel in which you'd like the user attribute to be populated by the actual UserModel document, then you'll want to use a ref.