So I have two schemas user and driver they both have latitude and longitude attributes.
At some point I want to query the database for nearby drivers, I will be sending the user's current location (latitude and longitude) and I have a function to calculate the distance between two points.
I am trying to do something like this:
find all drivers with distance less than 2 KM using my function ( the function is called calculateDistance).
In code this will be like this:
const drivers = await Driver.find();
const driversToReturn = drivers.filter(
driver => calculateDistance(userLat, userLong, driver.latitude, driver.longitude) <= 2
);
res.status(200).json({
drivers: driversToReturn
});
but I don't think this is the best way to do it, I've checked the mongoose virtuals but we can't pass params (userLat and userLong) to the get method of a virtual and I don't think instance methods are the solution.
so how should I do this?
Thanks
EDIT
Driver Model
const mongoose = require("mongoose");
const { Schema } = mongoose;
const driverSchema = new Schema(
{
/** Required Attributes */
name: { type: String, required: true },
carBrand: { type: String, required: true },
plateNumber: { type: String, required: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: true },
price: { type: Number, required: true },
latitude: { type: Number, required: true },
longitude: { type: Number, required: true },
/** Not Required Attributes */
rating: { type: Number, required: false },
},
{
timestamps: true,
}
);
const Driver = mongoose.model("Driver", driverSchema);
module.exports = Driver;
User Model
const mongoose = require("mongoose");
const { Schema } = mongoose;
const userSchema = new Schema(
{
/** Required Attributes */
name: { type: String, required: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: true },
latitude: { type: Number, required: true },
longitude: { type: Number, required: true },
},
{ timestamps: true }
);
const User = mongoose.model("User", userSchema);
module.exports = User;
Users Controller
const Driver = require("../models/driver");
const Order = require("../models/order");
const calculateDistance = require("../utils/calculateDistance");
const CIRCLE_RADIUS_IN_KM = 2;
exports.getNearbyDrivers = async (req, res, next) => {
try {
const userLat = req.body.userLat,
userLong = req.body.userLong,
drivers = await Driver.find();
const driversToReturn = drivers.filter(
(driver) =>
calculateDistance(
userLat,
userLong,
driver.latitude,
driver.longitude
) <= CIRCLE_RADIUS_IN_KM
);
res.status(200).json({
drivers: driversToReturn,
});
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
Here is some code from the documentation of Mongoose that can help you with that I think :
const denver = { type: 'Point', coordinates: [-104.9903, 39.7392] };
return City.create({ name: 'Denver', location: denver }).
then(() => City.findOne().where('location').within(colorado)).
then(doc => assert.equal(doc.name, 'Denver'));
and here is the link : https://mongoosejs.com/docs/geojson.html
Related
I’m pretty new to using Mongoose and can’t seem to find a fix. I have two schema’s; postSchema, commentSchema. The first one is for a post and the second is for comments that are stored within the post. Both schema’s have a map field to store likes. The post likes field’s setter and getter work when I try to update but when I try to do the same for the comments it gives me an error that the set or get is not a function. When I check if the likes are an instance of a map, the post likes will return true, while the comments like will return false. If anyone could help or direct me in the right direction it would be greatly appreciated.
Here is the code that I'm working with. When I create a comment to add to a post, the comment.likes checks as a Map. After I update the post I make a call to get all the post's and I rechecked that the comment.likes is a Map, but now it turns out false.
import mongoose from 'mongoose';
const postSchema = mongoose.Schema(
{
userId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
picturePath: {
type: String,
default: '',
},
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
likes: {
type: Map,
of: Boolean,
default: new Map(),
},
comments: {
type: Array,
default: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
},
},
{ timestamps: true }
);
const Post = mongoose.model('Post', postSchema);
export default Post;
import mongoose from 'mongoose';
const commentSchema = mongoose.Schema(
{
postId: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
picturePath: {
type: String,
default: '',
},
description: {
type: String,
required: true,
},
likes: {
type: Map,
of: Boolean,
default: new Map(),
},
},
{ timestamps: true }
);
const Comment = mongoose.model('Comment', commentSchema);
export default Comment;
export const addComment = async (req, res) => {
try {
const { id } = req.params;
const { userId, picturePath, description } = req.body;
const user = await User.findById(userId);
const newComment = new Comment({
id,
userId,
userName: user.userName,
picturePath,
//likes: {},
description,
});
newComment.set('likes', new Map());
console.log(newComment.likes instanceof Map);
const upDatedPost = await Post.findByIdAndUpdate(
id,
{ $push: { comments: newComment } },
{ new: true }
);
const allPost = await Post.find();
console.log(allPost[0].comments[2].likes instanceof Map);
res.status(200).json(allPost);
} catch (err) {
console.log('err');
res.status(404).json({ message: err.message });
}
};
This works for the post.likes.
export const likePost = async (req, res) => {
try {
const { id } = req.params;
const { userId } = req.body;
const post = await Post.findById(id);
const isLiked = post.likes.get(userId);
if (isLiked) {
post.likes.delete(userId);
} else {
post.likes.set(userId, true);
}
const upDatedPost = await Post.findByIdAndUpdate(
id,
{ likes: post.likes },
{ new: true }
);
res.status(200).json(upDatedPost);
} catch (err) {
res.status(404).json({ message: err.message });
}
};
This doesn’t work. When I check if element.likes is an instanceOf Map it gives back false, but for post.likes it returns true. Updated with the console.log's.
export const likeComment = async (req, res) => {
try {
const { id } = req.params;
const { postId, userId } = req.body;
let post = await Post.findById(postId);
let comments = post.comments;
console.log('comments: ', comments);
console.log('likes: ', comments[0].likes);
console.log(
'Is likes an instanceof Map: ',
post.comments[0].likes instanceof Map
);
//comments[0].likes.set(userId, true);
//post.comments[0].set('likes', new Map());
//console.log(comments[6].likes);
// comments.forEach((element) => {
// if (element._id.toString() === id) {
// element.likes.set(userId, true);
// }
// });
res.status(200).json(post);
} catch (err) {
res.status(404).json({ message: err.message });
}
};
Here is the output fro the console.log's.
comments: [
{
userId: '63dc0274bd8c03b1e417cfc4',
userName: 'dummyUserThree',
picturePath: '',
description: 'Likes still not working',
_id: new ObjectId("63e13f26603a052fc8f16b09"),
likes: {}
}
]
likes: {}
Is likes an instanceof Map: false
I'm quite new to node and mongoose. I'm trying to do a project using them, but i'm running into an error while trying to populate. The comment is saved to the Comment schema perfectly, but throws an error when i reference it Organization Schema.Please advise me on what i'm doing wrong. Any form of assistance will be appreciated.
// Post route for comment(on the Organization's profile page)
router.post('/comment/:id', ensureAuthenticated,(req, res) =>{
let id = req.params.id;
console.log(mongoose.Types.ObjectId.isValid(id))
const commentObject = new Comment({
sender: 'Fred kimani',
commentBody: req.body.commentBody
})
console.log(commentObject);
commentObject.save((err, result) =>{
if(err){console.log(err)}
else{
Organization.findByIdAndUpdate(id, {$push: {comments: result}}, {upsert: true}, (err, organization) =>{
if(err){console.log(err)}
else{
console.log('======Comments====')
}
})
res.redirect('/users/organizationprofilepage/:id')
}
})
});
//Organization Schema
const mongoose = require('mongoose');
const OrganizationSchema = new mongoose.Schema({
organization_name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
category: {
type: String,
required: true
},
isApproved: {
type: Boolean,
default: false
},
image:{
type:String,
required:true
},
description: {
type: String,
required: true,
},
comments: [{
type: mongoose.Types.ObjectId,
ref: 'Comment'
}],
},
//{ typeKey: '$type' }
);
OrganizationSchema.statics.getOrganizations = async function () {
try {
const organizations = await this.find();
return organizations;
} catch (error) {
throw error;
}
}
//defines the layout of the db schema
const Organization = mongoose.model('0rganization', OrganizationSchema);
module.exports = Organization;
//Comment schema
const mongoose = require('mongoose');
const CommentSchema = mongoose.Schema({
sender: {
type: String,
},
commentBody: {
type: String,
required: false,
},
date: {
type: Date,
default: Date.now
},
})
CommentSchema.statics.getComments= async function () {
try {
const comments = await this.find();
return comments ;
} catch (error) {
throw error;
}
}
const Comment= mongoose.model('Comment', CommentSchema);
module.exports = Comment;
Try to change the comments type to mongoose.Schema.Types.ObjectId:
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
},
],
Try to push the new commend _id into the Organization object after its creation, not the whole object:
commentObject.save((err, result) => {
if (err) {
console.log(err);
} else {
Organization.findByIdAndUpdate(
id,
{ $push: { comments: result._id } }, // <- Change this line
{ upsert: true },
(err, organization) => { }
);
...
}
});
If you just updated the schema you will need to make sure all of the comments are following the new form you created, when you save it will attempt to validate them, that is why an updateOne will work but not await save()
Here is the function getPublicFields in User Schema
User Schema
UserSchema.methods.getPublicFields = function () {
var returnObject = {
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
_id: this._id,
};
return returnObject;
};
here just I connect the User Schema with the product and they gave me all the user Data watch I don't want
productController.Js
exports.getProducts = async (req, res, next) => {
try {
const products = await Product.find().populate("owner");
res.status(200).send(products);
} catch (e) {
next(e);
}
};
Product Schema
var mongoose = require("mongoose");
var { Schema } = mongoose;
const ProductSchema = new Schema({
title: {
type: String,
},
category: {
type: String,
},
price: {
type: Number,
},
completed: {
type: Boolean,
default: false,
},
owner: {
ref: "User",
type: mongoose.Schema.Types.ObjectId
},
img: {
type : Array,
}
});
module.exports = mongoose.model("Product", ProductSchema);
populate will give you a plain object, not a Mongoose instance. What you can do is construct a User instance from it:
const user = new User(product.owner);
product.owner = user.getPublicFields();
At this moment I try to find the query which works in this case, but then want to update the object. So I want to check if review object exist and if not then create that key name first and then push that object into an array. Else push into that in existing object of array.
Default object looks like (without review object):
const mongoose = require('mongoose');
const musicSchema = mongoose.Schema({
id: {
type: Number,
required: true
},
artist: {
type: String,
required: true
},
title: {
type: String,
required: true
},
release_year: {
type: Number,
required: true
},
genre_id: {
type: Number,
required: true
},
image_url: {
type: String,
required: true
},
reviews: [{
id: {
type: Number,
required: true
},
locale: {
type: String,
required: true
},
rating: {
type: Number,
required: true
},
comment: {
type: String,
required: true
}
}]
});
const Music = mongoose.model("Music", musicSchema); // now we have to create our model
console.log;
module.exports = Music; // export our created model
app.post('/addReview/:id', async (req, res) => {
let idNumber = parseInt(req.params.id); // 501437
let reviewObject = req.body; // {id: "501437", locale: "nl", rating: 3, text: "helello"}
try {
const music = client.db('database').collection('music');
const query = { id: idNumber };
const musicSong = await music.findOne(query);
await musicSong.update({ $push: { reviews: reviewObject } }); // error comes from here
} catch (err) {
console.log(err);
}
});
check if reviews field is not exists then initialise it to blank array
push object to reviews
save() to save main document
app.post('/addReview/:id', async (req, res) => {
let idNumber = parseInt(req.params.id); // 501437
let reviewObject = req.body; // {id: "501437", locale: "nl", rating: 3, text: "helello"}
try {
const music = client.db('database').collection('music');
const query = { id: idNumber };
let musicSong = await music.findOne(query);
if (!Array.isArray(musicSong.reviews)) {
musicSong.reviews = [];
}
musicSong.reviews.push(reviewObject);
music.save();
} catch (err) {
console.log(err);
}
});
Second option using updateOne():
It does not require to find, check and save operations if you use update methods,
app.post('/addReview/:id', async (req, res) => {
const query = { id: parseInt(req.params.id) };
let reviewObject = req.body;
try {
const music = client.db('database').collection('music');
await music.updateOne(query, { $push: { reviews: reviewObject } });
} catch (err) {
console.log(err);
}
});
How can I set default value (like bdhash which is async) to one field in my mongoose schema?
Now I see only promise inside. But why? Seems that I'm using async/await in a right way. Also I tried to do this in a hook ('validate')
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
bcrypt = require('bcrypt');
hashIt = async () => {
let pwd = new Date();
pwd = pwd.toUTCString() + Math.random();
return await bcrypt.hash(pwd, Number(process.env.SALT_WORK_FACTOR));
};
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
unique: 'Please enter a unique business name',
required: 'Please enter a business name'
},
salt: {
type: String,
trim: true,
default: () => {
return hashIt();
},
required: 'Please enter a business salt'
},
created: {
type: Date,
default: Date.now
}
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);
/* Also tried this way
businessSchema.pre('validate', next => {
if (this.salt) {
return next();
}
this.salt = hashIt();
next();
}); */
module.exports = mongoose.model('Business', businessSchema);
Is it possible to do? And how? The best way :)
see http://mongoosejs.com/docs/defaults.html
Check this example :
var schema = new Schema({
title: String,
genre: {type: String, default: 'Action'}
});
var Movie = db.model('Movie', schema);
var query = {};
var update = {title: 'The Terminator'};
var options = {
// Create a document if one isn't found. Required
// for `setDefaultsOnInsert`
upsert: true,
setDefaultsOnInsert: true
};
Movie.
findOneAndUpdate(query, update, options, function (error, doc) {
assert.ifError(error);
assert.equal(doc.title, 'The Terminator');
assert.equal(doc.genre, 'Action');
});