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 */
});
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
}
});
I have a Simpleschema of a Meteor Collection like this:
Users.attachSchema(new SimpleSchema({: {
...
age: {
type: String,
optional: true
},
...
}));
Now i want to change age's type to type: Number.
Q1: Do i have to run a migration?
Q2: If so, how would the migration look like?
Usually these kinds of question are really broad but I just try to give you some idea on how to think about this issue:
Q1: Do i have to run a migration?
If you have external models, controllers or views which depend on or make use of age (even implicitly) and expect it to be a number, then you need to migrate.
Q2: If so, how would the migration look like?
There are many options here.
For instance you can do a fast migration and just change the schema to use Number and update any code that uses age to handle it as a Number. To ensure compatibility you would require to write a patch, that updates all existing documents with age being a String to Number.
You can also do a much slower migration and keep your model backwards compatible by extending your schema:
Users.attachSchema(new SimpleSchema({: {
...
age: {
type: String,
optional: true
},
ageNum: {
type: Number,
optional: true
}
...
}));
Then you write a helper, that always returns the age in the new format, with fallback to the old structure:
const getAge = userDoc => {
if (!userDoc) return
// compatibility to schema v.2
if (typeof user.ageNum === 'number') {
return user.ageNum
}
// fallback compatibility to schema v.1
if (typeof user.age === 'string') {
return parseInt(user.age, 10)
}
// else do nothing, which returns undefined
// when age and ageNum are not set
}
Then you rewrite the other code to use only getAge to retrieve the age. Note, that you also need to update methods, that insert / update user documents, so they use ageNum instead of age.
Using this approach all new users will have a Number based age by default, while older documents, that have not been updated yet are still supported. This avoids the need to patch all documents for integrity.
There are many other options here and it is hard to give a more precise answer.
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 });
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.
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.