Mongoose - persistent `virtual` field? - node.js

Ok, I have node as backend, it has the following Mongoose model:
var SomeSchema = new Schema({
name: {type: String, required: true},
iplanned: {type: String, default:60}
});
SomeSchema.virtual('planned')
.get(function () {
return parseInt(this.iplanned / 60, 10) + ' mins';
})
.set(function (val) {
this.iplanned = parseInt(val, 10) * 60;
});
someModel = mongoose.model('Some', SomeSchema);
So far it is good, from node.js side of things I can work with the records and access this planned field as I like.
The following is a simple responder to serve this list through http:
exports.list = function (req, res) {
someModel.find(function (err, deeds) {
return res.send(deeds);
});
});
And here's the problem - the virtual field planned is not included in each record (well, it is understandable, sort of). Is there a way to inject somehow my virtual field to each record? Or I have to do the virtual conversion on a front end as well? (Yeah, I know that there's Meteor.js out there, trying go without it here).

In your schema you should redefine toJSON() method:
SomeSchema.methods.toJSON = function () {
var obj = this.toObject();
obj.planned = this.planned;
return obj;
};

Related

how to get the existing collection in mongodb using mongose- Nodejs

In my mongodb i already had a collection and document
Now, i want to use this collection in my node-js using mongoose. how we do this.
const mongoose = require("mongoose");
const schema = mongoose.Schema;
const adminLogin = new schema({
name: { type: String, required: true },
password: { type: String, required: true }
})
module.exports = mongoose.model("adminDetails", adminLogin)
while doing this it is creating new collection. Unable to use the existing collection.
At first you need to make a GET method Router in your jsFile.
like this
app.get("/mainData", async (req, res) => {
const menuInfo = await Nutrients.find({});
res.json(menuInfo);
});
You can set and use VSCode extension "thunderClient"!
like this
enter image description here
setting your request method and URI endpoint
(when you user GET method to get some data in your case, you don't need to write something in request body)
Then, you can see your data on the 'response part' as an Object Data.
If you want to use your data on Front side on your Project, you can use like this!
(in my case, I used jQuery. )
function menu_show() {
$('#result_list').empty()
$.ajax({
type: 'GET',
url: '/mainData', //you need to write same url with your no3.
data: {},
success: function (response) {
console.log(response)
let rows = response['menus']
for (let i = 0; i < rows.length; i++) {
let menuName = rows[i]['menuName']
console.log(menuName)
}
}
}
This is my answer. Let me know if you've solved it!
the image 1 is the structure in the MongoDB. I want to read the data from that collection, below is the code using and the URL and output in post man.
route.get('/adminLogin', (request, response) => {
const data = adminDetails.find({}, (err, result) => {
if (err) {
console.log(err)
} else {
res.json(result)
}
})
})
http://localhost:5000/admin/adminLogin

Mongoose, geospatial query for users

I'm currently working with nodeJS, using express and mongoDB and mongoose for an ORM. When I create a User and save them to the database I would like to query their location and save it. This is what I am currently doing, I have a UserSchema and a location Schema.
My userSchema just has the location stored as a string and in the location Schema itself I have
var locationSchema = new mongoose.Schema({
name: String,
loc: {
type: [Number],
index: '2d'
}
});
mongoose.model('Location', LocationSchema);
And then in my controller, I have the following
import json from '../helpers/json;
import mongoose from 'mongoose';
var User = mongoose.model('User);
module.exports = function() {
var obj = {};
obj.create = function (req, res) {
var user = new User(req.body);
user.roles = ['authenticated']
user.location = getLocation(req);
user.save(function (err) {
if (err) {
return json.bad(err, res);
}
json.good({
record: user,
});
});
};
return obj;
function getLocation (req) {
var limit = req.query.limit || 10;
var maxDistance = req.query.distance || 1;
maxDistance /= 6371;
var coords = [];
coords[0] = req.query.longitude;
coords[1] = req.query.lattitude;
Location.findOne({
loc: {
$near: coords,
$maxDistance: maxDistance
}
}).limit(limit).exec(function (err, location) {
if (err) {
console.log(err);
}
return location.name;
});
}
};
I have also tried using location.find instead of findOne and returning locations[0].name.
The error is thrown says cast to the number failed for value undefined at loc.
Do I need to send the location data to the server from the client side? If so, is there a best method to implement this? I have heard of the HTML5 Geolocation API, but I have never utilized it.
Thank you!
!!! -- UPDATE --- !!
I have started using the Geolocation API on the client side to send this data to the server in the request. I am using angular on the client side like so
(function() {
'use strict';
angular.module('opinionated.authentication')
.controller('SignupController', SignupController);
/* #ngInject */
function SignupController ($state, appUsers, appToast) {
var vm = this;
vm.reset = reset;
vm.create = create;
vm.user = {
name: '',
username: '',
email: '',
password: ''
};
vm.location = {
lattitude: '',
longitude: ''
};
function create = (isValid) {
if (isValid) {
var user = new appUsers.single({
name: vm.user.name,
username: vm.user.username,
email: vm.user.email,
password: vm.user.password,
lattitude: vm.location.lattitude,
longitutde: vm.location.longitude
});
user.$save(function (response) {
if (response.success) {
appToast('Welcome to Opinionated, ' + response.res.record.name);
$state.go('authentication.wizard')
} else {
appToast(response.res.messsage);
}
});
} else {
appToast('Hmm... Something seems to be missing');
}
}
function getPosition() {
navigator.geolocation.getPosition(updatePosition);
}
function updatePosition (position) {
vm.location.lattitude = position.coords.lattitude;
vm.location.longitutde = position.coords.longitude;
}
getPosition();
....
I think it has something to do with how I am getting the coordinates now. My browser prompts me for permission to use my location, so I am at least requesting the data. However, I changed my User Schema to save the lat and long and neither of these values are being saved upon success.
I found my error. I did need to include the Geolocation API to get the users coordinates. I then just saved the coordinates to the database and am using mongo's geo service from there! Everything works fine now.

mongo insert in nodejs for loop only inserting one from array

im building an application to collect votes for a live event.
the api doesnt give us option to select users from a time frame so im polling the endpoint every second.
i currently have 13 entries that return from the endpoint, i parse them into and array and for loop around them setting my mongoose schema with the attributes and trying to save them, but when i do
db.votes.count() my result is always 1
my node module looks like
var express = require('express');
var unirest = require('unirest');
var voteSchema = require(GLOBAL.rootdir + '/modules/voting/models/votes');
var seconds = 0;
var interval = 1000;
express({
votePoller : setInterval(function () {
seconds++;
if (typeof GLOBAL.accessToken != 'undefined') {
var Request = unirest.get('https://api.domain.io/api/v1/guests');
Request
.header('Accept', 'application/json')
.header('Content-Type', 'application/json; charset=utf-8')
.header('Authorization', 'Bearer ' + GLOBAL.accessToken)
.end(function (response) {
if(response.code === 200){
var votesModel = new voteSchema;
var payloadArray = JSON.parse(response.raw_body);
for(var i in payloadArray.guests){
console.log(i);
console.log(payloadArray.guests[i]);
votesModel.ctid = payloadArray.guests[i].id;
votesModel.email = payloadArray.guests[i].username;
votesModel.voteStatus = 0;
votesModel.createdAt = new Date(1000 * payloadArray.guests[i].created_at);
votesModel.save(function(err) {
if (err) {
console.log(err);
console.log({ message: err });
} else {
console.log({ message: 'vote saved' });
}
});
console.log('Done');
}
}
});
}
console.log(seconds);
}, interval)
});
var votePoller = express;
module.exports = votePoller;
my mongoose model is
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var votesSchema = new Schema({
ctid: { type: String, required: true, unique: true },
fullName: { type: String},
email: { type: String, required: true, unique: true },
mobileNumber: { type: String },
vote: { type: Number},
voteStatus: Boolean,
createdAt: Date
});
var Votes = mongoose.model('Votes', votesSchema);
module.exports = Votes;
the console log counts out each i in the array so why the save function isn't being fired is stumping me
Thanks in advance
You need to use an async function to do an async for loop, there are many answer on here for that code. I would suggest a control flow library like async or if using a new version of node, use native promises instead. Promises all method is the best way to achieve this.

locomotivejs and mongoose: passing promise variables to a controller

preamble:
I'm not sure if this is the best way to ask this question as I'm fairly sure it's more general than I'm making it and there's probably a global pattern that address my concern.
For the moment here's the question in the context of the original code that I'm working with.
Given a locomotivejs controller, let's call it Contact_Controller, with general structure like this:
'\controllers\contact_controller.js
var locomotive = require('locomotive')
, Controller = locomotive.Controller
, Contact = require('../models/contact')
, sender = require('../helpers/sender')
;
var Contact_Controller = new Controller();
and then:
Contact_Controller.list = function() {
//Provides a list of all current (successful) contact attempts
var controller = this;
Contact.find({status: 1}, function(err, results) {
if(err) {
controller.redirect(controller.urlFor({controller: "dashboard", action: "error"}));
}
controller.contacts = results;
controller.render();
});
};
and the model:
'/models/contact.js
var mongoose = require('mongoose')
, mongooseTypes = require('mongoose-types')
, pass = require('pwd')
, crypto = require('crypto')
, Schema = mongoose.Schema
, Email = mongoose.SchemaTypes.Email;
var ContactSchema = new Schema({
email: {type: Email, required: true},
subject: {type: String, required: true },
message: { type: String, required: true},
status: {type: Number, required: true, default: 1},
contact_time: {type: Date, default: Date.now}
});
module.exports = mongoose.model('contact', ContactSchema);
Inside the list action of the contact_controller I'd really rather not use controller = this; I generally prefer using redirect = this.redirect.bind(this); style localized binding to handle these sort of situations.
However, I can't think of a way to return the results to the controller's this object without creating a global variable version of this and having the promise's callback talk to that. Is there a better way to return the results variable or expose the contact_controller object in this context?
Do you mean this?
Contact.find({status: 1}, function(err, results) {
if (err) {
this.redirect(this.urlFor({this: "dashboard", action: "error"}));
return; // you should return here, otherwise `render` will still be called!
}
this.contacts = results;
this.render();
}.bind(this));
^^^^^^^^^^^ here you bind the controller object to the callback function

How to Make Mongoose's .populate() work with an embedded schema/subdocument?

I read up that you can make Mongoose auto pouplate ObjectId fields. However I am having trouble structuring a query to populate fields in a subdoc.
My models:
var QuestionSchema = new Schema({
question_text: String,
type: String,
comment_field: Boolean,
core_question: Boolean,
identifier: String
});
var SurveyQuestionSchema = new Schema({
question_number: Number,
question: {type: Schema.Types.ObjectId, ref: 'Question', required: true} //want this popuplated
});
var SurveySchema = new Schema({
start_date: Date,
end_date: Date,
title: String,
survey_questions: [SurveyQuestionSchema]
});
Right now I achieve the effect by doing:
Survey.findById(req.params.id, function(err, data){
if(err || !data) { return handleError(err, res, data); }
var len = data.survey_questions.length;
var counter = 0;
var data = data.toJSON();
_.each(data.survey_questions, function(sq){
Question.findById(sq.question, function(err, q){
sq.question = q;
if(++counter == len) {
res.send(data);
}
});
});
});
Which obviously is a very error-prone way of doing it...
As I noted in the comments above, this is an issue currently under scrutiny by the mongoose team (not yet implemented).
Also, looking at your problem from an outsider's perpsective, my first thought would be to change the schema to eliminate SurveyQuestion, as it has a very relational db "join" model feel. Mongoose embedded collections have a static sort order, eliminating the need for keeping a positional field, and if you could handle question options on the Survey itself, it would reduce the schema complexity so you wouldn't need to do the double-populate.
That being said, you could probably reduce the queries down to 2, by querying for all the questions at once, something like:
Survey.findById(req.params.id, function(err, data){
if(err || !data) { return handleError(err, res, data); }
var data = data.toJSON();
var ids = _.pluck(data.survey_questions, 'question');
Question.find({_id: { $in: ids } }, function(err, questions) {
_.each(data.survey_questions, function(sq) {
sq.question = _.find(questions, function(q) {
var id = q._id.toString();
return id == sq.question;
});
});
res.send(data);
});
});

Resources