Geo spatial queries with Doctrine MongoDB ODM - mongodb

I have a 2d index on a coordinates property of my document. Using the mongo shell, I can query the collection like this;
db.adverts.find({coordinates:{$near:[20,40]}})
And that returns the following results, as expected;
{ "_id" : ObjectId("4fddac51352de93903000000"), "title" : "dummy #3", "coordinates" : { "longitude" : 22, "latitude" : 31 } }
{ "_id" : ObjectId("4fddac48352de95105000000"), "title" : "dummy #3", "coordinates" : { "longitude" : 20, "latitude" : 30 } }
{ "_id" : ObjectId("4fddaca4352de93703000000"), "title" : "dummy #3", "coordinates" : { "longitude" : 31, "latitude" : 22 } }
{ "_id" : ObjectId("4fdda6a2352de90a03000000"), "title" : "dummy title", "created" : ISODate("2012-06-17T09:42:58Z"), "coordinates" : { "longitude" : 54.1234, "latitude" : -1.234 } }
{ "_id" : ObjectId("4fdda6d8352de9c004000000"), "title" : "dummy title #2", "created" : ISODate("2012-06-17T09:43:52Z"), "coordinates" : { "longitude" : 54.34, "latitude" : -1.124 } }
However, using Doctrine as per the documentation to query the exact same collection, I get no results e.g.
$adverts = $dm->createQueryBuilder('Advert')
->field('coordinates')->near(20, 40)
->getQuery()
->execute();
$adverts->count(); // => 0
My advert yaml looks like this;
Advert:
type: document
collection: adverts
fields:
id:
id: true
title:
type: string
content:
type: string
created:
type: date
updated:
type: date
status:
type: int
distance:
type: int
indexes:
coordinates:
keys:
coordinates: 2d
referenceOne:
owner:
targetDocument: User
embedOne:
coordinates:
targetDocument: Coordinates
And the Coordinates document is like this;
Coordinates:
type: embeddedDocument
fields:
longitude:
type: float
latitude:
type: float
Any ideas why using Doctrine's ODM would return zero results on the same query?
UPDATE #1
It looks that there is a problem with Doctrine\MongoDB\Query\Builder::near() L363. The method parameter ignores the second value ($y). So only the first value is being passed to be executed.

There appears to be an implementation issue with the near() method (see https://github.com/doctrine/mongodb/pull/53). To fix my original query, I would need to do the following;
$adverts = $dm->createQueryBuilder('Advert')
->field('coordinates.latitude')->near(20)
->field('coordinates.longitude')->near(40);
$adverts->getQuery()->count(); // => 5
This contradicts the current documentation that implies both x, y coordinates can be passed to Doctrine\MongoDB\Query\Builder::near().
EDIT
To make life easier, I've created a custom repository class to provide a more intuitive solution for this inconsistency;
public function near($longitude, $latitude)
{
$query = $this->createQueryBuilder()
->field('coordinates.longitude')->near((float) $longitude)
->field('coordinates.latitude')->near((float) $latitude)
->getQuery();
return $query;
}

Related

How to query date from mongodb using graphql?

I am trying to find documents by DateTime.
I have the following sample of mongo document:
{
"_id" : ObjectId("5df8ea55ac02282c15917e46"),
"title" : "Bán gấp căn 01 DT 62m2 căn góc, giá 1,05 tỷ. LH C Nga 0979441985",
"address" : "La Khê - Hà Đông",
"avatar" : "https://file4.batdongsan.com.vn/crop/200x200/2019/12/07/20191207173723-31f3_wm.jpg",
"price" : "1.05 Tỷ",
"lat" : 20.9719924926758,
"lon" : 105.756340026855,
"id" : 15504225,
"area" : "62 m²",
"cat" : "Bán căn hộ chung cư",
"date" : "17/12/2019",
"room" : 2,
"DateTime" : ISODate("2019-12-17T22:00:36.136+08:00")
}
I have defined a schema:
const sellType = new GraphQLObjectType({
name:'Sell',
fields: () => ({
_id:{type:GraphQLString},
id:{type:GraphQLString},
title:{type:GraphQLString},
address:{type:GraphQLString},
avatar:{type:GraphQLString},
price:{type:GraphQLString},
DateTime:{type:GraphQLDateTime}
})
})
I have also defined the root query:
const RootQuery = new GraphQLObjectType({
name:'RootQueryType',
fields: {
property_for_sell_by_datetime:{
type: sellType,
args:{DateTime:{type:GraphQLDateTime}},
resolve(parent, args){
return sell.find({DateTime:new Date(args.DateTime)})
}
}
}
})
But I don't know why I cannot find any document using:
{
property_for_sell_by_datetime(DateTime:"2019-12-17T14:00:36.136Z"){
_id
DateTime
}
}
Edit:
I tired to use findOne. It works. But find is not working
Edit2:
I need to define type as GraphQLList
to use find instead of findOne, I need to define the type as GraphQLList

How to search nearest place in array object in mongodb

the mongodb document 'contents' is
{
"_id" : ObjectId("57bd1ff410ea3c38386b9194"),
"name" : "4Fingers",
"locations" : [
{
"id" : "locations1",
"address" : "68 Orchard Rd, #B1-07 Plaza Singapura, Plaza Singapura, Singapura 238839",
"phone" : "+65 6338 0631",
"openhours" : "Sunday-Thursday: 11am - 10pm \nFriday/Saturday/Eve of PH*: 11am - 11pm",
"loc" : [
"1.300626",
"103.845061"
]
}
],
"comments" : [ ],
"modified" : 1472271793525,
"created" : 1472012276724,
"createdby" : "Admin",
"modifiedby" : "Admin",
"createdipaddress" : "localhost",
"modifiedipaddress" : null,
"types" : "Restaurant",
"category" : "FoodAndBeverages",
"logo" : "logo4Fingers.png",
"tags" : "western, chicken, restaurant, food, beverages"
}
I want to find the nearest place to my location that i get from HTML5 navigation. How do i query it? Data should be sorted in near to far order.
Thank you.
To query mongodb geospatial data first you need a geo spatial index on your location field.
Your location field is a string, it needs to be a numeric type, you need to update your data accordingly.
Create your index on numerical location data:
db.collection.createIndex( { "locations.loc" : "2d" });
Query:
var projection = {"locations.loc":1};
var query = {"locations.loc": {"$near":[1.300626, 103.845061], "$maxDistance": 0.5}};
db.collection.find(query, projection).pretty();
//result:
{
"_id" : ObjectId("57bd1ff410ea3c38386b9194"),
"locations" : [
{
"loc" : [
1.300626,
103.845061
]
}
]
}
var query2 = {"locations.loc": {"$near":[2.300626, 103.845061], "$maxDistance": 0.5}};
db.collection.find(query2, projection).pretty();
//result:
{}
The query result will be sorted always, with nearest as first document.
Thanks Sergiu Zaharie.
It's working.
Then how can i returned all the field instead of returning this field only.
{
"_id" : ObjectId("57bd1ff410ea3c38386b9194"),
"locations" : [
{
"loc" : [
103.845061,
1.300626
]
}
]
}
//edit
Solved.
i just clear the projection then it work like charm.
Thank you.

Mongoose Mongo 2dsphere geoWithin

After reading many questions that are SO close to mine, and reading the MongoDB docs and Mongoose docs, I still cannot answer my question.
Using express 4.13.4, mongoose 4.4.10, mongodb 2.1.14 on Node 4.4.0
My Mongoose Location schema:
var schema = new Schema({
type: {type: String},
coordinates: []
},{_id:false});
var model = mongoose.model('LocationModel',schema);
module.exports = {
model : model,
schema : schema
};
My CatalogModel schema (what I write to Mongo):
var locationSchema = require('./locationModel').schema;
var schema = new Schema({
title : String,
format: {type: String, maxlength: 4},
location: {type: locationSchema, required:true},
otherStuff: String
});
schema.index({location: '2dsphere'}); // Ensures 2dsphere index for location
model = mongoose.model('CatalogModel',schema);
I create a concrete example and write to MongoDB (this works fine... in that I can query it in Mongo)
var polyEntry = new CatalogModel({
title:"I am just a Polygon",
otherStuff: "More stuff here",
location:{
type:'Polygon',
coordinates:[[[0,1],[0,2],[1,2],[0,1]]]
}
});
In Mongo, I asked the collection for the indexes:
db.catalogmodels.getIndexes()
And this is what it says (not entirely sure what this means)
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.catalogmodels"
},
{
"v" : 1,
"key" : {
"location" : "2dsphere"
},
"name" : "location_2dsphere",
"ns" : "test.catalogmodels",
"background" : true,
"2dsphereIndexVersion" : 3
}
]
I can do a db.catalogmodels.find() and get my document back.
{
"_id" : ObjectId("12345678901234566778"),
"title" : "I am just a Polygon",
"location" : {
"type" : "Polygon",
"coordinates" : [ [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 0, 1 ] ] ]
},
"__v" : 0
}
I can even do a $geoWithin call in Mongo:
db.catalogmodels.find(
{
location:{
$geoWithin:{
$geometry:{
type:"Polygon",
"coordinates":[[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
}
}
}
})
But here's the actual question:
Mongoose keeps telling me [Error: Can't use $geoWithin]
var geoJson = {
"type" : "Polygon",
"coordinates" : [[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
};
CatalogModel
.find()
.where('location').within(geoJson)
.exec(function(err,data){
if ( err ) { console.log(err); }
else {console.log("Data: " + data);}
db.close()
});
I also replaced the .find().where().within() call to:
CatalogEntryModel.find({
location:{
$geoWithin:{
$geometry:{
type:"Polygon",
"coordinates":[[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
}
}
}
})
.exec(function(err,data){
if ( err ) { console.log(err); }
else {console.log("Data: " + data);}
db.close();
});
Is there a reason Mongoose does not like the $geoWithin call? The latest API says this should work.
I wrote this up as an issue on Mongoose: https://github.com/Automattic/mongoose/issues/4044#
And it has been closed.

Meteor Highcharts using collection data

I'm trying to display a piechart using the Highchart package for Meteor.
I've got it working with a static data source, like this:
series: [{
type: 'pie',
name: 'Sales',
data:
[
["Norway", 123123.32],
["Canada", 1977844.86],
["France", 2644017.71],
["Denmark", 28954612.34],
["United Kingdom", 3391712.21],
["United States", 9389789.51]
]
}]
in the series part of the .js of the template file.
But when I'm trying to use MongoDb's find statement it either shows no data or the report is showing up weirdly, I guess this is due to the formatting of the data resulted from the find query.
My data in the collection is looking something like this:
{ "_id" : "HsgEBrrSvBp6qzp8Q", "0" : "Norway", "1" : 9061000.58 }
{ "_id" : "5dvtddEogj6d5Zw7D", "0" : "Canada", "1" : 1977844.86 }
{ "_id" : "6XmfgjBG4dupes3ma", "0" : "France", "1" : 2644017.71 }
{ "_id" : "BB8Av8GRpPXPsWfzj", "0" : "Denmark", "1" : 2894312.34 }
{ "_id" : "44qbHdtA3wTtf9QFL", "0" : "United Kingdom", "1" : 3391712.21 }
{ "_id" : "YqyWGzXkT4pD532qJ", "0" : "United States", "1" : 9389789.51 }
I've also tried to only find the selected fields ("0" and "1")
SalesData.find({}, {_id:0}).fetch()
Anyone got any tips on how to get the data from a collection in a format that can be used in Highcharts?!
Thanks!
EDIT: I solved it by pushing the required values to an array and set that array as data source.
var seriesData = [];
var reportData = SalesData.find({});
reportData.forEach(function(countryData) {
var dataPoint = [countryData.Country, countryData.Total];
seriesData.push(dataPoint);
console.log(countryData.Country);
});
Actually you can convert the result from a find query directly into an array of values by using cursor.map.
Here is an example for your scenario (using ecmascript2015)
SalesData.find({}, {fields: {Country: 1, Total: 1}}).fetch().map((countryData) =>
{
{return [countryData.Country, countryData.Total]}
})
You should be able to get the data source format needed by highchart
db result is a collection of objects (curly braces on each row). While the static data is an array of values (rectangular braces). So, you need to convert each row in db into array of values.
here is the mapping code:
var ds = [
{ "_id" : "HsgEBrrSvBp6qzp8Q", "0" : "Norway", "1" : 9061000.58 },
{ "_id" : "5dvtddEogj6d5Zw7D", "0" : "Canada", "1" : 1977844.86 },
{ "_id" : "6XmfgjBG4dupes3ma", "0" : "France", "1" : 2644017.71 },
{ "_id" : "BB8Av8GRpPXPsWfzj", "0" : "Denmark", "1" : 2894312.34 },
{ "_id" : "44qbHdtA3wTtf9QFL", "0" : "United Kingdom", "1" : 3391712.21 },
{ "_id" : "YqyWGzXkT4pD532qJ", "0" : "United States", "1" : 9389789.51 }
];
var asValueArray = ds.map(function(row){
return [row["0"],row["1"]];
});
Then, you just assign it to your series object:
series: [{
type: 'pie',
name: 'Sales',
data: asValueArray
}]

how to change mongodb string to geoJSON point throughout the collection

I have a Mongodb collection parking_info and this is one document in the collection:
{
"_id" : ObjectId("559c152fa439a961c357f931"),
"POST_ID" : "354-20160",
"MS_ID" : "-",
"MS_SPACEID" : 0,
"CAP_COLOR" : "Grey",
"METER_TYPE" : "SS",
"SMART_METE" : "Y",
"ACTIVESENS" : "N",
"JURISDICTI" : "SFMTA",
"ON_OFF_STR" : "ON",
"OSP_ID" : 0,
"STREET_NUM" : 2016,
"STREETNAME" : "CHESTNUT ST",
"STREET_SEG" : 3.977e+006,
"RATEAREA" : "Area 5",
"SFPARKAREA" : "Marina",
"LOCATION" : "(37.8007983983, -122.4368696024)"
}
I need to convert the last field "LOCATION" : "(37.8007983983, -122.4368696024)"
here the order is latitude , then longitude.
to
"LOCATION" : { type: "Point", coordinates: [ -122.4368696024, 37.8007983983]},
The coordinate order is longitude, then latitude.
How can I apply that to all documents in the collection?
You can use the cursor method forEach() returned from the find() method to iterate the cursor, access the documents, and with each document modify the LOCATION field by using native JavaScript methods as in the following example:
db.parking_info.find({"LOCATION": {"$type": 2}}).forEach(function(doc){
var obj = {},
loc = [],
temp = doc.LOCATION.replace("(", "").replace(")", "");
loc = temp.split(",").map(function(point){
return parseFloat(point);
});
obj["type"] = "Point";
obj["coordinates"] = loc;
doc.LOCATION = obj;
db.parking_info.save(doc);
});