String Mongoose句柄重复url段塞

String Mongoose句柄重复url段塞,string,mongoose,slug,String,Mongoose,Slug,我使用Mongoose在MongoDB中存储博客标题中的url片段。因此,当用户输入相同的博客标题时,我想像Wordpress处理它一样,在slug上添加一个数字,并为每个重复项递增 例如: 博客标题 blog-title-2 blog-title-3 另外,当从blog标题计算的slug是blog-title-2,并且已经有blog-title-2 slug时,在后面附加-2而不是增加数字将足够聪明。所以它将是blog-title-2-2 如何实现这一点?在您的mongooese模型中,您可以

我使用Mongoose在MongoDB中存储博客标题中的url片段。因此,当用户输入相同的博客标题时,我想像Wordpress处理它一样,在slug上添加一个数字,并为每个重复项递增

例如:

  • 博客标题
  • blog-title-2
  • blog-title-3
  • 另外,当从blog标题计算的slug是blog-title-2,并且已经有blog-title-2 slug时,在后面附加-2而不是增加数字将足够聪明。所以它将是blog-title-2-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,除非它已经存在

    我自己设法想出了如何实现它

    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:

    • 删除所有特殊字符
    • 修剪前导空间和结束空间
    • 将剩余的空格转换为单破折号
    • 小写所有内容

    我发现以下代码适用于我:

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

    注意:以下解决方案使用。这是可选的。我还使用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");
            });
        });
    }
    
    private getUniqueSlug(标题:string){
    //返回允许异步代码的承诺。
    返回新承诺((解决、拒绝)=>{
    const uniqueSlug:string=“”;
    //使用npm slugify。你可以使用任何你想要的库或自己制作的库。
    常量slug:string=slagify(title,{lower:true,strict:true});
    //确定slug是否为ObjectID
    常量slugIsObjId:boolean=(ObjectId.isValid(slug)和&!!slug.match(/^[0-9a-fA-F]{24}$/);
    //创建段塞列表(添加/删除到您的首选项)
    常量段塞:字符串[]=[];
    //确保slug不是ObjectID
    段塞-段塞推力(段塞+“(1)”):段塞推力(段塞);
    弹头推进(弹头+“(2)”);
    弹头推进(弹头+“(3)”);
    弹头推进(弹头+“(4)”);
    弹头推进(弹头+“(5)”);
    //可选。3随机作为回退(用于生成随机4个字符字符串的加密)
    for(设x=0;x<2;x++){
    slug.push(slug+“(“+加密随机字节(2).toString(“十六进制”)+”));
    }
    //出于性能目的,使用单个查找实例
    //$in搜索上面slug数组中包含slug的所有集合
    常量查询:any={slug:{$in:slugs};
    find(查询,{slug:true})。然后((结果)=>{
    如果(结果){
    results.forEach((结果)=>{
    slug.every((s,si)=>{
    //如果找到匹配项,则从slug中删除,因为该slug ID无效。
    if(s===result.slug){slugs.splice(si,1);返回false;}else{return true;}
    });
    });
    }
    //返回第一个slug。slug按优先级排序
    如果(slugs.length>0){resolve(slugs[0]);}else{
    拒绝(“无法生成唯一的slug。”);//注意:如果没有slug,则失败。可以使用ObjectID作为故障保护(几乎不可能)。
    }
    },(错误)=>{
    拒绝(“查找失败”);
    });
    });
    }