update a subfield with mongoose - javascript

I have a Node.js mongoose schema that looks like this:
var Schema = mongoose.Schema;
var playersSchema = new Schema({
login: String,
pwd: String,
game: {
lat: Number,
lon: Number,
speed: Number,
money: Number
}
});
I want to update only the game.money field of a given element. How can I do that with findByIdAndUpdate ?

MongoDB supports "dot notation" for specifying fields in sub-documents. The same principle applies to either a plain object or an array of objects:
Model.findByIdAndUpdate(id,{ "$set": { "game.money" },function(err,doc) {
});
The $set operator also only updates the specified field or fields without affecting the rest of the document and overwriting the full content.
Also see other field update operators such as $inc to increment an existing number, either up or down.

Related

MongoDB auto pushes arrays into data schemas

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
}
});

Enum on array with mongoose

According to the documentation (https://mongoosejs.com/docs/validation.html#built-in-validators):
Strings have enum, match, maxlength and minlength validators.
(the link does point to an invalid zone of the doc).
Is there a way to declare enum on array with mongoose?
Assuming you mean an array of enums, that would be:
const testSchema = new Schema({
name: String,
enums: [{type: String, enum: ['Coffee', 'Tea']}]
});

Assign key and variable without need to assign in Schema

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.

Big numbers break underscore.js _.contains

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 */
});

mongoose js: should documents be embedded when field is not a list?

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.

Categories