Using MongoDB v2.6.5
When I attempt to save a geojson document into the db I receive the following error:
name: 'MongoError',
code: 16755,
err: 'insertDocument :: caused by :: 16755 Can\'t extract geo keys from object, malformed geometry?: { _id: ObjectId(\'55271b90d075728028d4c9e1\'), ..., location: [ { _id: ObjectId(\'55271b90d075728028d4c9e3\'), loc: { type: "Point", coordinates: [ -105.01621, 39.57422 ] } } ] } ], status: [ "lead" ], created: new Date(1428626320406), lastName: "Doe", firstName: "John", __v: 0 }' }
I'm attempting to insert a Point into the table with a 2dsphere index, all managed through MongooseJS as shown below.
var GeoAddressSchema = new Schema({
// Only holds points.
loc: {
type: { type: String },
coordinates: []
}
});
var Lead = new Schema({
// Other fields ...
location: [GeoAddressSchema],
// ...
});
LeadAddressSchema.index({ location: '2dsphere' });
The geojson that is being saved:
{ type: "Point", coordinates: [ -111.855211, 33.58513 ] }
The geojson is valid according to: http://geojsonlint.com/ if I wrap the fields in quotes, but I should not need to do that (and cannot for Mongo afaik).
Anyone have any idea why this would fail? It looks correct.
Reference links
MongoDB GeoJSON: http://docs.mongodb.org/manual/reference/geojson/
MongoDB 2dSphere: http://docs.mongodb.org/manual/core/2dsphere/
First observation: you need not introduce the loc path in the location structure.
Further, you need not even define a separate schema for your location field in the Lead schema.
Try this:
var Lead = new Schema({
// Other fields ...
location: {
type: {
type: 'String',
default: 'Point' // and optionally skip including `type: String` everywhere
},
coordinates: {
type: [Number]
}
},
// More fields ...
});
LeadAddressSchema.index({ location: '2dsphere' });
Another problem I faced was that the 2dsphere index gets messed up, when figuring and testing out solutions. So try dropping the index, or even better the collection after you make structural changes to the schema.
> db.lead.drop(); // on the mongo console`
Related
I have recently started working on GeoJson in Mongodb.
I have collection named Restaurants which stores the coordinates of the restaurants in GeoJson POINT object.
Is there a mongodb way of finding the centroid of the collection?
Schema of the Restaurants is as given below:
"use strict";
const mongoose = require("mongoose");
const restaurantSchema = new mongoose.Schema({
name: {
type: String,
},
geoCoordinates: {
type: {
type: String,
enum: ["Point"],
},
coordinates: {
type: [Number],
},
},
createdTimestamp: {
type: Date,
default: Date.now,
},
lastUpdatedTimestamp: {
type: Date,
default: Date.now,
},
});
const Restaurants = mongoose.model("Restaurants", restaurantSchema, "Restaurants");
module.exports = {
Restaurants,
};
Thank you,
KK
One option is to $group and calculate the $avg:
db.collection.aggregate([
{$group: {
_id: 0,
lon: {$push: {$arrayElemAt: ["$location.coordinates", 0]}},
lat: {$push: {$arrayElemAt: ["$location.coordinates", 1]}}
}},
{$project: {
_id: 0,
centroid: [{$avg: "$lon"}, {$avg: "$lat"}]
}}
])
See how it works on the playground example
I have the following Schema:
const PublicationSchema = mongoose.Schema({
title: {
type: String,
required: true
},
files:[{
contentType: String,
data: Buffer,
name: String
}]
})
What I'm trying to do is to get the file with the same index as the query.For example I have this object:
_id: new ObjectId("637f20ce6ce5c48d9788a1ff"),
title: 'TEST',
files: [
{
contentType: 'application/pdf',
name: 'imId1',
_id: new ObjectId("id1")
},
{
contentType: 'application/pdf',
name: 'imId2',
_id: new ObjectId("id2")
}
]
where if I query id2 it only retrieves:
{
contentType: 'application/pdf',
name: 'imId2',
_id: new ObjectId("id2")
}
What I was trying to use was const onePublication = await Publication.findOne({ "files._id": req.body.fileId},{}) but this retrieves every field.
I was going to just tell it to not retrieve the other field using field:0 but I realized that this will still retrieve the files in other indexes of the field.
Is there a way to tell it to only retrieve the one with the same index or should I be using another query entirely?
One of the options is to $unwind the array first. $match by your criteria. Then, $replaceRoot to get your array entry.
db.collection.aggregate([
{
"$unwind": "$files"
},
{
$match: {
"files._id": "id2"
}
},
{
"$replaceRoot": {
"newRoot": "$files"
}
}
])
Mongo Playground
Consider changing your schema to store files as an individual collection, if most of the time you are going to access the array objects only.
I have to do an update on the following Mongoose Model.
var room_schema = new Schema({
title: { type: String, required: true },
questions: [{ //array of reflections
q_index: {type: Number},
q_text: { type: String},
responses: [{ //array of
student: { type: Schema.Types.ObjectId, ref: 'User' },
response: { type: String }
}]
}]
});
module.exports = mongoose.model('Room', room_schema);
Required values are in an object as
x = {
room: ObjectId("586a0aa0232a3918c8b7f5c9"),
student: ObjectId("5863918c85c9ba0aa0232a7f"),
question: 0,
msg: "Some Message"
}
Now, i want to update the room. I tried doing something like this
Room.update(
{_id:x.room,
'questions.q_index':x.question,
'questions.responses.student':x.student},
{$set:{
'responses.$.student.response' : x.msg
}},function(err, data){
if(err){throw err}
console.log(data);
}
);
The msg which is being returned is { ok: 0, n: 0, nModified: 0 } and needless to say the update is not happening.
Also, there is a possibility that the Room may not have a response array in it. And i expect that, if that is the case, then the array should be created and then updated.
Please give me some guidance.
Sry, but I can't write a comment since I don't have enough of rep, so I'm forced to write my notice as an answer.
I notice something in your first query when you search for a room by it's _id. Try to put _id in quotes like this "_id", because it should be like this relating to this post
stackoverflow: updating-an-array-inside-a-mongoose-model
The idea is to push an object that looks like this into a field called likes, which is an array:
{
movieId: "VgtyvjVUAjf8ya",
information: {
genre: "Action",
length: "160",
language: "English"
}
}
I thought this would do it:
Meteor.users.update({_id: Meteor.userId()}, {$push: {likes: {movieId: movieId, information: informationObj}}})
But either it is wrong or the validation by SimpleSchema has some issues (it doesn't complain, though) because all I get is an empty object in an array! And no, there's nothing wrong with the values themselves, I have checked.
The SimpleSchema for the field in question looks like this:
likes: {
type: [Object],
optional: true
}
I've tried reading through the documentation but I don't really understand what's wrong. Anyone knows?
If you don't care to validate the objects that get pushed into the likes property, you can set blackbox to true in your schema, like so:
likes: {
type: [Object],
optional: true,
blackbox: true
}
This will allow you to put whatever you want into a "like" object.
If you do want to validate the "like" objects, then you'll need to create some additional schemas, like so:
var likeInfoSchema = new SimpleSchema({
genre: {
type: String
},
length: {
type: String
},
language: {
type: String
}
});
var likeSchema = new SimpleSchema({
movieId: {
type: String
},
information: {
type: likeInfoSchema
}
});
Meteor.users.attachSchema(new SimpleSchema({
// ...
likes: {
type: [likeSchema]
}
}));
I am attempting to run a Geo Box query using Mongoose however do not get any results. I have built out a simplified test case:
var mongoose = require('mongoose');
// Schema definition
var locationSchema = mongoose.Schema({
userid: { type : [Number], required: true},
loc: {
'type': {
type: String,
required: true,
enum: ['Point', 'LineString', 'Polygon'],
default: 'Point'
},
coordinates: []
},
tags: { type : [String], index: true, required: true },
create_date : {type: Date, "default": Date.now()}
});
locationSchema.index({ 'loc.coordinates': '2dsphere' });
var Location = mongoose.model('Location', locationSchema);
mongoose.connect('mongodb://localhost/test');
var chkin = new Location({
userid: 1,
loc: { type: 'Point', coordinates: [-122.424088, 37.529876] },
tags:"foo"
});
chkin.save(function (err, locations) {
console.log('SAVE err: ' + err)
console.log('SAVE locations: ' + locations)
console.log('');
var query = Location.find(
{ "coordinates" :
{ "$geoWithin" :
// <bottom left coordinates> - <upper right coordinates> - long,lat
{ "$box" :[[-122.610168,37.598167], [-122.288818,37.845833]] }
}
}, function (err, docs) {
console.log('FIND err: '+ err)
console.log('FIND locations: '+docs)
});
});
Console output:
SAVE err: null
SAVE locations: { __v: 0,
_id: 53cc7a3ea44a9bc70634fdc6,
create_date: Sun Jul 20 2014 19:26:06 GMT-0700 (PDT),
tags: [ 'foo' ],
loc: { coordinates: [ -122.424088, 37.529876 ], type: 'Point' },
userid: [ 1 ] }
FIND err: null
FIND locations:
Can anyone advise where my error is?
There are several problems in what you are coding up here. Best to look at a whole working listing and break that down:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var locationSchema = new Schema({
userid: { type: Number, required: true },
loc: {
type: {
type: "String",
required: true,
enum: ['Point', 'LineString', 'Polygon'],
default: 'Point'
},
coordinates: [Number]
},
tags: [{ type: String, index: true, required: true }],
create_date: { type: Date, default: Date.now }
});
locationSchema.index({ 'loc': '2dsphere' });
var Location = mongoose.model( 'Location', locationSchema );
mongoose.connect( 'mongodb://localhost/geotest' );
async.series(
[
function(callback) {
Location.remove(function(err) {
if (err) throw err;
callback();
});
},
function(callback) {
var loc = new Location({
userid: 1,
loc: {
type: 'Point',
coordinates: [ -122.4481201171875, 37.71370177998719 ]
},
tags: "foo"
});
loc.save(function(err,loc) {
if (err) throw err;
console.log( "Location: %s", JSON.stringify( loc, undefined, 4 ) );
callback();
});
},
function(callback) {
var query = Location.find({
"loc": {
"$geoWithin": {
"$geometry": {
"type": "Polygon",
"coordinates": [[
[ -122.610168, 37.598167 ],
[ -122.288818, 37.598167 ],
[ -122.288818, 37.845833 ],
[ -122.610168, 37.845833 ],
[ -122.610168, 37.598167 ]
]]
}
}
}
});
query.exec(function(err,docs) {
if (err) throw err;
console.log( "Docs: %s", JSON.stringify( docs, undefined, 4 ) );
callback();
});
}
]
);
Not directly related but seemingly that you did not mean to do is how you defined some elements in your schema like this:
userid: { type: [Number], required: true },
That defines the field as an "Array", and it would seem you really don't mean to in this case. If you did then it really makes more sense to write like this:
userid: [{ type: Number, required: true }],
But for the sake of this listing it is just shown as a plain field. The "tags" field makes sense as an array and has been defined that way.
The next thing to look at is the index definition. This may have been a result of trying things that did not work, but generally if you are defining a GeoJSON structure under "loc" in your documents, then that is the field you need to index on. Which possibly is related to the next part.
locationSchema.index({ 'loc': '2dsphere' });
When you are querying on GeoJSON formatted objects, helper operator such as $box cannot be used:
..."The $box operator returns documents based on grid coordinates and does not query for GeoJSON shapes."
So when you are actually using GeoJSON then you must specify as a "Polygon" or "MultiPolygon" type of bounds in order to match GeoJSON objects within those bounds. At any rate, the field supplied to $geoWithin has to be the field that holds the index. You where supplying "coordinates" which was not a valid top level field. Depending on the index field you are on "loc" or "loc.coordinates":
var query = Location.find({
"loc": {
"$geoWithin": {
"$geometry": {
"type": "Polygon",
"coordinates": [[
[ -122.610168, 37.598167 ],
[ -122.288818, 37.598167 ],
[ -122.288818, 37.845833 ],
[ -122.610168, 37.845833 ],
[ -122.610168, 37.598167 ]
]]
}
}
}
});
Finally, the "Point" you were looking for lies outside of the "box" you were specifying.
Here is the "box" you were searching with and the "Point" shown outside of the "box":
The listing shown above corrects all of these things and supplies a "Point" and "Polygon" forms with will actually meet the conditions. Try working with sites such as gejsonlint.com and others to check the validity of the data you are working with.