Skip to content

FAQ

为什么 hooks 不返回 error

看这个代码:

go
func (r *Repo[T]) InsertOne(ctx context.Context, doc T, opts ...*options.InsertOneOptions) (T, error) {
    if err := doc.BeforeInsert(ctx); err != nil {
        return *new(T), err
    }

    _, err := r.collection.InsertOne(ctx, doc, opts...)
    if err != nil {
        return *new(T), err
    }

    if err = doc.AfterInsert(ctx); err != nil {
        return *new(T), err
    }
    return doc, err
}

假如在 AfterInsert 钩子中发生了错误,返回nil, error,这在处理 error 时是反直觉的,因为数据已经写入数据库。

正确的做法是发生错误就回滚操作,也就是事务。 在许多 orm 库中,如 gorm,hooks 也是默认使用事务

受限于开发成本考虑(对错误的处理会比较复杂),目前不使用事务方案。

关于 BeforeDelete/AfterDelete

暂不支持,目前设想的方案是:

go
func (r *Repo[T]) DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) error {
    var doc T
    if err := r.collection.FindOne(ctx, filter).Decode(&doc); err == nil {
        doc.BeforeDelete(ctx)
        defer doc.AfterDelete(ctx)
    }
    _, err := r.collection.DeleteOne(ctx, filter, opts...)
    return err
}

实用性较低,且会增加一次查询操作,所以等待后续讨论再开发

关于 BeforeFind

正如钩子教程提到的,模型钩子需要在方法中传入模型才能触发,而 Find/Delete 等方法是不需要传入模型的,所以 BeforeFind 和 BeforeDelete 大概率不会有

关于 ReplaceOne

我不推荐使用该方法,所以也没有钩子

为什么没有 exists()

mongoose.js 中 model.exists() 返回 _id

js
/**
 * Returns a document with `_id` only if at least one document exists in the database that matches
 * the given `filter`, and `null` otherwise.
 *
 * Under the hood, `MyModel.exists({ answer: 42 })` is equivalent to
 * `MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()`
 *
 * #### Example:
 *
 *     await Character.deleteMany({});
 *     await Character.create({ name: 'Jean-Luc Picard' });
 *
 *     await Character.exists({ name: /picard/i }); // { _id: ... }
 *     await Character.exists({ name: /riker/i }); // null
 *
 * This function triggers the following middleware.
 *
 * - `findOne()`
 *
 * @param {Object} filter
 * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
 * @return {Query}
 */

Model.exists = function exists(filter, options) {
  _checkContext(this, 'exists')
  if (typeof arguments[2] === 'function') {
    throw new MongooseError('Model.exists() no longer accepts a callback')
  }

  const query = this.findOne(filter)
    .select({ _id: 1 })
    .lean()
    .setOptions(options)

  return query
}

目前,可以使用 IsDocumentExists() 来处理

go
_, err := db.Mongo.Account.FindOne(context.TODO(), filter)
exists, err := modm.IsDocumentExists(err)
if err != nil {
    return err
}
if !exists {
    return fmt.Errorf("Document not found")
}

如果需要使用 _id 则在 FindOne() 处返回文档即可

Released under the MIT License.