get locations by grouping with mongodb - node.js

good work.
I have a mongoose schema similar to the following
const mongoose = require('mongoose')
var stations = new mongoose.Schema({
//başlık
title: {
type: String
},
//tam adresi
address: {
default:"-",
type: String
},
//ücretlendirme bilgisi
tariff_information: {
default:"-",
type: String
},
//soketler
sockets: {
type: Array,
default:[]
},
//şarj türü (SADECE AC VEYA DC)
type: {
type: String
},
//istasyon sahibi
station_owner: {
type: String
},
//istayon benzersiz kodu
station_code: {
default:"-",
type: String
},
//gps koordinatı
loc: {
"type": {
type: String,
enum: ['Point'],
default: 'Point',
},
"coordinates": {
type: [Number],
default: [0, 0], //[LONG , LAT]
required:true
},
},
}, { timestamps:true, skipVersioning: { dontVersionMe: true } })
stations.index({loc:"2dsphere"})
module.exports = mongoose.model("stations", stations, "stations");
I want to get the data by grouping from here, for example, if there are 1000 locations in 10 km2 area, I want it to return only 1 of them to me.
The reason why I want this is that I have close to 10 million documents in my collection and I'm trying to show it on a map, but it doesn't work at all. Let me reply with a minimum distance of 10km between the locations I want.
I tried using $geoHaystack but it is deprecated. I tried with $geoNear and still couldn't get the result I wanted.

Related

Sort data with a loop to find the nearest restaurant to a user

I have restaurant geocoordinates stored in a MongoDB database.
Now I need to create a loop to get all the restaurant geocoordinates with user geocoordinates to get the nearest 5 restaurants. I don't know how to loop and match the geocoordinates with the user.
I want to get exactly 5 restaurants nearby me because the Google place API is integrated in the frontend so I can only get the geocoordinates on which I need to match to get the exact restaurant nearby me.
nearby_restro: (req, res) => {var user_id = req.user;var reqdata = req.body;var lat = reqdata.lat;
var long = reqdata.long;
locationModel.find({
"loc": {$near: [longitude = long, latitude = lat]}}, (err, data) => {console.log(err)
console.log(data)})
model
userId :{ type: mongoose.Schema.Types.ObjectId, required: true, auto: true},
loc: {
type: {
type: String, // Don't do `{ location: { type: String } }`
enum: ['Point'], // 'location.type' must be 'Point'
},
cordinates:[{
longitude:{type:String },
latitude:{type:String },
}],
I want all the nearby restaurants of the user by using coordinates of the restro and the user in long and lat. I am using the user coordinates but this query is not working for me.
MongoDB Geospatial uses geoJSON. below is how the schema should look. note that the geo field was indexed using 2dsphere.
const productSchema = new Schema({
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
geo: {
type: {
type: String, // Don't do `{ location: { type: String } }`
enum: ['Point'], // 'location.type' must be 'Point'
default: 'Point',
},
coordinates: [
{ type: Number },
],
},
}, { timestamps: true });
productSchema.index({ geo: '2dsphere', name: 'text' });
to query the product collection, we'll use the MongoDB $geoNear query
const products = ProductModel.aggregate([
{
$geoNear: {
near: { type: 'Point', coordinates: [<lat>, <lng>] },
key: 'geo',
distanceField: 'dist.calculated',
spherical: true,
},
},
{ $limit: 10 },
]);
The distanceField will return a number showing the distance between the item and the passed coordiante.

MongoDB - Whats wrong with how I create index?

I am trying to use Mongo's GeoSpatial features to locate documents based on coordinates.
I need to create indexes for it to work - but it seems like I can't get the index created? Can you help?
I'll walk through what I have so far.
//User saves the location using a PUT Command.
props.updateBandLocation({
geometry: {
type: "Point",
coordinates: [ lat, lon ]
}
})
Here is the route that puts it in the database. I try to get the index created here.
router.put('/:id', (req, res) => {
quoteGenerator.findByIdAndUpdate({_id: req.params.id}, req.body).then(() => {
quoteGenerator.findOne({_id: req.params.id}).then(generator => res.send(generator))
quoteGenerator.createIndex( { bandLocation: "2dsphere" } )
})
})
The indexing throws an error in my terminal but it creates the location anyways. Here it is in the database.
//How the query looks in the database
"bandLocation": {
"geometry": {
"type": "Point",
"coordinates": [
32.96179,
-96.82916850000001
]
}
},
Lastly, I am trying to get all the documents near a point using this route.
router.get('/allbands/:lat/:lng', (req, res) => {
quoteGenerator.find(
{
bandLocation:
{ $near: {
$geometry: {
type : "Point",
coordinates : [-req.params.lng, +req.params.lat],
}
}
}
}
).then(bands => res.json(bands))
});
Thanks for any help you have to offer!
Here is my schema --
//Create GeoSchema
const GeoSchema = new Schema({
geometry: {
type: {
type: String,
default: "Point",
index: "2dsphere",
},
coordinates: {
type: [Number],
},
}
})
//Create Schema - Band
const AutoQuoteGeneratorSchema = new Schema({
baseCost: {
type: Number
},
mainDate: {
type: Object
},
quoteGenerator: {
type: Array,
required: true,
},
userId: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
bandName: {
type: String,
required: true,
},
bandBio: {
type: String,
required: true,
},
bandLocation: GeoSchema,
bandTour: {
type: Array,
required: true,
},
bandGenre: {
type: String,
required: true,
},
youtube: {
type: Array,
required: true,
},
published: {
type: Boolean,
required: true,
},
posts: {
type: Array,
required: true,
},
});
Ok so from what i can see we have a couple of problems:
Structure:
From the 2dsphere index docs:
The 2dsphere index supports data stored as GeoJSON objects and legacy coordinate pairs
What type are GeoJSON objects?, they're { type: <GeoJSON type> , coordinates: <coordinates> }
And what type are legacy coordinates pairs? they're in the form of: [<longitude>, <latitude> ] or { <field1>: <x>, <field2>: <y> }
So we can see bandLocation is neither, you need to use
quoteGenerator.createIndex( { "bandLocation.geometry": "2dsphere" } )
Your Coordinates are in the wrong order, You need to specify in the order of longitude then latitude. The valid range of latitude in degrees is -90 and +90. Your value of latitude of -96 is out of range.
So change your document to [-96.82916850000001, 32.96179].
Now we just need to adjust your query:
quoteGenerator.find({
"bandLocation.geometry":
{ $near: {
$geometry: {
type : "Point",
coordinates : [-req.params.lng, +req.params.lat],
}
}
}
})
** Another thing that pops up is that you create (try to create) an index every time there's a function call when in-fact you should only do it once. it should not be a part of your code. Now this won't throw an error but it's redundant as Mongo auto index documents on changes / inserts.

Mongoose geolocation dynamic max distance [duplicate]

This question already has answers here:
Find a Location within a stored Circle
(2 answers)
Ambiguity about $geoNear in aggregate query
(1 answer)
Closed 3 years ago.
Currenty I'm working with MongoDB and I have a users collection with the following schema:
const DEFAULT_JOB_RADIUS = 5000 // In meters
const settingsSchema = new Schema({
jobRadius: {
type: Number,
default: DEFAULT_JOB_RADIUS
}
})
const userSchema = new Schema({
firstName: {
trim: true,
type: String
},
lastName: {
trim: true,
type: String
},
email: {
trim: true,
type: String,
unique: true
},
password: {
type: String
},
token: {
type: String
},
fcmToken: {
type: String
},
lastLocation: {
type: pointSchema
},
settings: {
type: settingsSchema,
default: settingsSchema
}
}, {
timestamps: true
})
Point schema looks like this:
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const pointSchema = new Schema({
type: {
type: String,
enum: ['Point'],
default: 'Point'
},
coordinates: {
type: [Number],
default: [0, 0],
index: '2dsphere'
}
});
module.exports = pointSchema
Every user has a jobRadius property. This property represents the max distance of the user to any point.
In my code, I need to fetch all the users that are near a specific point.
Here is what I'm currently trying to do:
async getNearbyUsers(point) {
const users = await this.model.aggregate([
{
$geoNear: {
near: point,
distanceField: "dist.calculated",
maxDistance: '$settings.jobRadius',
spherical: true
}
}
])
return users
}
This code doesn't work. It always bring me all the users inside the collection.
If I change the maxDistance field to something like that, it works:
maxDistance: 1
My questions is - How can I perform such aggregation, where the max distance is dynamic and specific to each user?
Ok so I managed to solve this, with the help of #Ashh
I first calculate the distance between the users and the point and then I filter all the users that their radius property exceeds the distance.
async getNearbyUsers(point) {
const users = await this.model.aggregate([
{
$geoNear: {
near: point,
distanceField: "dist.calculated",
spherical: true
}
},
{
$match: {
$expr: {
$gt: ['$settings.jobRadius', '$dist.calculated']
}
}
}
])
return users
}

geo element must be an array or object: type: "Point"

When I tried post request like this
"{ "imgUrl": "Server\Pictures\i14182109167", "text": "Myself in
seoul", "tag": ["seoul", "tour"], "geometry" : {"type": "Point","coordinates": [80,
-27]} }"
The error causes
'Can\'t extract geo keys: { _id:
ObjectId(\'5b8e204455526366f86a6383\'), tag: [ "seoul", "tour" ],
date: new Date(1536041028423), imgUrl:
"Server\Pictures\i14182109167", text: "Myself in seoul", geometry: {
type: "Point", coordinates: [ 80, -27 ], _id:
ObjectId(\'5b8e204455526366f86a6384\') }, __v: 0 } geo element must
be an array or object: type: "Point"' }
even I added "type": "Point" in post request but, why?
const geoSchema = new Schema({
type: {
type: String,
default: 'Point',
index: '2dsphere'
},
coordinates: {
type: [Number]
}
});
const memoSchema = new Schema({
imgUrl: {
type: String
},
text: {
type: String
},
date: {
type: Date,
default: Date.now
},
tag: {
type: [String]
},
user: {
type: Schema.Types.ObjectId,
ref: 'Memo'
},
geometry: geoSchema
})
I experienced this error, tried several schema declarations, until I fixed by this implementing this design:
1. Create a new schema, which has Point as a property.
const mongoose = require('mongoose');
const {Point} = require('mongoose-geojson-schema');
const pointSchema = new mongoose.Schema({
type: {
type: String,
enum: ['Point'],
required: true
},
coordinates: {
type: [Number],
required: true
}
});
2. Schema for the object including above as a property:
const itemsSchema = new mongoose.Schema({
description: {
type: String,
required: true
},
location: {
type: pointSchema,
required: true
}
)
const Item = mongoose.model('Item', ItemsSchema);
module.exports = Item;
enter code here
3. Finally, my controller succesfully instantiates the object like this:
var geolocation =
{
type: 'Point',
coordinates: [
lng,
lat
]
};
const item = new Item({
description: req.body.description,
location: geolocation
})
Hope that helps.
I changed geometry to loc it worked!
but, I don't know why...
I was facing the same issue even after doing everything mention here. After spending too much time on this problem I get to know that if we save a default value it will work correctly.
Just follow the above answer and add
location: {
type: pointSchema,
default: {
type: 'Point',
coordinates: [0, 0],
},
required: false,
},
And delete the previous documents if any document has location.

Make a limit in a sub array of a MongoDB document

I have a MongoDB collection that allows to store videos rankings, here is the schema:
var ChallengeVideoRankingSchema = new Schema({
_challenge: {
type: ObjectId,
ref: 'Challenge',
},
since: {
type: String,
enum: ['all', 'day', 'week', 'month']
},
type: {
type: String,
enum: ['won_duels', 'diamonds']
},
total: {
type: Number
},
challengeVideos: [
{
rank: {
type: Number
},
_challengeVideo: {
type: ObjectId,
ref: 'ChallengeVideo'
}
}
],
creation_date: {
type: Date,
default: Date.now
},
});
I would like to make a request with mongoose which allows me to retrieve some of the ChallengeVideo objects in one of the rankings to make a pagination. I would like for example to have in the field "challengeVideos", 20 objects representing the ranks 1-20, 21-40, 41-60, ... according to a given parameter.
Thank you in advance,
Bastien
Get last elements with slice
db.posts.find( {}, { comments: { $slice: -3 } } )
link https://www.mongodb.com/docs/manual/reference/operator/projection/slice/#return-an-array-with-its-last-3-elements

Resources