In a GeoJSON Polygon (or more strictly: LinearRing), the last set of coordinates needs to evaluate to the same value as the first one:
[[0,0], [0,1], [1,1], [1,0]] // bad
[[0,0], [0,1], [1,1], [1,0], [0,0]] // good
I would like to do some lenient validation (i.e. fixing the object, instead of rejecting it) when saving a GeoJSON Polygon to my MongoDB instance with Mongoose.
I do this:
export type Coordinates = number[]
// model object
export class Polygon {
type: string;
coordinates?: Coordinates[];
constructor(coordinates?: Coordinates[]) {
this.type = "Polygon";
this.coordinates = coordinates;
}
}
// schema object
export const PolygonSchema = {
type: String,
coordinates: [[Number]],
};
// model-schema binding
const polygonSchema = new mongoose.Schema(PolygonSchema, { typeKey: '$type' }).loadClass(Polygon);
// pre-save hook
polygonSchema.pre('save', async (next, opts) => {
// @ts-ignore
const pol: Polygon = this; // I find this assignment everywhere in documentation, but it seems to fail
if (pol?.coordinates?.length > 1 && pol.coordinates[0] !== pol.coordinates[pol.coordinates.length - 1]) {
pol.coordinates.push(pol.coordinates[0]);
}
next();
});
tsc
complains that this
will always be undefined, and the pol
object is indeed undefined when debugging. However, this
is not, according to the Visual Studio debugger.
Is it possible to modify the object during the pre-save hook, or do I need to do this beforehand ?
A note, in case it matters: a Polygon will always be a subdocument in my MongoDB instance: it is saved as the shape of another object.
CodePudding user response:
The pre('save', ...)
function won't give you access to the document if you pass it an arrow function instead of a traditional function. Arrow functions do not have this
bound, and instead use the parent scope.
From mozilla dev docs
Arrow functions don't have their own bindings to this, arguments or super, and should not be used as methods.
Change your arrow to a traditional function definition
From
polygonSchema.pre('save', {document: true, query: true}, async (next, opts) => {...}
To
polygonSchema.pre('save', {document: true, query: true}, async function (next, opts) {...}
CodePudding user response:
This turned out to be a problem that I did not expect. Apparently, you have no access to the this
object in anonymous (arrow) functions ( source: https://youtu.be/DZBGEVgL2eE?t=1495 ). I don't know if this means in JavaScript in general or Mongoose, specifically, but the problem was fixed by doing this:
const polygonSchema = new mongoose.Schema(PolygonSchema, { typeKey: '$type' }).loadClass(Polygon);
async function preSave() {
// @ts-ignore
const pol: Polygon = this;
if (pol?.coordinates?.length && pol.coordinates[0] !== pol.coordinates[pol.coordinates.length - 1]) {
pol.coordinates.push(pol.coordinates[0]);
}
}
polygonSchema.pre('save', preSave);