我使用Mongoose存储MongoDB中博客标题的url slugs。因此,对于用户输入相同博客标题的情况,我想这样做就像Wordpress如何处理它一样,它会在slug上附加一个数字并为每个重复项增加它。
示例:
此外,当从博客标题评估的slug是blog-title-2并且已经有blog-title-2 slug时,它会足够聪明地在后面追加-2而不是增加数字。所以这将是blog-title-2-2。
我该如何实现?
答案 0 :(得分:0)
在您的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,除非已经存在
答案 1 :(得分:0)
我设法弄清楚如何自己实现它。
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 :(得分:0)
我发现以下代码对我有用:
通常的想法是创建一个可能可能要使用的块数组,然后使用MongoDB的$in query operator来确定其中是否存在这些块。
注意:以下解决方案使用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");
});
});
}