循环生成一个独特的url-frinedly路由

时间:2016-11-08 15:38:27

标签: mysql sql mariadb

我在这个结构中有一个mariadb表,

CREATE TABLE `items` (
    `id` char(36), `route` varchar(255), `value` text,  
    PRIMARY KEY (`id`),
    UNIQUE KEY `route` (`route`)
)

我使用route列来获取用户友好的网址,例如http://www.examle.com/this-is-the-route-of-an-item

当用户创建一个新项目时,除了省略空格和非法字符外,我想"赶上"为新项目选择的路线正在使用的情况,并生成有效的路线。

例如,如果route-of-an-item已被使用,我会回退到route-of-an-item-aroute-of-an-item-b等。

天真的解决方案可能是在一个循环中查询db,例如(一种伪代码):

var additionalChars = "";
while (db.query("select count * from `items` where `route`='" + route + "-" + additionalChars + "'"))
    additionalChars = nextAdditionalChars(additionalChars);

finalRoute = route + '-' + additionalChars;

由于这涉及多次查询数据库,我想到了另一种解决方案。

var additionalChars = "";
var usedRoutes = db.query("select `route` from `items` where `route` like '" + route + "%'");
while(usedRoutes.contains(route + '-' + additionalChars))
     additionalChars = nextAdditionalChars(additionalChars);

finalRoute = route + '-' + additionalChars;

有没有更好的方法来解决这类问题?

我是否认为第二种解决方案会表现得更好?

如果我使用第二个解决方案,我应该在路线字段中添加全文索引吗?

2 个答案:

答案 0 :(得分:1)

您可以按路线降序对查询进行排序,只检索并检查一个项目。在您的伪代码中,这看起来像:

var additionalChars = "";
var usedRoutes = db.query("select `route` from `items` where `route` like '" + route + "%' order by `route` desc limit 1");
if(usedRoutes.route is already in use)
     additionalChars = nextAdditionalChars(additionalChars);

finalRoute = route + '-' + additionalChars;

答案 1 :(得分:0)

好的,所以在咨询了同事之后我最终使用了解决方案2,这里是代码(node.js),万一有人遇到这个问题:

数据库访问

var route = req.body.route || 'noname';    
route = route.replace(/[\s_]/g, '-').toLowerCase().replace(/[^0-9a-z\u0591-\u05F4\u0621-\u064A\-_\s]/g, "").replace(/_+/g, ' ').trim().replace(/[\s_]+/g, '-');

var isArabic = (/[\u0621-\u064A]/g).test(route),
    isHebrew = (/[\u0591-\u05F4]/g).test(route),
    lang = isArabic ? 'ar' : (isHebrew ? 'he' : 'en');

Items.findAll({ where: { route: { $like: route + '%' } }, attributes: ['route'] })
    .then((items) => {
        var routes = _.keyBy(items, 'route'),
            prefix = '';

        while (routes[route + (prefix ? '-' + prefix : '')])
            prefix = charactersCount(prefix, lang);

        Items.create({ route: route + (prefix ? '-' + prefix : ''), value: req.body.value })
        .then(function(item){ 
            res.send({ item: _.pick(item, ['id', 'route', 'author_id', 'created_at']) })
        })
        .catch(function(){ res.sendStatus(500)});
    })
    .catch(function(){ res.sendStatus(500) });

生成其他字符

var chars = {
    ar: { val: "اﻻبتثجحخدذرزسشصضطظعغفقكلمنهةوىي", len: 0 },
    he: { val: "אבגדהוזחטיכלמנסעפצקרשת", len: 0 },
    en: { val: "abcdefghijklmnopqrstuvwxyz", len: 0 }
};
_.forEach(chars, (c) => { c.len = c.val.length });

function charactersCount (current, lang) => {
    if (!current) return chars[lang].val[0];
    lang = lang || 'en';
    var curr = current.split(''),
        len = curr.length,
        pointer = len,
        lastIndex;

    while ((lastIndex = chars[lang].val.indexOf(curr[--pointer]) + 1) >= chars[lang].len) curr[pointer] = chars[lang].val[0];
    if (pointer < 0) { curr.unshift(''); pointer++; }
    curr[pointer] = chars[lang].val[lastIndex];

    return curr.join('');
}

所以我最终得到一个选择查询和一个插入查询,并防止节点侧的冲突。

并且不需要全文索引,因为%仅在like操作符的末尾显示