How to location data in node express mongo - node.js

I have the following schema to store coordinates:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const LocationSchema = new Schema({
// Matches
loc: {
type:
{
type: String
},
coordinates: [Number]
},
member_id: {
type: String
}
});
LocationSchema.index({ loc: '2dsphere' });
mongoose.model('locations', LocationSchema);
I have also created a get route to get to the page where I want to show this stored coordinates data. When I hit the route I get the following in the console.log which I am using to debug:
{ loc: { coordinates: [ 74.360106, 31.497754 ], type: 'Point' },
_id: 5b5abd4bdcbd0f4e5c70b799,
member_id: 'HyQUa4_N7',
__v: 0 }
This looks fine to me since I am getting back the coordinates and the member ID. Following is the code for my get route using which I am passing the data to my express handlebars view:
// Manage Location
router.get('/userlocation/:member_id', ensureAuthenticated, (req, res) => {
Location.findOne({ member_id: req.params.member_id })
.then(locations => {
console.log(locations);
res.render('users/userlocation', { locations: locations });
})
.catch(error => {
console.log('could not find user location');
});
});
I am confused about how do I show this data in my HTML/View. Normally I use {{variable name}} to show data which is being passed into the view. what format should I use. I tried using {{locations.coordinates[0]}} but that gives me an error.
Please help. This is the first time I am attempting something like this in Node.

You can access the longitude like this:
{{locations.loc.coordinates[0]}}
I would suggest you should define schema like this:
location: {type: [Number], index: '2d'}, // [, ]

Related

Node.js: Cast to ObjectId failed for value "new" (type string)

I am rather new to node and am having an issue that is a bit difficult for me to decipher. I am trying to make a reddit clone. The part that I am working on right now is to allow users to view posts other users have posted. I was trying to implement a new route by pasting in this bit of code
app.get('/', (req, res) => {
Post.find({}).lean()
.then((posts) => res.render('posts-index', { posts }))
.catch((err) => {
console.log(err.message);
})
})
However, I am getting this error when I run it:
Cast to ObjectId failed for value "new" (type string) at path "_id" for model "Post"
It seems like something is wrong with my post.js model, but I can't identify it. Could someone review this and help me identify the triggering factor?
const { Schema, model } = require('mongoose');
const postSchema = new Schema({
title: { type: String, required: true },
url: { type: String, required: true },
summary: { type: String, required: true },
}, { timestamps: true });
module.exports = model('Post', postSchema);

MongoDB/Mongoose how to manage incrementation of some Number fields

I'm currently working on an API and I need the users to have at least 4 fields of number which will increase over time. Some of them might be 15000/hour, some of them 500/hour, some will be negative like -8000/hour. How should I handle such data storage using Mongoose? What kind of properties should these user model fields have? If you can provide me a simple Mongoose model for such data storage, I would be happy.
Should I have something in back-end just to increase (or decrease) these fields of users? Or does MongoDB/Mongoose have something to provide this feture? Also, how should I show these fields increasing on the web page? Should I always get the increased fields of user every few seconds? Or should I just use JavaScript on front-end?
Thanks in advance.
It would depending on what you would want to achieve with your data, at the end of the day.
To keep track of all changing logs, persisted in its respective array, then bucket pattern would best solve your problem [bucket pattern explanation][1]
[1]: https://www.mongodb.com/blog/post/building-with-patterns-the-bucket-pattern
, else just ordinary field manipulation should solve your problem.
Schema design for implementation of both, [bucket pattern] and [field manipulation] are shown as follows:
If you plan to keep track of all your changing logs, then bucket pattern is your best bet, your schema could look like this:
const mongoose = require('mongoose')
const { Schema } = mongoose
const numberLogSchema1 = new Schema({
field1: [
{type:Number}
],
field2: [
{type:Number}
],
field3: [
{type:Number}
],
field4: [
{type:Number}
]
})
module.exports = mongoose.model('numberLogs1',numberLogSchema1)
And its corresponding would look like this:
router.post('/numberLog', async (req, res) => {
try {
const saveNumberLog = await numberLog1.updateOne(
{ _id: numberCollectionIdhere },
{
$push: {
field1: req.body.fieldLogValue
}
})
res.json(saveNumberLog)
}catch (err) {
res.json({
message: err
})
}
})
Else if you just want to manipulate field values, at specific intervals from the frontend, using a javascript timer which could also be persisted to the database and fetched on a page Reload, your schema could look like this:
const mongoose = require('mongoose')
const { Schema } = mongoose
const numberLogSchema2 = new Schema({
field1: {
type: Number,
required: true,
default: 0
},
field2: {
type: Number,
required: true,
default: 0
},
field3: {
type: Number,
required: true,
default: 0
},
field4: {
type: Number,
required: true,
default: 0
}
})
module.exports = mongoose.model('numberLogs2',numberLogSchema2)
And its corresponding route paths, could look like this:
//if you intend to just increase the logvalue hourly
//without keeping track of it previous value then we use $inc
router.post('/numberLog2', async (req, res) => {
try {
const saveNumberLog2 = await numberLog2.updateOne(
{ _id: numberCollectionIdhere },
{
$inc: {
field1: req.body.fieldLogValue
}
})
res.json(saveNumberLog2)
}catch (err) {
res.json({
message: err
})
}
})

Unable to find index for $geoNear query error with mongoose

I have the following codes which try to create secondary indexes with mongoose. I have followed the mongoose official document to implement it ( mongoose documentation: Indexes section). However, when I send a GET request through Postman, an error, "unable to find index for $geoNear query", occurs. My understanding is that in my case, location is equivalent to a $geoNear object, so my code should be fine (I know it's not fine. That's why I have got an error). Any comments or suggestions would be greatly appreciated.
app.js(get endpoint)
app.get('/api/stores', (req, res) => {
const zipCode = req.query.zip_code;
const googleMapsURL = "https://maps.googleapis.com/maps/api/geocode/json";
axios.get(googleMapsURL, {
params: {
address: zipCode,
key : "KEY"
}
}).then((response) => {
const data = response.data
const coordinates = [
data.results[0].geometry.location.lng,
data.results[0].geometry.location.lat,
]
Store.find({
location: {
$near: {
$maxDistance: 3218,
$geometry: {
type: "Point",
coordinates: coordinates
}
}
}
}, (err, stores)=> {
if (err) {
console.log(err);
res.status(500).send(err);
} else {
res.status(200).send(stores);
}
})
}).catch((error)=> {
console.log(error);
})
})
store.js
const mongoose = require('mongoose');
const storeSchema = mongoose.Schema({
storeName: String,
phoneNumber: String,
address: {},
openStatusText: String,
addressLines: Array,
location: {
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
}
})
storeSchema.index({ location : "2dsphere"}, {sparse: true});
module.exports = mongoose.model('Store', storeSchema);
When creating a new index in MongoDB, you may have to drop the table as to have the index apply properly. Try creating the index on a fresh table. Does that work?
I figured out this error by first creating a new database and collection(I was using the default database named <dbname>). When I sent a POST request with the data I would like to store in MongoDB, MongoError: Can't extract geo keys appeared. I fixed this error by following this thread(reference). After these steps, my GET request worked and indexes were successfully created.

node.js mongoose subfield in a document

I have been working with node.js and mongoose for sometime and I am hitting a wall. I have a database with 20,000 documents and when i search the database from the cli it works fine.
db.Tickets.find({ "Customers.Customer.CustomerID" : '123123123' })
This returns 256 results
Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Define collection and schema for Ticket
var Ticket = new Schema({
UserName: {
type: String
},
Status: {
type: String
},
TicketNumber: {
type: Number
},
Name: {
type: String
},
Description: {
type: String
},
TicketTypeName: {
type: String
},
DueDate: {
type: Date
},
MapCollectDate : {
type: Date
},
NumberofUsersAffected : {
type: Number
},
DNNumber : {
type : String
},
RevisionDate : {
type : Date
},
CommercialImpact : {
type: String
},
Customers :[{
Customer: [{
CustomerID: Number,
CustomerName: String
}]
}],
Although if I test this in node.js using mongoose. I can't get it to return anything
I have a generic search that works
Ticket.find(function (err, tickets){
But can't get the specific search to work.
I am Connecting to Mongo
const config = require('./db');
//const Course = require('./models/Course');
//const CourseRoute = require('./routes/CourseRoute');
const Ticket = require('./models/Ticket');
const TicketRoute = require('./routes/TicketRoute');
const PORT = 4000;
mongoose.connect(config.DB).then(
() => {console.log('Connected to MongoDB') },
err => { console.log('Error connecting to MongoDB' +err)
});
Output of the log
Your node js server is running on PORT: 4000
Connected to MongoDB
Connected to MySQL
My Route End point
router.route('/').get(function (req, res) {
Ticket.find({ "Customers.Customer.CustomerID" : global.auth_username }, function(err, ticket) {
if(err){
console.log(err);
}
else {
res.json(tickets);
}
});
});
Also tried without the variable
router.route('/').get(function (req, res) {
Ticket.find({ "Customers.Customer.CustomerID" : "123123123" }, function(err, ticket) {
if(err){
console.log(err);
}
else {
res.json(tickets);
}
});
});
I had the same issue when I forgot to connect to Mongoose before running query
mongoose.connect(MONGO_URL, mongoOptions)
.then(() => {
// do your thing here
})
You had over a year to figured this out, and I am sure that you did so, but either way it seems that you have a typo in your code. The callback parameter is named ticket - function(err, ticket) {, whereas you are logging tickets - res.json(tickets);. In the generic test you correctly wrote tickets - Ticket.find(function (err, tickets){, which is probably why it worked.
The takeaway lesson here is - use debugging tools instead of logging, makes it easier to catch such problems.
Also, it would be appropriate to answer your own question once you've figured it out. But given that this is probably completely useless, you might as well delete it. Cheers!

Mongoose query nested document returns empty array

I have these schemas:
var Store = mongoose.model('Store', new Schema({
name: String
}));
var Client = mongoose.model('Cllient', new Schema({
name: String,
store: { type: Schema.ObjectId, ref: 'Store' }
}));
var Order = mongoose.model('Order', new Schema({
number: String,
client: { type: Schema.ObjectId, ref: 'Client' }
}));
I'm trying to code the Url handler of the API that returns the order details, which looks like this:
app.get('/api/store/:storeId/order/:orderId', function (...));
I'm passing the store id in the Url to quickly check if the logged user has permissions on the store. If not, it returns a 403 status. That said, I think this storeId and the orderId are enough data to get the order, so I'm trying to do a query on a nested document, but it just doesn't work.
Order.findOne(
{ 'client.store': req.params.storeId, _id: req.params.orderId },
function (err, order) { ... });
But the order object is null;
Even when I perform a find, it returns an empty array:
Order.find(
{ 'client.store': req.params.storeId },
function (err, results) { ... });
I know that I could as well pass the cliendId to the Url and check first if the client belongs to the store, and then retrieve the order from the client, but I think the client part is redundant, don't you think? I should be able to get the order in a secure way by using only these two fields.
What am I doing wrong here?
Ok, I found it. The secret was in the match option of populate. The final code looks like this:
Order
.findOne({ _id: req.params.orderId })
.populate({ path: 'client', match: { store: req.params.storeId } })
.exec(function (err, order) { ... });

Resources