【问题标题】:Mongoose handle duplicate url slugMongoose 处理重复的 url slug
【发布时间】:2017-01-13 08:26:13
【问题描述】:

我正在使用 Mongoose 在 MongoDB 中存储来自博客标题的 url slug。因此,对于用户输入相同博客标题的情况,我想像 Wordpress 处理它的方式一样,在 slug 上附加一个数字,并为每个重复项增加一个数字。

例子:

  1. 博客标题
  2. blog-title-2
  3. blog-title-3

此外,当从博客标题评估的 slug 是 blog-title-2 并且已经存在 blog-title-2 slug 时,它会很聪明地在后面附加 -2 而不是增加数字。所以它将是 blog-title-2-2。

我该如何实现?

【问题讨论】:

    标签: string mongoose slug


    【解决方案1】:

    我自己想出了如何实现它。

    function setSlug(req, res, next) {
      // remove special chars, trim spaces, replace all spaces to dashes, lowercase everything
      var slug = req.body.title.replace(/[^\w\s]/gi, '').trim().replace(/\s+/g, '-').toLowerCase();
      var counter = 2;
      // check if there is existing blog with same slug
      Blog.findOne({ slug: slug }, checkSlug);
      // recursive function for checking repetitively
      function checkSlug(err, existingBlog) {
        if(existingBlog) { // if there is blog with the same slug
          if(counter == 2) // if first round, append '-2'
            slug = slug.concat('-' + counter++);
          else // increment counter on slug (eg: '-2' becomes '-3')
            slug = slug.replace(new RegExp(counter++ + '$', 'g'), counter);
          Blog.findOne({ slug: slug }, checkSlug); // check again with the new slug
        } else { // else the slug is set
          req.body.slug = slug;
          next();
        }
      };
    }
    

    我玩了一会儿 Wordpress;发布了许多带有奇怪标题的测试博客文章,只是为了看看它如何处理标题转换,我根据我的发现实现了转换标题的第一步。 WordPress:

    • 删除所有特殊字符
    • 修剪前导和结尾空格
    • 将剩余空格转换为单个破折号
    • 全部小写

    【讨论】:

      【解决方案2】:

      在您的 mongooese 模型中,您可以为该字段提供唯一选项:

      var post = new post({
        slug: {
          type: String,
          unique: true
        }
      });
      

      然后当你保存它时,你可以验证并知道 slug 是否不是唯一的,在这种情况下你可以修改 slug:

      var post = new post({ slug: "i-am-slug"});
      var tempSlug = post.slug
      var slugIsUnique = true;
      car counter = 1;
      do{
        error = post.validateSync();
        if(// not unique ){ //Make sure to only check for errors on post.slug here
          slugIsUnique = false;
          counter++;
          tempSlug = post.slug + "-" + counter;
        }
      }while(!slugIsUnique)
      
      post.slug = tempSlug;
      post.save((err, post) => {
        // check for error or do the usual thing
      });
      

      编辑: 这不会占用 blog-title-2 并输出 blog-title-2-2 但它将输出 blog-title-2-1 除非它已经存在

      【讨论】:

        【解决方案3】:

        我发现以下代码对我有用:

        一般的想法是创建一个您可能想要使用的 slug 数组,然后使用 MongoDB 的 $in query operator 来确定这些 slug 是否存在。

        注意:以下解决方案使用NPM's slugify。这是可选的。我也使用 crytpo 进行随机化,因为它具有更高的性能。这也是可选的。

        private getUniqueSlug(title: string) {
            // Returns a promise to allow async code.
            return new Promise((resolve, reject) => {
        
                const uniqueSlug: string = "";
                // uses npm slugify. You can use whichever library you want or make your own.
                const slug: string = slugify(title, { lower: true, strict: true });
        
                // determines if slug is an ObjectID
                const slugIsObjId: boolean = (ObjectId.isValid(slug) && !!slug.match(/^[0-9a-fA-F]{24}$/));
        
                // creates a list of slugs (add/remove to your preference)
                const slugs: string[] = [];
                // ensures slug is not an ObjectID
                slugIsObjId ? slugs.push(slug + "(1)") : slugs.push(slug);
                slugs.push(slug + "(2)");
                slugs.push(slug + "(3)");
                slugs.push(slug + "(4)");
                slugs.push(slug + "(5)");
        
                // Optional. 3 random as fallback (crypto used to generate a random 4 character string)
                for (let x = 0; x < 2; x++) {
                    slugs.push(slug + "(" + crypto.randomBytes(2).toString("hex") + ")");
                }
        
                // Uses a single find instance for performance purposes
                // $in searches for all collections with a slug in slugs array above
                const query: any = { slug: { $in: slugs } };
                Collection.find(query, { slug: true }).then((results) => {
                    if (results) {
                        results.forEach((result) => {
                            slugs.every((s, si) => {
                                // If match found, remove from slugs since that slug ID is not valid.
                                if (s === result.slug) { slugs.splice(si, 1); return false; } else { return true; }
                            });
                        });
                    }
                    // returns first slug. Slugs are ordered by priority
                    if (slugs.length > 0) { resolve(slugs[0]); } else {
                        reject("Unable to generate a unique slug."); // Note: If no slug, then fails. Can use ObjectID as failsafe (nearly impossible).
                    }
                }, (err) => {
                    reject("Find failed");
                });
            });
        }
        

        【讨论】:

          猜你喜欢
          • 2020-11-24
          • 2013-03-27
          • 2020-04-03
          • 2015-01-27
          • 2014-05-04
          • 2019-07-28
          • 2018-10-12
          • 1970-01-01
          • 2020-08-04
          相关资源
          最近更新 更多