修改 Mongoose 预保存挂钩中的对象

Modify object in Mongoose pre-save hook

在 GeoJSON 多边形(或更严格地说:LinearRing)中,最后一组坐标需要评估为与第一组坐标相同的值:

[[0,0], [0,1], [1,1], [1,0]] // bad
[[0,0], [0,1], [1,1], [1,0], [0,0]] // good

在使用 Mongoose 将 GeoJSON 多边形保存到我的 MongoDB 实例时,我想做一些宽松的验证(即修复对象,而不是拒绝它)。

我这样做:

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抱怨this永远是undefined,调试的时候pol对象确实是undefined。但是,根据 Visual Studio 调试器,this 不是。

是否可以在预保存挂钩期间修改对象,或者我需要事先这样做吗?

注意事项,以防万一:在我的 MongoDB 实例中,多边形将始终是 子文档:它被保存为另一个对象的形状。

如果您将箭头函数而不是传统函数传递给 pre('save', ...) 函数,则它不会为您提供访问文档的权限。箭头函数没有 this 绑定,而是使用父作用域。

来自mozilla dev docs

Arrow functions don't have their own bindings to this, arguments or super, and should not be used as methods.

将箭头更改为传统函数定义

来自

polygonSchema.pre('save', {document: true, query: true}, async (next, opts) => {...}

polygonSchema.pre('save', {document: true, query: true}, async function (next, opts) {...}

这竟然是我没想到的问题。显然,您无权访问匿名(箭头)函数中的 this 对象(来源:https://youtu.be/DZBGEVgL2eE?t=1495)。我不知道这意味着在 JavaScript 一般情况下还是在 Mongoose 中,具体而言,但问题是通过这样做解决的:

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);