How to use aggregate with $near mongoose? - node.js

I was trying to get random 20 foods near the particular location. When i used find it works but how to use aggregate with $near
Schema:
const foodSchema = new Schema({
foodName: {
type: String,
required: true
},
image: {
type: String,
required: true
},
price: {
type: String,
required: true
},
shopName: {
type: String,
required: true
},
isVerified:{
type:Boolean,
default:false,
required:true
},
isEnabled:{
type:Boolean,
default:false,
required:true
}
,
location: {
type: { type: String },
coordinates: [],
},
shopId:{
type: Schema.Types.ObjectId,
ref: 'Shop',
required: true
}
});
foodSchema.index({ location: "2dsphere" });
Food.aggregate([{$match:{isEnabled:true,location: {
$near: {
$maxDistance: 10000,
$geometry: {
type: "Point",
coordinates: [13.3339, 80.1943]
}
}
}}}])
.then(data=>{
console.log(data);
res.render('user/home',{
pageTitle:"UiMart",
foodlist:data
});
})
.catch(err=>{
console.log(err);
res.send("err");
});
MongoError: $geoNear, $near, and $nearSphere are not allowed in this context
How to use aggregate function with $near mongoose to get random foods near the particular location

Could you try something like this:
Food.aggregate
([
{
$geoNear: {
near: { type: "Point", coordinates: [13.3339, 80.1943] },
distanceField: "dist.calculated",
maxDistance: 10000,
query: { "isEnabled": true }
}
}
])
In the aggregation framework the $geoNear should be the first in the pipeline and you can use the query to filter the results instead of using match.

Related

How to add GeoJson data with mongoose?

I am sending the object to create a user model.
"{
type: 'Point',
coordinates: [ 25.2239771, 51.4993224 ]
}"
And, here is the Mongoose Schema that I created.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserProfileSchema = new Schema(
{
userId: {
type: String,
// required: true,
unique: true,
},
userFirstName: {
type: String,
// required: true,
},
userLastName: {
type: String,
// required: true,
},
userGender: {
type: String,
// required: true,
},
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
index: '2dsphere',
},
},
},
{ collection: 'userprofilemodels' }
);
module.exports = UserProfile = mongoose.model(
'userprofilemodels',
UserProfileSchema
);
This is the code that I am using to add geoJson type file. However, I am getting an error.
I also tried to add index after the Schema has been defined
await new userProfileModel({
userId,
userFirstName,
userLastName,
userCoordinates,
})
.save()
.then(() => {
console.log('it worked!');
res.send('worked!');
})
.catch((error) => {
console.log('did not worl')
// console.log(error);
});
If I exclude userCoordinates, then it works. So, def my geoJson object is wrong. However, I have no clue where I have made mistakes.
Mongoose supports GeoJSON objects indexing so first add the "2dsphere" index to the userCoordintes rather than to the coordinates within the object in order to make it work.
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
default: undefined,
required: true
},
index: '2dsphere'
},
Make sure your userCoordinates looks something like this:
const userCoordinates = {
type: "Point",
coordinates: [coordinatesA, coordinatesB],
};
Taken from the mongoose documentation it seems the GeoJSON Type mustn't be only a String.
here is the example with location as a GeoJSON type:
const citySchema = new mongoose.Schema({
name: String,
location: {
type: {
type: String, // Don't do `{ location: { type: String } }`
enum: ['Point'], // 'location.type' must be 'Point'
required: true
},
coordinates: {
type: [Number],
required: true
}
}
});

$pull not removing element from collection

I am trying to make a like/unlike system. and for unlike i need to remove the user from array of likes. but the mongodb $pull doesn't work.
the $inc command works fine but $pull doesn't.
here is my schema
postedBy: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User"
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
],
likeCount: {
type: Number,
default: 0
},
commentBody: {
type: String
},
contentId: {
type: mongoose.Schema.Types.ObjectId,
refPath: "contentType",
required: true
},
contentType: {
type: String,
required: true,
enum: ["Video", "Comment"]
},
isApproved: {
type: Boolean,
default: true
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
],
postedOn: {
type: Date,
default: Date.now()
}
mongodb query
await Video.updateOne(
{ _id: contentId, likeCount: { $gt: 0 } },
{
$pull: {
likes: decoded.id
},
$inc: { likeCount: -1 }
}
);
You should update your query to look like this:
await Video.updateOne(
{ _id: contentId, likeCount: { $gt: 0 } },
{
$pull: {
"likes": { "_id": ObjectId(decoded.id)}
},
$inc: { likeCount: -1 }
}
);
I assume that in devede.id you have string, and string is not the same as ObjectId. Because of that we need to transform it to ObjectId.

Mongoose : Can't populate after find to make $near query

I am trying to populate user informations to get his location but i am getting undefined.
Here are my schemas:
AnnounceSchema:
const AnnounceSchema = new mongoose.Schema({
titre: String,
contenu: String,
image: String,
tag: String,
media: String,
date: {
type: Date,
default: Date.now
},
commentaires: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
authorId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
})
UserSchema:
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true, unique: true },
loc: { type: { type: String, default: 'Point' }, coordinates: { type: [Number] } }
});
UserSchema.index({ loc: '2dsphere' });
And here is my query :
Announce.find({})
.populate('authorId', null,
{
'authorId.loc': {
$near: {
$geometry: {
type: 'Point',
coordinates: [req.user.loc.coordinates[0], req.user.loc.coordinates[1]]
},
$maxDistance: 10000
}
}
})
.exec((err, announces) => {
if (err) {
res.send(err)
} else {
res.render('announce/search', { announces: announces })
}
})
The error I'm getting is "unable to find index for $geoNear query". I've added an index to the AnnonceSchema but no change:
AnnonceSchema.index({ "authorId.loc" : "2dsphere"})
This should work:
Announce.find({})
.populate('author',
{
path : 'authorId',
match: {
'loc': {
$near: {
$geometry: {
type: 'Point',
coordinates: [req.user.loc.coordinates[0], req.user.loc.coordinates[1]]
},
$maxDistance: 10000
}
}
},
model: 'User',
})
.exec((err, announces) => {
if (err) {
res.send(err)
} else {
res.render('announce/search', { announces: announces })
}
})
populate works in two ways
it can populate by reference Announce.find({}).populate('authorId')
or by a custom query (which is what you need)

Mongoose sub field aggregation with full text search and project

I have a Mongoose model called Session with a field named course (Course model) and I want to perform full text search on sessions with full text search, also I wanna aggregate results using fields from course sub field and to select some fields like course, date, etc.
I tried the following:
Session.aggregate(
[
{
$match: { $text: { $search: 'web' } }
},
{ $unwind: '$course' },
{
$project: {
course: '$course',
date: '$date',
address: '$address',
available: '$available'
}
},
{
$group: {
_id: { title: '$course.title', category: '$course.courseCategory', language: '$course.language' }
}
}
],
function(err, result) {
if (err) {
console.error(err);
} else {
Session.deepPopulate(result, 'course course.trainer
course.courseCategory', function(err, sessions) {
res.json(sessions);
});
}
}
);
My models:
Session
schema = new mongoose.Schema(
{
date: {
type: Date,
required: true
},
course: {
type: mongoose.Schema.Types.ObjectId,
ref: 'course',
required: true
},
palnning: {
type: [Schedule]
},
attachments: {
type: [Attachment]
},
topics: {
type: [Topic]
},
trainer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'trainer'
},
trainingCompany: {
type: mongoose.Schema.Types.ObjectId,
ref: 'training-company'
},
address: {
type: Address
},
quizzes: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'quiz'
},
path: {
type: String
},
limitPlaces: {
type: Number
},
status: {
type: String
},
available: {
type: Boolean,
default: true
},
createdAt: {
type: Date,
default: new Date()
},
updatedAt: {
type: Date
}
},
{
versionKey: false
}
);
Course
let schema = new mongoose.Schema(
{
title: {
type: String,
required: true
},
description: {
type: String
},
shortDescription: {
type: String
},
duration: {
type: Duration
},
slug: {
type: String
},
slugs: {
type: [String]
},
program: {
content: {
type: String
},
file: {
type: String
}
},
audience: [String],
requirements: [String],
language: {
type: String,
enum: languages
},
price: {
type: Number
},
sections: [Section],
attachments: {
type: [Attachment]
},
tags: [String],
courseCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'course-category',
required: true
},
trainer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'trainer'
},
trainingCompany: {
type: mongoose.Schema.Types.ObjectId,
ref: 'training-company'
},
status: {
type: String,
default: 'draft',
enum: courseStatus
},
path: {
type: String
},
cover: {
type: String,
required: true
},
duration: {
type: Number,
min: 1
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date
}
},
{ versionKey: false }
);
I am not sure if what I tried is gonna bring me what I want and I am getting this error concerning the $unwind operator:
MongoError: exception: Value at end of $unwind field path '$course'
must be an Array, but is a OID
Any kind of help will be really appreciated.
You can try below aggregation.
You are missing $lookup required to pull course document by joining on course object id from session document to id in the course document.
$project stage to keep the desired fields in the output.
Session.aggregate([
{
"$match": {
"$text": {
"$search": "web"
}
}
},
{
"$lookup": {
"from": "courses",
"localField": "course",
"foreignField": "_id",
"as": "course"
}
},
{
"$project": {
"course": 1,
"date": 1,
"address": 1,
"available": 1
}
}
])
Course is an array with one course document. You can use the $arrayElemAt to project the document.
"course": {"$arrayElemAt":["$course", 0]}

Node.js with mongoose delete one array element

I am trying to delete one array element when I click delete button on jade view page.
When clicked, it's going to send selected instructor objected as req.body.
At sever side, it will find courses that contain the instructor objectId.
Any idea for me?
Thank you for reading it.
here is my code:
var id = req.body._id;
clist.find({ instructors: { $in: [id] } }).exec(function (err, result) {
result.forEach(function (obj) {
clist.update(
{ _id: new mongoose.Types.ObjectId(obj._id)},
{ $pull: { instructors : [new mongoose.Types.ObjectId(id)] } }
);
console.log(new mongoose.Types.ObjectId(obj._id) + ' was deleted');
});
});
Schema Clist and ilist:
var instructorlist = mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
gender: { type: String, required: true },
DOB: { type: Date, required: true, default: Date.now },
email: { type: String, required: true },
phone: { type: Number, required: true },
address: { type: String, required: true },
dateofstart: { type: Date, required: true},
courses: [{
type: mongoose.Schema.Types.ObjectId,
ref: "clist"
}]
});
var courselist = mongoose.Schema({
coursename: { type: String, required: true },
coursenumber: { type: String, required: true },
coursecredit: { type: Number, required: true },
courseroom: { type: String, required: false },
courseregisteddate: {type: Date, default: Date.now},
students: [{
type: mongoose.Schema.Types.ObjectId,
ref: "slist"
}],
instructors: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ilist"
}]
});
one example for mongodb :
{
"_id": {
"$oid": "591a7a3b391a1842e8a69e23"
},
"coursename": "JDKD",
"coursenumber": "COMP4483",
"coursecredit": 4,
"courseroom": "sdaf",
"instructors": [
{
"$oid": "591a374422a3a13d38c0bbe5"
}
],
"students": [],
"courseregisteddate": {
"$date": "2017-05-16T04:04:11.848Z"
},
"__v": 0
}
When I add instructor objectID in Course.
var newcourse = new clist({
'coursename': req.body.coursename, 'coursenumber': req.body.coursenumber, 'coursecredit': req.body.coursecredit
, 'courseroom': req.body.room, 'instructors': instructors._id
});
Use same operation to find and update multiple
clist.update(
{ instructors: { $in: [id] }},
{ $pull: { instructors : { _id : new mongoose.Types.ObjectId(id) } } }, //or{ $pull: { instructors: mongoose.Types.ObjectId(id) } }
{
multi:true
},
function(error, success){
if(error){
console.log("error",error)
}
console.log("success",success)
});

Resources