Mongoose, geospatial query for users - node.js

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.

Related

node js , mongodb : check if an object already exist

I'am newbie to nodejs and mongodb, so how can I check if an object already exist in the collections , Note that my field type in the schema is object or JSON
const BillSchema = mongoose.Schema(
{
content: {
type: Object //or JSON
},
}
);
const Bill = module.exports = mongoose.model('Bill', BillSchema);
module.exports.addBill = function (newBill, callback) {
//Check for all bill titles and content, if newBill doesn't exist then add else do nothing
Bill.count({ content: newBill.content }, function (err, count) {
//count == 0 always ???
if (err) {
return callback(err, null);
} else {
if (count > 0) {
//The bill already exists in db
console.log('Bill already added');
return callback(null, null);
} else { //The bill doesnt appear in the db
newBill.save(callback);
console.log('Bill added');
}
}
});
}
One Of Nice Question You asked, I was suppose to achieve the same task before, I make the use of mongoose-unique-validator third party npm Package, & plugin to our schema
https://www.npmjs.com/package/mongoose-unique-validator
npm install mongoose-unique-validator
var uniqueValidator = require('mongoose-unique-validator');
const BillSchema = mongoose.Schema(
{
content: {type:Object , unique:true },
}
);
BillSchema.plugin(uniqueValidator, {message: 'is already taken.'});
Usage:
module.exports.addBill = function (newBill, callback) {
newBill.save(callback);
}
I Hope If this work for you too.

TypeError: Cannot read property 'push' of undefined Mongoose

I am getting the below error which doing a app in Node.js using express.
I am using Mongoose for my DB operations below i have detailed my design
Party.js
var mongoose = require("mongoose");
var partySchema = new mongoose.Schema({
partyName: String,
songs: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Song"
}
]
});
module.exports = mongoose.model("Party", partySchema);
Song.js
var mongoose = require("mongoose");
var songsSchema = new mongoose.Schema({
videoId: String,
videoName: String
});
module.exports = mongoose.model("Song", songsSchema);
My App.js
app.post("/search/addSong", function(req, res) {
Party.find({partyName:"hello"},function(err,party){
if(err){
console.log("Failed in find");
} else {
console.log(party);
// console.log(typeof(req.body.videoId));
var videoId = req.body.videoId;
var newSong = [ {
videoId:req.body.videoId,
videoName:req.body.videoName
}
];
Song.create(newSong, function(err, createdSong){
if(err){
console.log("Error creating a new party");
} else {
console.log(createdSong);
party.songs.push(createdSong);// ERROR ON THIS LINE
party.save();
res.redirect("/search");
}
});
}
});
res.render("addSong");
});
I am able to create collection objects of the Party and Song individually, when I add the song to the party queue, I get the following error:
TypeError: Cannot read property 'push' of undefined
Can anyone please let me know what I a missing in here..!!
Thanks in advance.!!
find returns a cursor which is converted in an array. if you are expecting a single doc, call findOne.
app.post("/search/addSong", function(req, res) {
Party.findOne({partyName:"hello"},function(err,party){
if(err){
console.log("Failed in find");
} else {
console.log(party);
// console.log(typeof(req.body.videoId));
var videoId = req.body.videoId;
var newSong = [ {
videoId:req.body.videoId,
videoName:req.body.videoName
}
];
Song.create(newSong, function(err, createdSong){
if(err){
console.log("Error creating a new party");
} else {
console.log(createdSong);
party.songs.push(createdSong);// ERROR ON THIS LINE
party.save(function(err){
res.redirect("/search");
});
}
});
}
});
res.render("addSong");
});
Also put redirect in the callback of save, it will make sure that redirect is called only when save is completed, otherwise it would redirect without saving, giving the asynchronous nature of javascript.

How to perform Update and Delete operations in a bucket using Couchbase and Nodejs sdk

I am moving from mongodb to Couchbase using Node.js. I want to perform the CRUD operations. Insert(create) and Get are working fine, but when I want to perform Update and Delete getting some error messages (Here update purpose using 'upsert','replace' are used) like:
TypeError: Cannot read property 'replace' of undefined
Here is code:
db.js
// Instantiate Couchbase and Ottoman
var couchbase=require('couchbase');
var ottoman=require('ottoman');
// Build my cluster object and open a new cluster
var myCluster = new couchbase.Cluster('localhost:8091');
var myBucket = myCluster.openBucket('default');
ottoman.bucket=myBucket;
require('./model/user');
ottoman.ensureIndices(function(){});
user.js
var db = require('./../db.js').myBucket;
var ottoman = require('ottoman');
var userMdl = ottoman.model('User', {
firstName: {type:'string'},
lastName: {type:'string'},
created: {type: 'Date', default:function(){return new Date()}},
email:'string',
phone: 'string'
},{
index: {
findByID: {
by: '_id'
},
}
})
module.exports = userMdl;
routes.js
var bodyParser = require('body-parser');
var db = require('../schema/db').myBucket;
var user=require('../schema/model/user');
var jsonParser = bodyParser.json();
var urlencodedParser = bodyParser.urlencoded({ extended: false });
module.exports = function (app) {
// Delete a record
app.post("/api/delete/:_id", function(req, res) {
console.log("_id:"+req.params._id)
if(!req.params._id) {
return res.status(400).send({"status": "error", "message": "A document id is required"});
}
db.delete({_id:req.params._id}, function(error, result) {
if(error) {
return res.status(400).send(error);
}
res.send(result);
});
});
app.post('/api/user/update/:id',function(req,res){
db.replace(req.params.id,{firstName:"Mahesh"},function(err,result){
if (err) {
res.status = 400;
res.send(err);
return;
}
else {
res.status = 202;
res.send(result);
}
})
})
}
I am stuck here from last two days.
You missed one argument although it can be optional.
From Couchbase Node.js SDK document, it have 4 arguments, but you have only 3.
db.replace(req.params.id,{firstName:"Mahesh"},function(err,result){
=>
db.replace(req.params.id,{firstName:"Mahesh"}, {}, function(err,result){
With 3rd argument of empty map may work properly, but notice that Couchbase uses optimistic locking, so you require "CAS" value for original document when you modify the original to get data integrity.
the line in db.js var ottoman = require('ottoman');it's a constructor itself. Then you have two instances, and the error comes in user.js when you try to define a model, because node-ottoman needs a reference to the bucket.
You should assign the bucket in the user.js or reuse the ottoman object that you left in the db.js
model.js
// Instantiate Couchbase and Ottoman
var couchbase = require('couchbase');
var ottoman = require('ottoman');
// Build my cluster object and open a new cluster
var myCluster = new couchbase.Cluster('localhost:8091');
var myBucket = myCluster.openBucket('default');
ottoman.bucket = myBucket;
var userMdl = ottoman.model('User', {
firstName: {type:'string'},
lastName: {type:'string'},
created: {type: 'Date', default:function(){return new Date()}},
email:'string',
phone: 'string'
},{
index: {
findByID: {
by: '_id'
},
}
}) ;
// this line needs to be after you define the model
ottoman.ensureIndices(function(){});
module.exports = userMdl;
model.exports = mybucket;
You can update Couchbase document using 2 ways 1st by upsert method and second by N1qlQuery
bucket.upsert('user', {'name': 'Jay'}, {'expiry': 1}, function(err){
bucket.get('user', function(err, result) {
console.log('Have item: %j', result.value);
})
});
let query = N1qlQuery.fromString("UPDATE `"+BUCKETNAME+"` SET name='"+data.name+"' where _id ='"+id+"'");
bucket.query(query,(error,result)=>{
if(error){
console.log(error);
}
console.log(result);
});
You can delete Couchbase document using 2 ways 1st is removed method and second by N1qlQuery
bucket.remove(id, function(error, result) {
console.log("Deleted");
});
let query = N1qlQuery.fromString("DELETE FROM `"+BUCKETNAME+"` WHERE _id = '"+id+"'");
bucket.query(query,(error,result)=>{
if(error){
console.log(error);
}
console.log(result);
})

Mongoose saving for populate

I'm new to Mongoose and Nodejs developement in general and I've got a bit of confusion around how to properly set up saving my records. Here are my two schemas:
Download
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var downloadSchema = Schema({
title : String,
description : String,
_project : { type: Schema.Types.ObjectId, ref: 'Project' }
});
module.exports = mongoose.model('Download', downloadSchema);
Project
...
var projectSchema = Schema({
name : String,
url : String,
pwd : String,
_downloads : [{type: Schema.Types.ObjectId, ref: 'Download' }]
});
module.exports = mongoose.model('Project', projectSchema);
This appears to be working correctly. The documentation explains my use-case of saving a download and linking a project, but I'm not sure how to properly populate the Project._downloads. Here's what I've done:
Express route handler:
function createDownload(req, res) {
// the Project Id is passed in the req.body as ._project
var dldata = req.body;
Project.findOne({ _id : dldata._project }, function(err, project) {
var dload = new Download(dldata);
dload.save( function (err, download) {
project._downloads.push(download._id);
project.save( function(err){
var msg = {};
if(err) {
msg.status = 'error';
msg.text = err;
}else {
msg.status = 'success';
msg.text = 'Download created successfully!';
}
res.json(msg);
});
});
});
}
This seems overcomplicated to me. Am I supposed to be manually pushing to the ._downloads array, or is that something Mongoose is supposed to handle internally based on the schema? Is there a better way to achieve it so that I can do:
Download.find().populate('_project').exec( ...
as well as:
Project.findOne({_id : _projectId}).populate('_downloads').exec( ...
According to the mongoose docs there are 2 ways to add subdocs to the parent object:
1) by using the push() method
2) by using the create() method
So I think that your code can be a bit simplified by eliminating the operation of saving a new Download item:
function createDownload(req, res) {
var dldata = req.body;
Project.findOne({ _id : dldata._project }, function(err, project) {
// handle error
project._downloads.push(dldata);
project.save(function(err) {
// handle the result
});
});
}
or
function createDownload(req, res) {
var dldata = req.body;
Project.findOne({ _id : dldata._project }, function(err, project) {
// handle error
project._downloads.create(dldata);
project.save(function(err) {
// handle the result
});
});
}

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.

Resources