How to search in sub doc - node.js

I have an angular-fullstack app generated from angular-fullstack yeoman generator and I have a Query Model as follows:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Discussion Question Schema
*/
var QuerySchema = new Schema({
date: {
type: Date,
default: Date
},
tags: [{
type: Schema.ObjectId,
ref: 'Tag'
}]
});
module.exports = mongoose.model('Query', QuerySchema);
var deepPopulate = require('mongoose-deep-populate')(mongoose);
and Tag has a field text. Now in my query controller I have to deep populate some other fields and paginate them so, I am trying something like this in the controller function:
exports.index = function(req, res) {
var escapeRegExpChars = function (text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};
require('paginate-for-mongoose');
var limit,page;
if(req.query.limit != undefined) limit = req.query.limit;
else limit = 10;
if(req.query.page != undefined) page = req.query.page;
else page = 1;
var queryObj = {};
if(req.query.searchText != undefined && req.query.searchText != '')
queryObj['title']= new RegExp(escapeRegExpChars(req.query.searchText), 'i');
var options = {
perPage:limit,
delta:2,
page:page
};
if(req.query.fold !=undefined && req.query.fold != '') queryObj["tags.text"] = req.query.fold;
var query = Query.find(queryObj).populate('tags','text').deepPopulate('user class user.class');
query.paginate(options,function(err, resp){
if(err) { return handleError(res, err); }
if(req.query.fold) console.log(resp.results);
return res.status(200).json(resp.results);
});
};
How do I search queries with tags.text value exactly as the req.query.fold value?

MongoDB doesn't support joins so to search on a linked doc you have to do it in two steps:
// First look up the _id of the tag
Tags.findOne({text: req.query.fold}, function(err, tag) {
if (tag) {
// Add a match in the doc's tags array to the tag's _id
queryObj.tags = tag._id;
var query = Query.find(queryObj)...
...
}
});

Related

how not to show items with specific condition in mongoose

My code is as shown below:
foodtruck.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Items = require('./items.js');
var FoodTruckSchema = new Schema({
foodtruck_name:String,
foodtruck_location:String,
foodtruck_rating:{type:Number,default:5},
foodtruck_total_votes:{type:Number,default:0},
foodtruck_tag:String,
foodtruck_open_status:{type:Number,default:1}, //0 open 1 closed
foodtruck_starting_timing:String,
foodtruck_closing_timing:String,
foodtruck_cusine:[String],
foodtruck_img:String,
foodtruck_logo:String,
item_list: [ {type : mongoose.Schema.ObjectId, ref : 'items'}]
},{ versionKey: false });
module.exports = mongoose.model('foodtruck',FoodTruckSchema);
items.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ItemSchema = new Schema({
no_of_times_ordered:Number,
item_name:String,
item_tag:String,
item_category:String,
item_description:String,
item_illustrations:[String],
item_stock:Number, //0 available 1 last 5 items 2 not available
item_quantity_ordered:{type:Number,default:0},
item_discount_price:Number,
item_price:Number,
item_img:String,
no_of_likes:{type:Number,default:0}
},{ versionKey: false });
module.exports = mongoose.model('items',ItemSchema);
My query is as show below:
var foodtrucklist = function(req, res) {
foodtr.find().populate('item_list').exec(function(err, foodtrucks) {
foodtrucks.forEach(function(ftr) {
var start_time = ftr.foodtruck_starting_timing;
var end_time = ftr.foodtruck_closing_timing;
var foodtruck_open_status = ftr.foodtruck_open_status;
// var shut_down = ftr.foodtruck_shutdown;
if ((start_time && end_time) &&
(start_time.trim() != '' &&
end_time.trim() != '')) {
if (inTime(start_time, end_time) &&
foodtruck_open_status ==0 ) {
ftr.foodtruck_open_status = 0;
ftr.save();
} else {
ftr.foodtruck_open_status = 1;
ftr.save();
}
}
})
res.json({
status: '200',
message: 'foodtrucklist',
data: foodtrucks
});
});
};
now what I want to achieve is, I don't want to show items which have **item_stock = -1 **. How can I achieve in populate query?
you can use populate options inside your populate (match for query condition).
Try this:
foodtr
.find()
.populate({
path :'item_list',
match : {
item_stock : { $ne : -1}
}).
exec(function(err, foodtrucks) {
...
});
For more information on populate options, read Mongoose populate query condtions and options documentation, look for query condition and options

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.

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
});
});
}

Mongoose Schema default value for ObjectId

I have a collection of warehouse upgrades. It is predefined "template" collection containing for example max_capacity, level and price. Then I have warehouse_levels collection, this contains different indexes to warehouse_upgrades for different stored resources. But I can't create warehouse_level model, because I need to load _ids of warehouse_upgrades
WarehouseUpgrade = mongoose.model("warehouse_upgrade");
// find wheat upgrade containing level 0
WarehouseUpgrade.find({ type: "wheat", level: 0 }).exec(function (err, wheat) {
var warehouseLevelSchema = Schema({
wheat: {
type: Schema.Types.ObjectId,
ref: "warehouse_upgrade",
default: wheat._id
},
... more resources
};
var WarehouseLevel = mongoose.model("warehouse_level", warehouseLevelSchema);
}
When I want to call var WarehouseLevel = mongoose.model("warehouse_level"); interpreting this code throws error:
MissingSchemaError: Schema hasn't been registered for model "warehouse_level"
If I extract out schema definition from WarehouseUpgrade.find, then code works, but I can't set up default values for resource warehouses.
How can I set default value for ObjectId from different collection when I don't want to hardcode this values?
EDIT:
I load all schema definitions in file named mongoose.js:
var mongoose = require("mongoose"),
Animal = require("../models/Animal");
Warehouse_upgrade = require("../models/Warehouse_upgrade"),
Warehouse_level = require("../models/Warehouse_level"),
User = require("../models/User"),
...
module.exports = function(config) {
mongoose.connect(config.db);
var db = mongoose.connection;
// And now I call methods for creating my "templates"
Warehouse_upgrade.createUpgrades();
Animal.createAnimals();
User.createDefaultUser();
}
MissingSchemaError occurs in model/User(username, hashed_password, email, warehouse_level,...) - every user has reference to his own document in warehouse_level.
// User
var mongoose = require("mongoose"),
Schema = mongoose.Schema,
Warehouse_level = mongoose.model("warehouse_level");
// There are no users in DB, we need create default ones
// But first, we need to create collection document for warehouse_level
// and warehouse (not shown in this code snippet)
Warehouse_level.create({}, function (err, warehouseLevel) {
if (err) { console.error(err); return; }
// warehouse_level document is created, let's create user
User.create({ username: ..., warehouse_level: warehouseLevel._id });
});
One possible way to achieve this is to create a method like "setDefaultIndexes"
var warehouseLevelSchema = mongoose.Schema({..});
warehouseLevelSchema.methods = {
setDefaultUpgrades: function() {
var self = this;
WarehouseUpgrade.find({ level: 0 }).exec(function (err, collection) {
for (var i = 0; i < collection.length; i++) {
var upgrade = collection[i];
self[upgrade.type] = upgrade._id;
}
self.save();
});
}
};
var Warehouse_level = mongoose.model("warehouse_level", warehouseLevelSchema);
And call it after creation of new warehouse_level:
WarehouseLevel.create({}, function (err, warehouseLevel) {
if (err) { console.error(err); return; }
warehouseLevel.setDefaultUpgrades();
});

NodeJS / Express 4 - Sqlite3 - Storing rowset into variable

I'm trying to store the rowset from the query below into a variable so I can play it into the view and loop through it.
I'm getting results from the DB, and can console.log them in the db.each block, and I thought I could generate my JSON object below and then store it - but it's not setting it for some reason and var data = "" just returns an empty string.
I am a bit baffled as to why - does anyone know where I am going wrong please?
Thank you for taking the time to read.
var express = require('express');
var router = express.Router();
var db = require('../lib/db.js');
/* GET contacts listing. */
router.get('/', function(req, res) {
var data = "";
db.serialize(function() {
var rowset = db.each("SELECT b.forename, b.surname FROM contacts a, contact_attributes b WHERE a.contact_id = b.contact_id", function(err, row) {
data = ' { "'+row.contact_id+'" : [ { "forename" : "'+row.forename+'", "surname" : "'+row.surname+'" } ] } ';
});
});
res.render('contacts', {
title: "Contacts",
active: "contacts",
contacts: JSON.stringify(data)
});
});
module.exports = router;
The database query runs asynchronously, executing the callback function once the query returns. Therefore, res.render is called after data gets set to empty string, but before it gets set to the result set.
Also, there is no need to JSON.stringify a string that you have already built as JSON.
The code executes as follows:
var data = "";
db.serialize
var rowset = db.each
res.render
DB query returns.
db.each callback function executes, which sets data based on the result set.
Try this:
db.serialize(function() {
var rowset = db.each("SELECT forename, surname FROM contacts a, contact_attributes b WHERE a.contact_id = b.contact_id", function(err, row) {
var data = ' { "'+row.contact_id+'" : [ { "forename" : "'+row.forename+'", "surname" : "'+row.surname+'" } ] } ';
res.render('contacts', {
title: "Contacts",
active: "contacts",
contacts: data
});
});
});
});
or, avoid the manual JSON stringification:
db.serialize(function() {
var rowset = db.each("SELECT forename, surname FROM contacts a, contact_attributes b WHERE a.contact_id = b.contact_id", function(err, row) {
var data = {};
data[row.contact_id] = [
{
forename: row.forname,
surname: row.surname
}
];
res.render('contacts', {
title: "Contacts",
active: "contacts",
contacts: data
});
});
});
});

Resources