String + autoincrement number on mongoose - node.js

I want to create a String + number every time a value is inserted on database (the number must be autoincrement).
Is it possible to do in Schema? Or do I need to do that before the value's inserted on database?
'question' -> String followed by an autoincrement number
var random = [{
question: 'question1'
},{
question: 'question2'
},{
question: 'question3'
},{
question: 'question4'
}];
const RandomSchema = new Schema({
question: {
type: String,
unique: true
}
})

Autoincrement fields in Mongodb, don't work exactly the same way that they do in an RDBMS. There is a lot more overhead involved. Never the less, creating an auto field is a solved problem. There is also a third party mongoose-autoincrement package that make it a lot easier.
Extending, from that. Your problem is a solved problem too. Because
the string part will always be the same
Simply use string concatenation to prepend 'question' to the auto increment field at display time.

Here is what I implemented with one of the approaches #e4c5 pointed out, without the use of a third-party package.
Define add.js as below:
db.counters.insert(
{
_id: "itemid",
seq: 0
}
)
function getNextSequence(id) {
var ret = db.counters.findAndModify(
{
query: { _id: id },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
db.randoms.insert(
{
question: "question"+getNextSequence("itemid"),
}
)
After starting a mongod instance, do mongo < add.js.

Related

How to update multiple fields (maxLength, maxBreadth or maxArea) of a document based on multiple conditions sent from req.body?

I want to store maximum length, breadth, area ever encountered from a request and store it in the database. In this case, anything can be maximum and based on that I want to update max values in the database for that particular field only.
const body = {
oID: 123, // Primary key
length: 50,
breadth: 50
};
const { length, breadth } = body;
const currentArea = length * breadth;
await totalArea.updateOne(
{ oID: 123 },
{
$set: { $maxLength: maxLength < length ? length : $maxLength }, // I want to update the docs based on mongo query only since this won't work.
$set: { $maxBreadth: maxBreadth < breadth ? breadth : $maxBreadth }, // I want to update the docs based on mongo query only since this won't work.
$set: { $maxArea: maxArea < currentArea ? currentArea : $maxArea } // I want to update the docs based on mongo query only since this won't work.
},
{ upsert: true }
);
In the above example, I have demonstrated the logic using ternary operator and I want to perform the same operation using mongoDB query. i.e update a particular field while comparing it to existing fields from the database and update it if it satisfies the condition.
await totalArea.updateOne(
{ oID: 123
},
{
$max: {
maxLength: length,
maxBreadth: breadth,
maxArea : currentArea
}
},
{ upsert: true }
);
I found this to be working correctly. Thanks to #Mehari Mamo's comment.
If I understand correctly, you want something like this:
db.collection.update({
oID: 123
},
[{$set: {maxLength: {$max: [length, "$maxLength"]},
maxBreadth: {$max: [breadth, "$maxBreadth"]},
maxArea : {$max: [currentArea, "$maxArea "]}
}
}
],
{
upsert: true
})
You can check it here .
The [] on the second step allows you to access the current values of the document fields, as this is an aggregation pipeline.

Mongoose create new data with incremented index-like field

What I want to do:
Whenever I add a new item to the collection (in my case a game), it will have a incremented "index"-like value (in my case I'm naming it index too).
My games collection should looks like:
[
{ "index":0, ... data }
{ "index":1, ... data }
{ "index":2, ... data }
]
The term is so hard to search. I always end up with:
$inc for update. Not this, I want to have incremented number on create.
Schema.index does look like what I want, but somehow it doesn't work at all:
const gameModel = new Schema({
index: {
type: Number,
default: 0
},
players: [{
name: String,
score: Number
}]
}, {
timestamps: {
createdAt: 'date'
}
});
gameModel.index({
index: 1
});
With this I always get index: 0. If I turn off default no index is created.
What do I do now? (I would prefer to keep the _id intact)
You can use a npm package named mongoose-auto-increment which provides this functionality. It is also very easy and well documented

Sequelize is returning integer as string

I using nodejs v4 with sequelize, and I have a model like this:
var Device = sequelize.define('Device', {
id: {
type: DataTypes.BIGINT,
primaryKey: true,
autoIncrement: true
},
tenantId: {
type: DataTypes.BIGINT,
allowNull: false
},
token: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'devices'
});
When I select a device by id the type of id is a string, exemple:
Device.findById(9).then( function(result) {
console.log(result.toJSON().id + 10);
});
The output will be 910, rather than 19, so I look at json and a saw this:
{
id: "9"
tenantId: "123"
token: "adsadsdsa"
}
The id in found device is a string, but I defined it as a number...
Doesn't it should be { "id": 9 } ?
How can I select a device with the types that I defined previously?
BIGINT maximum value is 2^63-1, javascript can safely represent up to 2^53. To be on the safe side libraries return those numbers as strings.
If you want to have numbers instead of strings, you can use this library https://github.com/mirek/node-pg-safe-numbers which deals with this issue.
I found a fix to this problem on sequelize repo.
https://github.com/sequelize/sequelize/issues/4523
The pg module used for sequelize returns bigint as string because bigints are not guaranteed to fit into js numbers. So I change my model to use integer (DataTypes.INTEGER)
IMO, a smoother solution is to force parsing of int to int, instead of strings. Checkout this issue and comments.
Trying to put this line before your logic code worked for me, this forces parsing of int8 to int, instead of strings:
require("pg").defaults.parseInt8 = true;
...
try {
const list = await Posts.findAll();
console.log(JSON.stringify(list));
} catch (e) {
console.log(e.message);
}
By default, sequelize try format your results. You can set raw :true for get raw data

Mongodb Deep Embedded Object Array Query

Hi I am facing a problem in updating an embedded object array using mongoose!
Here is the Schema:
var event = {
eTime : [String]
};
var schedule = {
events: [event]
};
var group = {
gName: {type:String},
sDate: { type: Date, default: Date.now },
schedules: [schedule]
};
MainSchema {
id : String,
groups : [group]
};
The thing which I want to do is that to update the array of eTime in events with an object of schedules. I am using this query
db.demoDb.update({
'id': 'MongoDb',
'groups':{
$elemMatch:{
'gName':'noSql'
}
}
},
{
$push:{
'groups':{
'schedules':{
'events':{
'eTime':'01-Marach-15'
}
}
}
}
}
)
but the schedules->events->eventTime is not updating with a value!!!
What wrong I am doing here?
My Main Scenario is to find the id(MongoDB) with associated gName and then update its schedules array of it.
My find query is working great but can not update the schedules... and events can be many
If I'm reading your schema correctly, the core part is an array containing an array containing an array containing an array of strings. I think you would do much better "inverting" the schema so each document represents an event. Metadata grouping multiple events can be duplicated into each event document. An example:
{
"event" : "cookie bake-off",
"gName" : "baking",
"sDate" : ISODate("2015-03-02T21:46:11.842Z")
}
The update that you are struggling with translates into an insertion of a new event document in the collection.

Mongoose update sub document if exists

I have following model :
var VoteSchema = new Schema({
up : Boolean
, createdBy:{type:ObjectId, ref:'users'}
, createdOn : {type:Date, default:Date.now}
});
var QuestionSchema = newSchema({
title:String
, description:String
, votes : [VoteSchema]
, createdBy:{type:ObjectId, ref:'users'}
, createdOn : {type:Date, default:Date.now}
});
var Question = mongoose.model('questions',QuestionSchema);
Suppose user1 is logged in user and question1 is current / viewing question.The user can upvote({up:true}) or downvote({up:false}) a question at any time. How can I add a new vote if a user1 have not casted a vote for question1 else update the vote.
I have been able to write the following lines of code:
QuestionSchema.statics.castVote = function(questionId, vote) {
//NOTE : vote.createdBy equalsto loggedInUserID
Q.nbind(Question.findOne, Question)({
$and:[
{_id:questionId},
{'votes.createdBy':vote.createdBy}
]
}).then(function(doc) {
if(doc) {
//I am clue less
//doc.votes is a list of votes for this question
// how can I get the particular vote casted by the user - vote.createdBy
}else {
//Question.votes.push(vote);
}
});
});
So you're part of the way there, but of course when you don't find a doc then you will not have a doc to work with in the callback. MongoDB has native ways of handling these sorts of updates, but of course you do need to test for the match as you are.
What we can do here is just work within the true or false condition of where the document exists.
Considering vote.value to be your true or false for the "upvote"
Where you do find that there is a matching document you can issue an update like this:
Question.update(
{
_id: questionId,
"votes.createdBy" vote.createdBy,
"votes.up": {"$ne": vote.value }
},
{ $set: { "votes.$.up": vote.value } }
);
So that matches and uses a positional $ operator to make sure the correct index of the matching item is updated. What I added there makes sure that you don't even touch the document where the vote.vaule is already of the same value.
And in the false condition you want to $push onto the array with the new item:
Question.update(
{
_id: questionId
},
{
$push: {
"votes": {
"up": vote.value,
"createdBy": vote.createdBy
}
}
}
);
Of course add the callback details where writeConcern is applied, which you probably do.

Resources