MongoDB: Point not in interval when using $near operator with $maxDistance - node.js

When I try to find all members within 50km of Salt Lake City, Utah from the Mongo shell I get the error:
error: {
"$err" : "point not in interval of [ -180, 180 ] :: caused by :: { 0: 0.0, 1: 50000.0 }",
"code" : 16433
}
Here is the query I am running:
db.members.find(
{ 'geo.point' :
{ $near :
{
$geometry : {
type : "Point" ,
coordinates : [ 111.000 , 40.000 ]
},
$maxDistance : 50000
}
}
}
)
Member schema is like this:
var memberSchema = mongoose.Schema({
name: {
first: {type:String, default:''},
last: {type:String, default:''},
},
geo: {
latitude: {type:String, default:''},
longitude: {type:String, default:''},
country: {type:String, default:''},
state: {type:String, default:''},
place: {type:String, default:''},
zip: {type:String, default:''},
point: {type: [Number], index: '2d'}
}
});
Member object in DB looks like this:
{
"_id" : ObjectId("xxxxxxxxxxxxxxxxxxx"),
"name": {
"first": "Thom",
"last": "Allen"
},
"geo" : {
"point" : [ -111.8833, 40.7500 ],
"zip" : "84115",
"state" : "UT",
"country" : "US",
"longitude" : "-111.8833",
"latitude" : "40.7500"
}
}
Is it possible that my fields are not stored in the correct format? If I change 50000 to anything below 180 it will work, but that is not how it should function according to the docs here:
http://docs.mongodb.org/manual/reference/operator/query/near/
** Just a heads up, the proper mongo location array IS in fact [longitude, latitude].

A few things. First, I think your query is off - you are querying for coordinates : [ 111.000 , 40.000 ] and it should be coordinates : [ -111.000 , 40.000 ]
Second, the example data point your provide [ -111.8833, 40.7500 ] is more than 50 km from your corrected query point, it's actually about 122 km (test it here: http://andrew.hedges.name/experiments/haversine/ )
So, correcting for those two issues if I store the data in mongodb as you have stored it I can do the following:
1) create the correct index:
db.members.ensureIndex({ "geo.point": "2dsphere" })
2) run this query:
db.members.find({ 'geo.point':
{$geoWithin:
{$centerSphere: [[ -111.000 , 40.000 ], 113/6371]}
}
} )
Note that I've divided 113 km/ 6371 which gives you radians which is what is required for this specific query.
Try it yourself. In general you will be better off if you can store things in the future using GeoJSON but with your existing schema and the above index and query I'm able to get the correct results.

What you have in your data is the format for legacy co-ordinate pairs but you are trying to query using the GeoJSON syntax.
The only valid index form for legacy co-ordinate pairs is a "2d" index, so if you have created a "2d sphere" index that will not work. So you need to remove any "2d sphere" index and create a "2d" index as follows:
db.members.ensureIndex({ "geo.point": "2d" })
If you actually intend to use the GeoJSON form and "2dsphere" index type, then you need the data to support it, for example:
{
"loc" : {
"type" : "Point",
"coordinates" : [ 3, 6 ]
}
}
So it needs that underlying structure of "type" and "coordinates" in order to use this index type and query form.

Related

find records where field type is string

I have this records :
{id : 1 , price : 5}
{id : 2 , price : "6"}
{id : 3 , price : 13}
{id : 4 , price : "75"}
I want to build a query that get just record who have price with type "string"
so, it will get :
{id : 2 , price : "6"}
{id : 4 , price : "75"}
You can use the $type query operator to do this:
db.test.find({price: {$type: 2}})
If you're using MongoDB 3.2+, you can also use the string alias for the type:
db.test.find({price: {$type: 'string'}})
While #JohnnyHK's answer is absolutely correct in most cases, MongoDB also returns documents where field is an array and any of the elements in that array have that type (docs). So for example, the document
{
_id: 1,
tags: ['123']
}
is returned for the query Books.find({ tags: { $type: "string" } }) as well. To prevent this, you can adjust the query to be
Books.find({
tags: {
$type: "string",
$not: {
$type: "array"
}
}
})

How to aggregate fields from embedded documents in Mongoose

Coverage Model.
var CoverageSchema = new Schema({
module : String,
source: String,
namespaces: [{
name: String,
types: [{
name: String,
functions: [{
name: String,
coveredBlocks: Number,
notCoveredBlocks: Number
}]
}]
}]
});
I need coveredBlocks aggregations on every level:
*Module: {moduleBlocksCovered}, // SUM(blocksCovered) GROUP BY module, source
**Namespaces: [{nsBlocksCovered}] // SUM(blocksCovered) GROUP BY module, source, ns
****Types: [{typeBlocksCovered}] // SUM(blocksCovered) BY module, source, ns, type
How do I get this result with Coverage.aggregate in Mongoose ?
{
module: 'module1',
source: 'source1',
coveredBlocks: 7, // SUM of all functions in module
namespaces:[
name: 'ns1',
nsBlocksCovered: 7, // SUM of all functions in namespace
types:[
{
name: 'type1',
typeBlocksCovered: 7, // SUM(3, 4) of all function in type
functions[
{name: 'func1', blocksCovered: 3},
{name:'func2', blocksCovered: 4}]
}
]
]
}
My ideas is to deconstruct everything using $unwind then reconstruct the document back again using group and projection.
aggregate flow:
//deconstruct functions
unwind(namesapces)
unwind(namespaces.types)
unwind(namespace.types.functions)
//cal typeBlocksCovered
group module&source ,ns,type to sum functions blocksCovered->typeBlocksCovered + push functions back to types
project to transform fields to be easier for next group
// cal nsBlocksCovered
group module&source ,ns to sum typeBlocksCovered -> nsBlocksCovered) + push types back to ns
project to transform fields to be easier for next group
// cal coveredBlocks
group module&source to sum nsBlocksCovered -> coveredBlocks
project to transform fields to match your mongoose docs
My sample query with mongo shell syntax and its seem working , guess is you are using collection name "Coverage"
db.Coverage.aggregate([
{"$unwind":("$namespaces")}
,{"$unwind":("$namespaces.types")}
,{"$unwind":("$namespaces.types.functions")}
,{"$group": {
_id: {module:"$module", source:"$source", nsName: "$namespaces.name", typeName : "$namespaces.types.name"}
, typeBlocksCovered : { $sum : "$namespaces.types.functions.blocksCovered"}
, functions:{ "$push": "$namespaces.types.functions"}}}
,{"$project" :{module:"$_id.module", source:"$_id.source"
,namespaces:{
name:"$_id.nsName"
,types : { name: "$_id.typeName",typeBlocksCovered : "$typeBlocksCovered" ,functions: "$functions"}
}
,_id:0}}
,{"$group": {
_id: {module:"$module", source:"$source", nsName: "$namespaces.name"}
, nsBlocksCovered : { $sum : "$namespaces.types.typeBlocksCovered"}
, types:{ "$push": "$namespaces.types"}}}
,{"$project" :{module:"$_id.module", source:"$_id.source"
,namespaces:{
name:"$_id.nsName"
,nsBlocksCovered:"$nsBlocksCovered"
,types : "$types"
}
,_id:0}}
,{"$group": {
_id: {module:"$module", source:"$source"}
, coveredBlocks : { $sum : "$namespaces.nsBlocksCovered"}
, namespaces:{ "$push": "$namespaces"}}}
,{"$project" :{module:"$_id.module", source:"$_id.source", coveredBlocks : "$coveredBlocks", namespaces: "$namespaces",_id:0}}
])

MongoDB query using $near not working when nested

I have several documents in my Articles collection. Every document has a location value and some extra data. The location value looks like this:
"loc" : {
"type" : "Point",
"coordinates" : [
4,
54
]
}
I can build an index by executing the following command:
db.articles.ensureIndex({loc:"2dsphere"});
And I can query documents based on their location and a $maxDistance with the following query:
db.articles.find({ loc : { $near : {$geometry : {type : "Point" , coordinates : [4, 54] }, $maxDistance : 1000 } } });
This works perfectly!
However when I change the location of my "loc" object in my document, my query always returns zero results. My document should look like this (This is a minimized version):
{
"articledata" {
"content": {
"contact": {
"loc" : {
"type" : "Point",
"coordinates" : [
4.1,
54
]
}
}
}
}
}
When I rebuild my index query:
db.articles.ensureIndex({"articledata.content.contact.loc":"2dsphere"});
and execute my query again after changing my 'loc' location in the document:
db.articles.find({ "articledata.content.contact.loc" : { $near : {$geometry : {type : "Point" , coordinates : [4, 54] }, $maxDistance : 10000 } } });
There are no results.
It's probably some stupid mistake but I really can't find the problem...
Is there anyone who can help me out?
Thanks in advance!

MongoDB geospatial index, how to use it with array elements?

I would like to get Kevin pub spots near a given position. Here is the userSpots collection :
{ user:'Kevin',
spots:[
{ name:'a',
type:'pub',
location:[x,y]
},
{ name:'b',
type:'gym',
location:[v,w]
}
]
},
{ user:'Layla',
spots:[
...
]
}
Here is what I tried :
db.userSpots.findOne(
{ user: 'Kevin',
spots: {
$elemMatch: {
location:{ $nearSphere: [lng,lat], $maxDistance: d},
type: 'pub'
}
}
},
},
function(err){...}
)
I get a strange error. Mongo tells me there is no index :2d in the location field. But when I check with db.userSpots.getIndexes(), the 2d index is there. Why doesn't mongodb see the index ? Is there something I am doing wrong ?
MongoError: can't find special index: 2d for : { spots: { $elemMatch: { type:'pub',location:{ $nearSphere: [lng,lat], $maxDistance: d}}}, user:'Kevin'}
db.userSpots.getIndexes() output :
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydb.userSpots",
"name" : "_id_"
},
"1" : {
"v" : 1,
"key" : {
"spots.location" : "2d"
},
"ns" : "mydb.usersBoxes",
"name" : "spots.location_2d",
"background" : true,
"safe" : null
}
}
For a similar geospatial app, I transformed the location into GeoJSON:
{
"_id" : ObjectId("5252cbdd9520b8b18ee4b1c3"),
"name" : "Seattle - Downtown",
"location" : {
"type" : "Point",
"coordinates" : [
-122.33145,
47.60789
]
}
}
(the coordinates are in longitude / latitude format. Mongo's use of GeoJSON is described here.).
The index is created using:
db.userSpots.ensureIndex({"location": "2dsphere"})
In my aggregation pipeline, I find matches using:
{"$match":{"location":{"$geoWithin": {"$centerSphere":[[location.coordinates[0], location.coordinates[1]], radius/3959]}}}}
(where radius is measured in miles - the magic number is used to convert to radians).
To index documents containing array of geo data MongoDB uses multi-key index. Multi-key index unwinds document to some documents with single value instead of array before indexing. So the index consider that key field as single value field not array.
Try query it without $elemMatch operator.

Mongoose embedded document query returning null

I have the following schema :
_schema : {
Prize : new Schema({
prizeName : { type : String },
thumbnailImage : [ String ],
detailImage : [ String ],
prizeCategory : [ {type : String, index : true } ],
prizeDescription : { type : String },
prizePrice : { type : Number, required : true }
}),
Game : new Schema ({
roomName : { type : String, required : true },
openTime : { type : Date },
closeTime : { type : Date },
minPlayers : { type : Number },
maxPlayers : { type : Number, required : true },
numberOfPlayers : { type : Number },
winner : { userId : { type : ObjectId, index : true, ref : 'User'} },
prize : [ this.Prize ],
tag : [ { type : String, index : true } ],
status : { type : Number, index : true },
businessType : { type : Number, required : true, index : true },
mallId : { type : ObjectId, ref : 'Mall' },
registeredPlayers : { type : ObjectId, ref : 'User' }
}),
Schedule : new Schema ({
_id : ObjectId,
time : { type : Date, index : true },
game : [ this.Game ]
}),
}
However when I try to query the game embedded document the object is always null. I'm querying like so:
var Schedule = mongoose.model('Schedule', this._schema.Schedule);
Schedule.findById({'game._id' : req.params._id}).exec(function(err,gameDetail){...});
Am I doing anything wrong when I declare the schema and models? I have seen numerous examples where people appear to be doing exactly what I'm trying. Any help would be greatly appreciated! Thanks in advance.
A mongoose Model's findById method is used to find the instance of that Model with the _id that's supplied as the first parameter to the method. So Schedule.findById returns Schedule instances, not individual Game instances. Schedule.findOne({'game._id' : req.params._id}, ... will get you the Schedule instance containing the Game with that id, but if you need to query for Game instances by id, you should be keeping them in a separate collection instead of embedding them in Schedule.
Looking at your code, my first guess is actually that your findById isn't structured quite right.
First, you're using the value req.params._id to get the id. Most of the code examples I have seen, and my code, uses :id in the router (app.get('/schedules/:id')), which would actually mean the ID you're looking for is stored in req.params.id. You'll have to check that in your code.
Secondly, to my understanding, findById is only useful for finding, in this case, a Schedule by that ID. Try using just a normal find.
Last thought: you're missing a closing curly bracket at the end of {'game._id' : req.params._id}.
Hopefully something in that helps. :)

Resources