Problem: When creating the first document for a user, query takes too long
I'm creating some report, of the schema Report. I also have a UserSchema. I create a document in my UI and pass that data to a post request which is this:
exports.addSubReport = function(req,res) {
var id = req.body.masterform;
var subform = new Report();
var myDate = Date();
subform.title = req.body.title;
subform.date = req.body.date;
subform.date = myDate;
subform.owner = req.user;
subform.body = req.body.body;
subform.save();
Report.findById(id, function (err, report) {
if(err) {
res.redirect('/404NotFound');
}
else {
report.subreport.push(subform);
subform.parentReport = report;
report.save();
}
});
User.findById(req.body.id, function (err, user) {
user.forms_created.push(subform);
subform.owner = req.user;
subform.authors[0] = user.profile.firstName + " " + user.profile.lastName;
subform.author = user;
subform.save();
});
res.json(req.body);
};
this works fine and creates the object the way I want it to, however after creating the document, I set the state in my UI to 'Wait' until I can recieve the JSON with this new Report I just created. This is the GET request code:
exports.allMyReports = function(req, res) {
var id = req.user._id;
var totalproc = 0;
var dupe = [];
Report.find({"author" : id}, function (err, form) {
dupe = form;
dupe.forEach(function (person) {
User.findById(person.author, function (err, user) {
if (!err) {
person.authors[0] = user.profile.firstName + " " + user.profile.lastName;
person.save();
totalproc = totalproc + 1;
}
if (totalproc == dupe.length) {
res.json(dupe);
}
}
);
});
});
};
However the problem is that on every first report I create for a user, it takes an extremely long time. It's most likely the query of searching for it by author but than I thought well.... if the user has 15 documents already how does it even find all those documents instaneously? I have no idea why it takes so long in this case though and I haven't been able to come up with a solution yet but I think it has to do with how I'm querying.
Here is a sample of how i do it in the UI:
_onCreateReport = () => {
const title = React.findDOMNode(this.refs.title).value;
const date = React.findDOMNode(this.refs.date).value;
const body = React.findDOMNode(this.refs.body).value;
ReportsActions.addNewReport({
title: title,
date: date,
body: body
});
ReportsActions.getMyReports();
}
I perform the action of adding a new report ('post' request to API), and then getMyReport 'get' request to api for all reports belonging to me, once that returns it shows a new render of 3 buttons, one to view that document, one to view all my documents, one to create another report.
All I did, was request all the documents, and figure it out in the front-end. It reduced the time of the ajax call and I just filtered it out in my front-end which performs quick and doesn't hold the server up.
Related
I'm creating an electron app using pouchDB and I want the app to be able to diferents customers and sync the data between them. As an example I'm making the tutorial: https://github.com/nolanlawson/pouchdb-getting-started-todo, I adapt the code to electron and I created a noSQL database at cloudant.
At the moment I can save data but I cannot sync with my remote db that is in cloudant. Here is the endpoint I'm using to sync data between both database.
Here is the error that I'm getting.
Here is the code of my script.js
(function() {
'use strict';
var $ = document.querySelector.bind(document);
var ENTER_KEY = 13;
var newTodoDom = document.getElementById('new_todo');
var syncDom = document.getElementById('sync-wrapper');
// EDITING STARTS HERE (you dont need to edit anything above this line)
var NodePouchDB = require('pouchdb');
var db = new NodePouchDB('todos');
var couchdb = require('felix-couchdb')
var remoteCouch = couchdb.createClient(5984, 'https://ac725f4e-29ec-4614-8e96-02ebc74a529b-bluemix.cloudant.com/')
db.info(function(err, info) {
console.log("is working", info)
db.changes({
since: info.update_seq,
live: true
}).on('change', showTodos);
});
// We have to create a new todo document and enter it in the database
function addTodo(text) {
var todo = {
_id: new Date().toISOString(),
title: text,
completed: false
};
db.put(todo).then(function (result) {
console.log("everything is A-OK");
console.log(result);
}).catch(function (err) {
console.log('everything is terrible');
console.log(err);
});
}
// Show the current list of todos by reading them from the database
function showTodos() {
db.allDocs({include_docs: true, descending: true}).then(function(doc) {
redrawTodosUI(doc.rows);
}).catch(function (err) {
console.log(err);
});
}
function checkboxChanged(todo, event) {
todo.completed = event.target.checked;
console.log(todo);
db.put(todo);
}
// User pressed the delete button for a todo, delete it
function deleteButtonPressed(todo) {
db.remove(todo);
}
// The input box when editing a todo has blurred, we should save
// the new title or delete the todo if the title is empty
function todoBlurred(todo, event) {
var trimmedText = event.target.value.trim();
if (!trimmedText) {
db.remove(todo);
} else {
todo.title = trimmedText;
db.put(todo);
}
}
// Initialise a sync with the remote server
function sync() {
syncDom.setAttribute('data-sync-state', 'syncing');
var opts = {live: true};
db.sync(remoteCouch, opts, syncError);
}
// EDITING STARTS HERE (you dont need to edit anything below this line)
// There was some form or error syncing
function syncError() {
syncDom.setAttribute('data-sync-state', 'error');
}
// User has double clicked a todo, display an input so they can edit the title
function todoDblClicked(todo) {
var div = document.getElementById('li_' + todo._id);
var inputEditTodo = document.getElementById('input_' + todo._id);
div.className = 'editing';
inputEditTodo.focus();
}
// If they press enter while editing an entry, blur it to trigger save
// (or delete)
function todoKeyPressed(todo, event) {
if (event.keyCode === ENTER_KEY) {
var inputEditTodo = document.getElementById('input_' + todo._id);
inputEditTodo.blur();
}
}
// Given an object representing a todo, this will create a list item
// to display it.
function createTodoListItem(todo) {
var checkbox = document.createElement('input');
checkbox.className = 'toggle';
checkbox.type = 'checkbox';
checkbox.addEventListener('change', checkboxChanged.bind(this, todo));
var label = document.createElement('label');
label.appendChild( document.createTextNode(todo.title));
label.addEventListener('dblclick', todoDblClicked.bind(this, todo));
var deleteLink = document.createElement('button');
deleteLink.className = 'destroy';
deleteLink.addEventListener( 'click', deleteButtonPressed.bind(this, todo));
var divDisplay = document.createElement('div');
divDisplay.className = 'view';
divDisplay.appendChild(checkbox);
divDisplay.appendChild(label);
divDisplay.appendChild(deleteLink);
var inputEditTodo = document.createElement('input');
inputEditTodo.id = 'input_' + todo._id;
inputEditTodo.className = 'edit';
inputEditTodo.value = todo.title;
inputEditTodo.addEventListener('keypress', todoKeyPressed.bind(this, todo));
inputEditTodo.addEventListener('blur', todoBlurred.bind(this, todo));
var li = document.createElement('li');
li.id = 'li_' + todo._id;
li.appendChild(divDisplay);
li.appendChild(inputEditTodo);
if (todo.completed) {
li.className += 'complete';
checkbox.checked = true;
}
return li;
}
function redrawTodosUI(todos) {
var ul = document.getElementById('todo-list');
ul.innerHTML = '';
todos.forEach(function(todo) {
ul.appendChild(createTodoListItem(todo.doc));
});
}
function newTodoKeyPressHandler( event ) {
if (event.keyCode === ENTER_KEY) {
addTodo(newTodoDom.value);
newTodoDom.value = '';
}
}
function addEventListeners() {
newTodoDom.addEventListener('keypress', newTodoKeyPressHandler, false);
}
addEventListeners();
showTodos();
if (remoteCouch) {
sync();
}
})();
To get to where the problem sits, can you verify that you can speak to the Cloudant database normally, that is using curl from the command-line? Using curl, fetch a document by its _id, perhaps a document you created manually using the Cloudant dashboard. That should shake out any problems with authentication: I note you're using IAM, which isn't always straight-forward -- and to my knowledge, not supported by PouchDB (or wasn't, last time I looked).
If that is the problem, create a new Cloudant instance with IAM+Legacy credentials.
I am trying to build a EJS form with three fields and I need to pass two sets of data to it at the same time: users and books using promises. Unfortunatly books are not getting passed and stay 'undefined'. I cannot figure out why.
Form
Textfield (irrelevant for this example)
Dropdown box with a list of users
Dropdown box with a list of books
For (2) and (3) I query my mysql database to get the data so that I can fill the form drop-down-boxes.
/controller.js
const User = require('../models/user.js');
const Book = require('../models/book.js');
exports.getAddNeueAusleihe = (req, res, next) => {
// storing users and books in this object
let view_data_for_my_view = {};
// fetch users for dropdown-box nr.1
User.fetchAll()
.then(([users_rows]) => {
view_data.users = users_rows;
// fetch books for dropdown-box nr. 2
return Book.fetchAll(([books_rows]) => {
view_data.books = books_rows;
});
})
.then(() => {
// send data to view
res.render('neue-ausleihe.ejs', {
users: view_data.users,
books: view_data.books,
pageTitle: 'Neue Ausleihe'
});
});
}
The User-fetch works fine. But the Book-fetch does return "undefined", although the SQL code in the books model works fine. It actually jumps into the books model, but does not get the values to the view. Here is my SQL-code for models.
/models/user.js
const db = require('../util/database.js');
module.exports = class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
static fetchAll() {
return db.execute('SELECT * from dbuser WHERE UserFlag = "active"');
};
}
/models/books.js
const db = require('../util/database.js');
module.exports = class Book {
constructor(id, name) {
this.id = id;
this.name = name;
}
static fetchAll() {
return db.execute('SELECT * from books WHERE status = "free"');
}
}
Assuming db.execute returns a promise that resolves to an array, for which the first entry is the actual result, your code should look more like this:
exports.getAddNeueAusleihe = (req, res, next) => {
// storing users and books in this object
const view_data = {};
// fetch users for dropdown-box nr.1
User.fetchAll()
.then(([users_rows]) => {
view_data.users = users_rows;
// fetch books for dropdown-box nr. 2
return Book.fetchAll();
})
.then([book_rows] => {
view_data.books = books_rows;
})
.then(() => {
// send data to view
res.render('neue-ausleihe.ejs', {
users: view_data.users,
books: view_data.books,
pageTitle: 'Neue Ausleihe'
});
});
}
Bonus version:
exports.getAddNeueAusleihe = async(req, res, next) =>
// send data to view
res.render('neue-ausleihe.ejs', {
pageTitle: 'Neue Ausleihe',
users: (await User.fetchAll())[0]
books: (await Book.fetchAll())[0]
});
}
Even as the person writing this, it still blows my mind how much more readable async/await-based code is vs promise chains.
I need to save data and file as a new project to my Mongo. For this I am using formidable.
My POST method looks like this:
exports.create = async (req, res) => {
let form = new formidable.IncomingForm();
form.keepExtensions = true;
form.parse(req, (err, fields, files) => {
if (err) {
return res
.status(400)
.json({ errors: [{ msg: 'Image could not be uploaded' }] });
}
const {
title,
description,
photo,
tags,
git,
demo,
projectType,
} = fields;
//get links object
const projectFields = {};
projectFields.creator = req.user._id;
if (title) projectFields.title = title;
if (title) projectFields.description = description;
if (photo) projectFields.photo = photo;
if (projectType) projectFields.projectType = projectType;
if (tags) {
projectFields.tags = tags.split(',').map((tag) => tag.trim());
}
//get links object
projectFields.links = {};
if (git) projectFields.links.git = git;
if (demo) projectFields.links.demo = demo;
//1kb = 1000
//1mb = 1000000kb
//name 'photo' mus match client side. use photo
if (files.photo) {
if (files.photo.size > 1000000) {
return res.status(400).json({
errors: [{ msg: 'Image could not be uploaded. File to big.' }],
});
}
//this relates to data in schema product
project.photo.data = fs.readFileSync(files.photo.path);
project.photo.contentType = files.photo.type;
}
});
I want to use async/await so I am using try{}catch(err){} for my project.save(). I am initializing all my fields where I have also nested links. Unfortunately this is not working as I thought it will work. Right now my POST is returning 500. I am sitting on this and right now I am at the point that this can get a bit messy and not even close to any solution.
I started working on this node.js app using express to send sensor data via http request to my database (from an arduino to be specific). The main purpose of the app is to get the sensors' values from the url and create a new document in my cloud firestore.
var admin = require('firebase-admin');
const functions = require('firebase-functions');
const cors = require('cors')({origin: true});
const express = require('express');
var serviceAccount = require("./minicapcollar-firebase-adminsdk-ovdpm-cda3767493.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://minicapcollar.firebaseio.com"
});
let db = admin.firestore();
const app = express();
app.use(cors);
//Declare the field view for the timestamp
let FieldValue = require('firebase-admin').firestore.FieldValue;
app.get('/send/sensorData', (req, res) => {
var id = 'HpwWiJSGHNbOgJtYi2jM'; //variable to store the id of the collar, TODO: req.query.id
var lat = req.query.lat/1000000;
var lon = req.query.lon/1000000;
var hr = req.query.hr;
var et = req.query.et;
var it = req.query.it;
//Rest of the values
//Declare the index of the collar id
let indexRef = db.collection('dogs').doc(id);
//Declare the index of the position
let posRef = indexRef.collection('position').doc();
//Declare the index of the heartrate
let hrRef = indexRef.collection('heartrate').doc();
//Declare the index of the external temperature
let etRef = indexRef.collection('external_temperature').doc();
//Declare the index of the internal temperature
let itRef = indexRef.collection('temperature').doc();
//Save the current time
var time = FieldValue.serverTimestamp();
//Set position
let setPos = posRef.set({
timestamp: time,
value: new admin.firestore.GeoPoint(lat,lon)
}).then(function() {
console.log("Position data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
//Set heartrate
let setHr = hrRef.set({
timestamp: time,
value: hr
}).then(function() {
console.log("Heartrate data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
//Set external temperature
let setET = etRef.set({
timestamp: time,
value: et
}).then(function() {
console.log("External temperature data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
//Set internal temperature
let setIT = itRef.set({
timestamp: time,
value: it
}).then(function() {
console.log("Data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
res.send(`All sensor data sent`);
});
app.get('/send/pos', (req, res) => {
var id = 'HpwWiJSGHNbOgJtYi2jM'; //variable to store the id of the collar, TODO: req.query.id
var lat = req.query.lat/1000000;
var lon = req.query.lon/1000000;
//Rest of the values
//Declare the index of the collar id
let indexRef = db.collection('dogs').doc(id);
//Declare the index of the position
let posRef = indexRef.collection('position').doc();
//Save the current time
var time = FieldValue.serverTimestamp();
//Set position
let setPos = posRef.set({
timestamp: time,
value: new admin.firestore.GeoPoint(lat,lon)
}).then(function() {
console.log("Position data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
res.send(`Position sent`);
});
app.get('/send/hr', (req, res) => {
var id = 'HpwWiJSGHNbOgJtYi2jM'; //variable to store the id of the collar, TODO: req.query.id
var hr = req.query.hr;
//Declare the index of the collar id
let indexRef = db.collection('dogs').doc(id);
//Declare the index of the heartrate
let hrRef = indexRef.collection('heartrate').doc();
//Save the current time
var time = FieldValue.serverTimestamp();
//Set heartrate
let setHr = hrRef.set({
timestamp: time,
value: hr
}).then(function() {
console.log("Heartrate data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
res.send(setHr & `Heartrate value sent`);
});
app.get('/send/temp', (req, res) => {
var id = 'HpwWiJSGHNbOgJtYi2jM'; //variable to store the id of the collar, TODO: req.query.id
var et = req.query.et;
var it = req.query.it;
//Declare the index of the collar id
let indexRef = db.collection('dogs').doc(id);
//Declare the index of the external temperature
let etRef = indexRef.collection('external_temperature').doc();
//Declare the index of the internal temperature
let itRef = indexRef.collection('temperature').doc();
//Save the current time
var time = FieldValue.serverTimestamp();
//Set external temperature
let setET = etRef.set({
timestamp: time,
value: et
}).then(function() {
console.log("External temperature data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
//Set internal temperature
let setIT = itRef.set({
timestamp: time,
value: it
}).then(function() {
console.log("Data saved to Firestore");
return null;
}).catch(function(error) {
console.log("Got an error: ", error);
});
res.send(`Temperature values sent`);
});
exports.app = functions.https.onRequest(app);
Once I finished the app, I tested it on my localhost, and it worked perfectly. I was able to write new data to the cloud firestore without any problems. Once I deployed my app, when sending the request, I get a 403 (Error: Forbidden Your client does not have permission to get URL /app/send/pos/?lat=11111111&lon=22222222 from this server.)
On the firestore rules, I specified that anyone can read and write to see if that was the problem, but it persists.
I also tried to follow along this tutorial: https://www.youtube.com/watch?v=LOeioOKUKI8, but I had the same problem when trying to access "http://baseURL.firebaseapp.com/timestamp", I got a 403 error when deployed (on the localhost it worked perfectly too)
NOTE: This is my first experience with node.js, please forgive any bad practices. I am an electrical eng student, so programming is not my main strength. THANKS IN ADVANCE FOR YOUR HELP!
Thank you samthecodingman! The answer you provided did the trick! The problem was in the IAM configuration of my function.
For those of you who don't want to jump yet to another link, I quote the answer provided by Mike Karp in the link above.
It seems to me that additional IAM functionality was added to Google Cloud Functions, and >as a result, you may have not turned on allUser access to the function (FYI this give >acess to the whole web).
On the Cloud Functions homepage, highlight the Cloud Function you want to add all >>access to.
Click "Show Info Panel" on the top right.
Click "Add Members" and type "allUsers" then select "Cloud Function Invokers" under >>"Cloud Function" in the Role box.
Click "Save"
Deploying a function failed (for some strange reason, which happens time to time), and I guess it didn't update permissions.
Re-deploying succeeded but didn't update its permissions.
I had to delete the function and redeploy to fix this issue.
I am new to node js. I am trying to develop API for getting items list by its category list. For that i have create a function to fetch all available active featured tags from table ( featured_tags ). After that featured tag list fetch i want to get items list from ( items ) table which belongs to that particular tag. Can anyone help me how to so that in node js. I am using mysql database.
i have done below things to fetch categories from table.
route.js file
this.app.get('/list_menu',async(request,response) =>{
var itemlist = '';
const featuretags = helper.getFeatureTag().then(function(featuredtags){
//console.log('test');
itemlist = helper.getitems(featuredtags);
});
response.status(200).json({
status:200,
message: "success",
data:itemlist
});
});
function to get active featured tags in helper.js file
async getFeatureTag(){
return this.db.query("select * from featured_tags where status = 1 order by id desc ");
//const featuredtags = await this.db.query("select * from featured_tags where status = 1 order by id desc ");
}
Function which get items list of featured tags in helper.js file
async getitems(featuredtags){
var itemdata = [];
var featured_tagdataset = [];
if(featuredtags.length > 0){
for (var i = 0; i < featuredtags.length; i++) {
var row = featuredtags[i];
var featurtag = {};
featurtag.id = row.id;
featurtag.featured_tag = row.featured_tag;
var itemresult = await this.db.query("SELECT * FROM `items` WHERE status = 1 and FIND_IN_SET('"+ row.id +"' ,featured_tags) > 0");
if(itemresult.length > 0){
for(var l=0; l < itemresult.length; l++){
var itemrow = itemresult[l];
var item = {};
item.id = itemrow.id;
item.category_id = row.id;
item.name = itemrow.item_name;
itemdata.push(JSON.stringify(item));
}
}
featurtag.tag_items = itemdata;
featured_tagdataset.push(featurtag);
}
//console.log(featured_tagdataset);
return featured_tagdataset;
}else{
return null;
}
}
when i console featuredtag_dataset array in itemlist() in helper.js file it show me perfect response which i have to pass in API response. But in route.js it shows me blank in data parameter.
Can anyone help me for how to develop this type of APIs in node js.
This is because helper.getitems(featuredtags) method is called successfully but send response doesn't wait until method returns a response as node js is asynchronous .
you need to write the code in such a way that it should work in series. I have created sample example you can try this.
this.app.get('/list_menu',async(request,response) =>{
helper.getFeatureTag().then(function(featuredtags){
helper.getitems(featuredtags).then(function(itemlist){
response.status(200).json({
status:200,
message: "success",
data:itemlist
});
})
}
});
You forget to use await in your roter.js on calling asynchronous function, just update your router to this
this.app.get('/list_menu',async(request,response) =>{
const featuredtags = await helper.getFeatureTag(),
itemlist = await helper.getitems(featuredtags);
response.status(200).json({
status:200,
message: "success",
data:itemlist
});
});
you can either nested callback function or async await function or chained promises using then.