Passing REST URL parameter as query condition in Mongoose - node.js

Newbie in nodejs/mongoDB here. I've tried, with no success,
to find the answer to my problem before posting here.
I'm creating a simple node RESTAPI get services with Mongoose. I'm trying to pass the field value of the collection to retrieve a specific document.
Like this http://localhost:3000/infrakpi/fieldvalue in the browser
I've written this following piece of code.
app.get('/infrakpi/:system',(req, res) => {
Infrakpi.getInfrakpiBySystem(req.params.system, function(err, infrakpibysystem){
if (err) {
throw err;
}
res.json(infrakpibysystem);
});
});
I have defined the get method in my mongoose model like below.
//Get Infrakpi by System
module.exports.getInfrakpiBySystem = function (system, callback) {
Infrakpi.find({system: 'system'}, callback)
}
when system is passed as fieldvalue in the restURL, I want to retrieve the specific document in the collection.
I understand that this may be very basic question but I get result when I use findById for _id field. But client will call only with the specific field.
Appreciate your help.

Not Sure if i can call it stackoverflow luck. I overlooked the quotes in get method in the model. Once I removed them, it worked.
//Get Infrakpi by System
module.exports.getInfrakpiBySystem = function (system, callback) {
Infrakpi.find({system: system}, callback)
}
I'm leaving the question here without deleting, if it can help someone else in the future.

Related

Mongoose adding incorrect value in UpdateOne

I'm trying to update a User's discord ID based on their key to link the two. Here is my code:
console.log(user.discordID);
User.updateOne({key: user.key},{discordID: user.discordID}, (err, result) => {
if(err){
console.log(err);
return err;
}
console.log("Updated missing User discord ID");
console.log(result);
console.log(user.discordID);
});
I pass in the user object to the function and have verified the values are correct. The console.logs show the correct value, 660955020694650891, both before and after the update. But in the DB itself after the update the value is 660955020694650900. For the life of me I can't figure out why this is happening and it is derailing other functionality. Any help would be greatly appreciated.
Its probably because the number of the discordID is bigger then the 'Number.MAX_SAFE_INTEGER', which means that javascript can not represent the exact number.
See more information here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
You can solve the problem by using BigInt.
See details here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt

Bot Framework Node.js ad hoc message TO A SPECIFIC USER

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.
I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.
//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
//REGISTERED MESSAGE
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)
I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?
function startProactiveDialog(address) {
bot.beginDialog(address, "A notification!");
}
This is how simple I think it should be. But where do you specify the user then?
You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.
They're registered to listen for the event
Their address has been saved inside of the database.
In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.
Edit:
So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.
You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this:
SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.
So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
session.userData.listenForEvent = 1 // Our property we're going to look for.
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.
So first of all, you should save the entire address json object in cosmosDB for later using.
As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:
Guide user to register & Save the user's address object with the account mapping in your DB
Create a Route in Restify or Expressjs for trigger the proactive message:
server.get('/api/CustomWebApi', (req, res, next) => {
//find the user's address in your DB as `savedAddress`
var msg = new builder.Message().address(savedAddress);
msg.text('Hello, this is a notification');
bot.send(msg);
res.send('triggered');
next();
}
);
or if you want to leverage loadSession
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('Hello, this is a notification')
session.endConversation();
}
})
res.send('triggered');
next();
});
I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

What does "this" refers to in the code below?

I was looking at some tutorial, there I found a piece of code where I stuck.Please help me to understand this code.I have marked the questions in my comments.
Code
UserSchema.pre('save', function(next){ //this is a pre hook which is used.understood.
var user = this; // what is the function of this?
var SALT_FACTOR = 5;
if(!user.isModified('password')){ //not understood.From where this function arises?I did not found this anywhere in mongoose tutorial/api.
return next();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt){
if(err){
return next(err);
}
A "pre-save" middleware in Mongoose is "document middleware".
The documentation states:
...in document middleware, this refers to the document being updated.
So this refers to the document to be saved.
This also provides a clue as to what isModified is: it's a document method that can be used to check if a particular field, password in this case, has been modified since the document was retrieved from the database earlier.
In the code you're posting, if the password hasn't been changed, there's not need to hash it again (using bcrypt), so that step is skipped by calling next and returning from the middleware.
isModified is documented here: http://mongoosejs.com/docs/api.html#document_Document-isModified

Should I return an array or data one by one in Mongoose

I have this simple app that I created using IOS, it is a questionnaire app, whenever user clicks play, it will invoke a request to node.js/express server
Technically after a user clicks an answer it will go to the next question
I'm confused to use which method, to fetch the questions/question
fetch all the data at once and present it to the user - which is an array
Fetch the data one by one as user progress with the next question - which is one data per call
API examples
// Fetch all the data at once
app.get(‘/api/questions’, (req, res, next) => {
Question.find({}, (err, questions) => {
res.json(questions);
});
});
// Fetch the data one by one
app.get('/api/questions/:id', (req, res, next) => {
Question.findOne({ _id: req.params.id }, (err, question) => {
res.json(question);
});
});
The problem with number 1 approach is that, let say there are 200 questions, wouldn’t it be slow for mongodb to fetch at once and possibly slow to do network request
The problem with number 2 approach, I just can’t imagine how to do this, because every question is independent and to trigger to next api call is just weird, unless there is a counter or a level in the question mongodb.
Just for the sake of clarity, this is the question database design in Mongoose
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new Schema({
question: String,
choice_1: String,
choice_2: String,
choice_3: String,
choice_4: String,
answer: String
});
I'd use Dave's approach, but I'll go a bit more into detail here.
In your app, create an array that will contain the questions. Then also store a value which question the user currently is on, call it index for example. You then have the following pseudocode:
index = 0
questions = []
Now that you have this, as soon as the user starts up the app, load 10 questions (see Dave's answer, use MongoDB's skip and limit for this), then add them to the array. Serve questions [index] to your user. As soon as the index reaches 8 (= 9th question), load 10 more questions via your API, and add them to the array. This way, you will always have questions available for the user.
Very good question. I guess the answer to this question depends on your future plans about this app.
If you are planning to have 500 questions, then getting them one by one will require 500 api calls. Not the best option always. On the other hand, if you fetch all of them at once, it will delay the response depending on the size of each object.
So my suggestion will be to use pagination. Bring 10 results, when the user reaches 8th question update the list with next 10 questions.
This is a common practice among mobile developers, this will also give you the flexibility to update next questions on the basis of previous responses from user. Like Adaptive test and all.
EDIT
You can add pageNumber & pageSize query parameter in your request for fetching questions from server, something like this.
myquestionbank.com/api/questions?pageNumber=10&pageSize=2
receive these parameters in on server
var pageOptions = {
pageNumber: req.query.pageNumber || 0,
pageSize: req.query.pageSize || 10
}
and while querying from your database provide these additional parameters.
Question.find()
.skip(pageOptions.pageNumber * pageOptions.pageSize)
.limit(pageOptions.pageOptions)
.exec(function (err, questions) {
if(err) {
res.status(500).json(err); return;
};
res.status(200).json(questions);
})
Note: start your pageNumber with zero (0) it's not mandatory, but that's the convention.
skip() method allows you to skip first n results. Consider the first case, pageNumber will be zero, so the product (pageOptions.pageNumber * pageOptions.pageSize) will become zero, and it will not skip any record.
But for next time (pageNumber=1) the product will result to 10. so it will skip first 10 results which were already processed.
limit() this method limits the number of records which will be provided in result.
Remember that you'll need to update pageNumber variable with each request. (though you can vary limit also, but it is advised to keep it same in all the requests)
So, all you have to do is, as soon as user reaches second last question, you can request for 10 (pageSize) more questions from the server as put it in your array.
code reference : here.
You're right, the first option is a never-to-use option in my opinion too. Fetching that much data is useless if it is not being used or has a chance to not to be used in the context.
What you can do is that you can expose a new api call:
app.get(‘/api/questions/getOneRandom’, (req, res, next) => {
Question.count({}, function( err, count){
console.log( "Number of questions:", count );
var random = Math.ceil(Math.random() * count);
// random now contains a simple var
now you can do
Question.find({},{},{limit:1,skip:random}, (err, questions) => {
res.json(questions);
});
})
});
The skip:random will make sure that each time a random question is fetched. This is just a basic idea of how to fetch a random question from all of your questions. You can put further logics to make sure that the user doesn't get any question which he has already solved in the previous steps.
Hope this helps :)
you can use the concept of limit and skip in mongodb.
when you are hitting the api for the first time you can have your limit=20 and skip=0 increase your skip count every time you that api again.
1st time=> limit =20, skip=0
when you click next => limit=20 , skip=20 and so on
app.get(‘/api/questions’, (req, res, next) => {
Question.find({},{},{limit:20,skip:0}, (err, questions) => {
res.json(questions);
});
});

how to use all-or-nothing option for bulksave using node-couchdb-api

For saving bulk documents i am using bulkDocs function and it is working fine. But how to use all-or-nothing option in bulkDocs function. I have tried the following code.
db.bulkDocs(docs, "all-or-nothing",function (err, body, res) {
console.log(body);
});
It's not working for me. Please suggest me to use that option because i need to save all documents or if any error occur none can be saved.
Thanks.
The node-couchdb-api bulkDocs api only supports two arguments -- data and the callback. Instead of passing the option as an argument try embedding it in the data passed to bulkDocs as shown in the CouchDB Documentation and below.
For example, I believe this should do what you want but I don't have a system setup at the moment to test it:
var data = {
"all_or_nothing": true,
"docs": docs
}
db.bulkDocs(data, function(err, body, errs) {
console.log(body);
}

Resources