【问题标题】:Mongoose unique auto-incrementing slugMongoose 独特的自增弹头
【发布时间】:2016-07-26 15:36:53
【问题描述】:

我正在为我的项目中的某些模型实施slug,我希望保持它的唯一性并基于文档的数据。

var user = new User({ name: { first: 'John', last: 'Doe' });
user.save().then(function(user) {}); // user.slug === 'john-doe'

var user2 = new User({ name: { first: 'John', last: 'Doe' });
user2.save().then(function(user) {}); // user.slug === 'john-doe-2'

我可以成功实现它,我什至可以在.pre('save')钩子上查询数据库,像这样:

UserSchema.pre('save', function(next) {
    var user = this;
    return User.distinct('slug')
        .exec()
        .then(function(slugs) {
            // generate a unique slug like "john-doe-2",
            // checking it against slugs that are already
            // in the database, and set it to user
            next();
        });
});

唯一的问题是并发请求(不太可能,但仍有可能):

var user = new User({ name: { first: 'John', last: 'Doe' }),
    user2 = new User({ name: { first: 'John', last: 'Doe' });

Promise.all([ user.save(), user2.save() ]);
// throws validation error if slug should be unique

为此,我正在寻找一种解决方案来处理验证错误(唯一索引重复)并尝试使用更新的参数(如“john-doe-2”)重新保存文档。

【问题讨论】:

  • 在保存中处理此错误可能会有问题。您已经离开了 presave 处理程序。您可以创建另一个集合“users-slug”并尝试将 slug 插入到那里(slug 字段也应该是唯一的)。您可以在预保存中执行此操作。如果你在那里失败了,你可以改变 slug 并重试。

标签: node.js mongodb concurrency mongoose slug


【解决方案1】:

我知道这个问题已经有一年了,但我遇到了同样的问题,并找到了一个解决方案,在找到第一个空闲 slug 之前不需要客户端重试。如果你有第二个集合userslugs,就这样做

var generatedSlug = 'john.doe';

var count = db.accountslugs.findOneAndUpdate({
    _id: generatedSlug
}, {
    $inc: { count: 1 }
}, {
    upsert: true,
    returnNewDocument: true
}).count;

var fullSlug = generatedSlug + ((count === 1) ? "" : "." + count);

您最终会得到一个完整的 slug,首先是 john.doe,然后是 john.doe.2,依此类推。

这种方法要快得多,因为它通过一个查询找到一个 slug。我没有使用 mongoose,因为这是这个系列唯一要做的事情,而且看起来有点过头了。

编辑:请记住,您的客户应该验证 generatedSlug 不是已经以点后跟数字结尾,那么您会遇到问题。

【讨论】:

    【解决方案2】:

    好的,所以我最终得到了一个单独的蛞蝓模型,有点像 @AmiramKorach 建议的那样。然后我实现了一个获取 slug 的模块,它依赖于 Mongo 的 11000 错误(重复条目)。

    function getSlug(base, counter) {
        if (typeof counter === 'undefined') counter = 0;
        var url = base;
        if (counter > 0) url += '-' + counter;
        var slug = new Slug({ url: url });
        return slug.save()
            .then(function(slug) {
                return slug.url;
            })
            .catch(function(reason) {
                if (reason.code === 11000) return getSlug(base, counter + 1);
                throw reason;
            });
    }
    
    Promise.all([
        getSlug('john-doe'),
        getSlug('john-doe'),
        getSlug('john-doe'),
        getSlug('john-doe'),
        getSlug('john-doe'),
        getSlug('john-doe'),
        getSlug('john-doe')
    ])
    .then(function(slugs) {
        // slugs are [ 'john-doe', 'john-doe-1', 'john-doe-2',
        //'john-doe-4', 'john-doe-5', 'john-doe-6', 'john-doe-3' ]
    });
    

    【讨论】:

      猜你喜欢
      • 2018-06-11
      • 2015-08-02
      • 2020-04-18
      • 1970-01-01
      • 1970-01-01
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多