MongoDB - Whats wrong with how I create index? - node.js

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.

Related

get locations by grouping with mongodb

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.

How to overwrite one field of a schema with another in mongodb

how can I overwrite the value of officialLyric with the value of updateLyric??
artist: { type: String, required: true },
title: { type: String, required: true },
officialLyric: { type: String, required: true },
editedLyrics: [
{
updatedLyric: String,
status: {
type: String,
enum: ["Aproved", "Rejected", "Pending"],
default: "Pending",
},
userId: { type: Schema.Types.ObjectId, required: true, ref: "User" },
},
],
releaseDate: { type: Date },
see image for clear depiction of the question.
enter image description here
You can try update with aggregation pipeline starting from MongoDB 4.2,
$arrayElemAt to get first value of updatedLyric from editedLyrics array and update it into officialLyric
db.collection.updateMany(
{}, // put your query
[{
$set: {
officialLyric: {
$arrayElemAt: ["$editedLyrics.updatedLyric", 0]
}
}
}]
)
Playground
If you want to always have to value of latest updateLyric in editedLyric array in officialLyric, you don't need to actually store officialLyric in DB. you can use mongoose virtual fields and remove officialLyric from schema.
LyricSchema.virtual('officialLyric').get(function () {
if(!this.editedLyrics.length) return null;
return this.editedLyrics[this.editedLyrics.length-1].updatedLyric;
});
If you still want to store the officialLyric first and then overwrite it with edited version you save. You can use hooks.
LyricSchema.post('save', async(error, doc, next) => {
if(doc.editedLyrics.length && doc.officialLyric != doc.editedLyrics[doc.editedLyrics.length-1].updatedLyric){
doc.officialLyric = doc.editedLyrics[doc.editedLyrics.length-1].updatedLyric;
await doc.save();
}
next();
});

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.

how to save geojson with mongodb?

I am trying to save a geojson, using mongoose and nodejs, for the moment I want to save a geojson of type 'MultiPoint'
this is the scheme that I have defined to save the geojson
let capaSchema = new Schema({
nombrecapa: {
type: String,
required: [true, 'El nombre de la capa es necesario']
},
descripcion: {
type: String,
required: [false]
},
geojson: Object([
geoSchema
])
});
const geoSchema = new Schema({
type: {
type: String,
default: 'FeatureCollection',
},
features: [
Object({
type: {
type: String,
default: 'Feature',
},
geometry: {
type: {
type: String,
default: 'MultiPoint'
},
coordinates: {
type: [
Number
],
index: '2dsphere'
}
}
})
],
});
this is the object that I want to save using the save method of moongose, first I make an instance of the schema, maybe my error may be inside the instance.
let capa = new Capa({
nombrecapa: body.nombrecapa,
descripcion: body.descripcion,
geojson: {
type: body.typefeature,
features: [{
type: body.featurestype,
geometry: {
type: body.geometrytype,
coordinates: [
[-105.01621, 39.57422],
[-105.01231, 39.57321]
]
}
}
]
}
});
capa.save((err, capadb) => {
if (err) {
return res.status(400).json({
ok: false,
err
})
}
res.json({
ok: true,
capa: capadb
})
})
but at the time of saving I returned the following errors:
"_message": "Capa validation failed",
"message": "Capa validation failed: geojson.0.features.0.geometry.coordinates: Cast to Array failed for value \"[ [ -105.01621, 39.57422 ], [ -105.01231, 39.57321 ] ]\" at path \"geometry.coordinates\"",
"name": "ValidationError"
In your schema you have coordinates as a single array but the data being passed is actually an array of nested arrays
I think what you need here is
coordinates: {
type: [[Number]],
index: '2dsphere'
}

mongoose $near populate ignored

I hope some one can help me - i want to populate a subdocument with a geo query to sort them:
i've got these models (simplified):
(a lot of articles):
var articleSchema = mongoose.Schema({
places: [{ type: mongoose.Schema.Types.ObjectId, ref: "places", required: false }],
someData: { type: String, required: true },
})
(a lot of places):
var placeSchema = mongoose.Schema({
longitudelatitude: {
type: { type: String, required: true },
coordinates: [
{ type: Number, required: true }, //longitude
{ type: Number, required: true } //latitude
]},
someData: { type: String, required: true }
})
my first query to find only places near a Position works fine:
getPlacesNearBy: function (lng, lat, skipNumber, limitNumber, callback) {
Place.find({
longitudelatitude: {
$near: {
$geometry: { type: "Point", coordinates: [lng, lat] },}}
}, null, {skip: skipNumber, limit: limitNumber}, function (err, foundPlaces) {
if (err)
return callback(err, null);
return callback(null, foundPlaces);
})
},
i get places near me - i can choose with limit how many - and i can reload some more with skip
now i wanted to do something similar:
i want to get an article - and populate the stored places (where you can get them) AND AFTER THAT i want to sort the places by distance and maybe to skip or limit the Response
so i tryed:
getPlacesforArticle: function (articleId, lng, lat, skipNumber, limitNumber, callback) {
var projection = null;
Article.findById(articleId, {places: 1}).populate("places", projection, {
longitudelatitude: {
$near: {
$geometry: { type: "Point", coordinates: [lng, lat] },
}
}
}, {skip: skipNumber, limit: limitNumber}).exec(function (getError, foundArticle) {
if (getError)
return callback(getError, null);
callback(null, foundArticle.places);
});
}
},
So this query is working (throw no error) but it doesnt response what i want - i get places but they are "ordered" by database sequenze not by distance - so the $near seems to be ignored (tested without this query brings same result) but when i filter to some other content that works fine (like Name="test") also the limit is working but i cant skip some places of this response...
well i hope some one understand me and can help me =)!
thank you very much!
So i've got a solution for my Problem...
https://docs.mongodb.com/manual/reference/command/geoNear/
use db.aggregate instead of db.find!
there you can define a seperate query =)

Resources