My schema looks something like this for mongoose:
var LocationSchema = new Schema({
id: ObjectId,
geo: {
type: {
type: String,
required: true,
enum: ['Point', 'LineString', 'Polygon'],
default: 'Point'
},
coordinates: [{
type: Number,
es_lat_lon: true,
es_type: 'geo_point'
}]
}
});
Then I add mongoosastic plugin to mongoose initiate the model and create the mappings for mongoosastic
var esClient = new elasticsearch.Client({
host: config.es_url,
requestTimeout: Infinity,
keepAlive: true
});
LocationSchema.plugin(mongoosastic, { esClient: esClient })
var Location = mongoose.model('Location', LocationSchema);
/**
* mongoosastic create mappings
*/
Location.createMapping(function(err, mapping) {
if (err) {
console.log('error creating mapping (you can safely ignore this)');
console.log(err);
} else {
console.log('mapping created!');
console.log(mapping);
}
});
Now I get this error [Error: MapperParsingException[No handler for type [{default=Point, enum=[Point, LineString, Polygon], required=true, type=string}] declared on field [geo]]]
The full error in the log:
{
[Error: MapperParsingException[No handler for type [{default=Point, enum=[Point, LineString, Polygon], required=true, type=string}] declared on field [geo]]]
status: '400',
displayName: 'BadRequest',
message: 'MapperParsingException[No handler for type [{default=Point, enum=[Point, LineString, Polygon], required=true, type=string}] declared on field [geo]]'
}
My question is am I doing this completely wrong or am I just missing something small? The way I am doing this works for mongoose, only mongoosastic is having trouble and I understand why, but I can't be the first to run into this (reason why mongoosastic is having trouble is it sees type and does not expect it to have a type - at least that is what I think it is having trouble with).
Instead of
coordinates: [{
type: Number,
es_lat_lon: true,
es_type: 'geo_point'
}]
Try
coordinates: {
type: [Number],
es_type: 'geo_point'
}
EDIT:
OP Posted this solution.
Changed:
geo: {
type: {
type: String, ......
},
coordinates: [
{ type: Number, ..... }
]
}
To
geo: {
point_type: {
type: String, ......
},
coordinates: [
{ type: Number, ..... }
]
}
Related
I have 4 nested documents as follow:
//Nested sub document subControl
const SubControlSchema = new Schema({
subControlNo: {
type: String
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
//Nested sub document control
const ControlSubSchema = new Schema({
mainControl: {
type: String
},
subControls: [SubControlSchema],
controlDescription: {
type: String,
trim: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
//Nested sub document domain
const DomainSubSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
domainNo: {
type: String,
trim: true
},
domainName: {
type: String,
trim: true
},
domainDescription: {
type: String,
trim: true
},
controls: [ControlSubSchema],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
// framework Schema
const FrameworkSchema = new Schema({
name: {
type: String,
trim: true
},
description: {
type: String,
trim: true
},
regulator: {
type: Schema.Types.ObjectId,
ref: 'Regulator',
default: null
},
client: {
type: Schema.Types.ObjectId,
ref: 'Client',
default: null
},
domains: [DomainSubSchema],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Framework', FrameworkSchema);
I'm trying to post a control under the domain which is inside the framework, here's what I have been trying to do:
//Add new control under a specific domain and framework
router.post('/add/:frameworkId/:domainId', auth, async (req, res) => {
try {
const control = req.body.controls; //take the request from the body
const query = { _id: req.params.frameworkId, _id: req.params.domainId };//pushing into the framework model by taking the ID from URL
await Framework.updateOne(query, { $push: { domains: control } }).exec(); //push the query into the framework model
res.status(200).json({
success: true,
controls: control
});
} catch (error) {
res.status(400).json({
// error: 'Your request could not be processed. Please try again.'
error
});
}
});
Data posted in postman:
Link: http://localhost:3000/api/framework/add/6233277f411377367f8ad1c0/6233277f411377367f8ad1c1
{
"controls":
{
"mainControl": "1-5",
"subControls": [{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}],
"controlDescription": "controlDescriptionTest"
}
}
Response:
{
"success": true,
"controls": {
"mainControl": "1-5",
"subControls": [
{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}
],
"controlDescription": "controlDescriptionTest"
}
}
Problem: I'm not getting any new data in mongodb , any idea if I'm approaching this the correct way? I'm guessing the data is posted correctly and It's a problem with saving it to the database
Picture of my schema: I want to be able to add elements under the controls:
First if you want your code to insert and not update you should use insertOne and not updateOne, regarding an "update" operation I can see 2 potential "issues" here:
req.params.frameworkId and req.params.domainId come as string type. And I assume the _id field is type ObjectId and not string.
To fix this you just need to cast it to the proper type, like so:
import { ObjectId } from 'mongodb';
...
{ _id: new ObjectId(req.params.frameworkId) }
Both parameters are "querying" the same field (_id), unless this is intentional somehow if these values are different it will never find a document to match, this should be changed.
Lastly if you want to update an existing object if exists, and if not insert then you should use updateOne with the upsert option:
await Framework.updateOne(query, { $push: { domains: control } }, { upsert: true }).exec();
I am sending the object to create a user model.
"{
type: 'Point',
coordinates: [ 25.2239771, 51.4993224 ]
}"
And, here is the Mongoose Schema that I created.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserProfileSchema = new Schema(
{
userId: {
type: String,
// required: true,
unique: true,
},
userFirstName: {
type: String,
// required: true,
},
userLastName: {
type: String,
// required: true,
},
userGender: {
type: String,
// required: true,
},
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
index: '2dsphere',
},
},
},
{ collection: 'userprofilemodels' }
);
module.exports = UserProfile = mongoose.model(
'userprofilemodels',
UserProfileSchema
);
This is the code that I am using to add geoJson type file. However, I am getting an error.
I also tried to add index after the Schema has been defined
await new userProfileModel({
userId,
userFirstName,
userLastName,
userCoordinates,
})
.save()
.then(() => {
console.log('it worked!');
res.send('worked!');
})
.catch((error) => {
console.log('did not worl')
// console.log(error);
});
If I exclude userCoordinates, then it works. So, def my geoJson object is wrong. However, I have no clue where I have made mistakes.
Mongoose supports GeoJSON objects indexing so first add the "2dsphere" index to the userCoordintes rather than to the coordinates within the object in order to make it work.
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
default: undefined,
required: true
},
index: '2dsphere'
},
Make sure your userCoordinates looks something like this:
const userCoordinates = {
type: "Point",
coordinates: [coordinatesA, coordinatesB],
};
Taken from the mongoose documentation it seems the GeoJSON Type mustn't be only a String.
here is the example with location as a GeoJSON type:
const citySchema = new mongoose.Schema({
name: String,
location: {
type: {
type: String, // Don't do `{ location: { type: String } }`
enum: ['Point'], // 'location.type' must be 'Point'
required: true
},
coordinates: {
type: [Number],
required: true
}
}
});
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'
}
This is /models/joke.js:
var mongoose = require ('mongoose')
, database = mongoose.connect('localhost', 'joke', { server: { poolSize: 3 } });
var jokeSchema = mongoose.Schema({
content: String,
upvotes: {
type: Number,
default: 0
},
downvotes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
published: {
type: Boolean,
default: true
},
author_id: Number,
created: {
type: Date,
default: Date.now
}
});
var Joke = mongoose.model('Joke', jokeSchema);
module.exports = Joke;
And I'm doing this to check if something exist — if doesn't, then create:
var Joke = require ('./models/joke');
// ...
Joke.findAndModify({
query: {
content: content
},
update: {
$setOnInsert: {
content: "test",
}
},
new: true,
upsert: true
});
But my console shout me the following:
TypeError: Object function model(doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId);
} has no method 'findAndModify'
I can understand the reason for the error – I'm calling it through a model instead of a collection, but how do I access my Jokes' collection methods?
I mean, all of the examples were using db.collection.findAndModify, but what is this db.collection? How do I call it?
To access findAndModify update functionality from Mongoose, use findOneAndUpdate:
Joke.findOneAndUpdate(
{ content: content },
{ $setOnInsert: { content: "test" } },
{ new: true, upsert: true },
callback
);
I have a mongoose schema and model defined as follows:
var mongoose = require('mongoose')
, Schema = new mongoose.Schema({
email: {
index: {
sparse: true,
unique: true
},
lowercase: true,
required: true,
trim: true,
type: String
},
location: {
index: '2dsphere',
type: [Number]
}
})
, User = module.exports = mongoose.model('User', Schema);
If I attempt:
var user = new User({ email: 'user#example.com' });
user.save(function(err) {
if (err) return done(err);
should.not.exist(err);
done();
});
I receive the error message:
MongoError: Can't extract geo keys from object, malformed geometry?:{}
Despite the location field in this schema not being required, it seems to be acting as such anyways. I have tried adding default: [0,0] which does circumvent this error, however it seems like a bit of a hack, as this is clearly not a good default, and ideally the schema would not require the user to have a location at all times.
Do geospatial indexes with MongoDB / mongoose imply that the field being indexed is required?
For mongoose 3.8.12, you set the default value:
var UserSchema = new Schema({
location: {
type: {
type: String,
enum: ['Point'],
default: 'Point',
},
coordinates: {
type: [Number],
default: [0, 0],
}
}
});
UserSchema.index({location: '2dsphere'});
By default, a property declared an array receives a default empty array to work with. MongoDB has started validating geojson fields and yells about empty arrays. The work around is to add a pre save hook to the schema that checks for this scenario and fixes up the document first.
schema.pre('save', function (next) {
if (this.isNew && Array.isArray(this.location) && 0 === this.location.length) {
this.location = undefined;
}
next();
})
The correct way to create a schema with geolocation and run geospartial queries was
var UserSchema = new Schema({
location: {
type: {
type: String,
enum: ["Point"], // 'location.type' must be 'Point'
},
coordinates: {
type: [Number]
}
}
});
UserSchema.index({location: '2dsphere'});
Be careful this is important to do, the index 2dsphere in the
location.coordinates, if you want to run geospartial queries!
The only way I have resolved this issue was changing the index type from
GEO:{
type: [Number],
index: '2dsphere'
}
to
GEO:{
type: [Number],
index: '2d'
}
It was a nightmare