我正在使用MySQL 5.5.50
。
我正在尝试建立一个用于对博客进行标记和交叉引用的系统。
这些表的当前结构如下(大致):
blog_table
| id | blog |
| :- | :---------- |
| 1 | Lorum Ipsum |
| 2 | Lorum Ipsum |
| 3 | Lorum Ipsum |
tag_table
| id | tag |
| :- | :------ |
| 1 | Cats |
| 2 | Dogs |
| 3 | Animals |
| 4 | Funny |
| 5 | Serious |
tag_blog_table
| id | tag_id | blog_id |
| :- | :----- | :------ |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 4 | 1 |
| 4 | 4 | 2 |
| 5 | 5 | 3 |
从理论上讲,这是可行的。但是,现在我想交叉引用标签并将其分组。
例如,Cats
和Dogs
是Animals
。因此,如果我想要所有“有趣的动物”博客,它将使用Cats
,Dogs
和Funny
来创建所有博客。此外,如果我添加了Non-Human
标签,则可以将Animals
和Non-Human
(如果存在)交叉引用/分组为Plants
,等等。
我意识到要正确执行此操作,我需要一个图形数据库,因为上述关系实际上是标签(即顶点)的分组(即边)。但是我有这个关系数据库。
到目前为止,我唯一想出的解决方案是制作一个名为tag_tag_table
的第四张表,它使我能够在每个“顶点”(标记)之间创建“ edges”。然后在服务器上遍历“图形”。该表如下所示:
tag_tag_table
| id | from_tag_id | to_tag_id |
| :- | :---------- | :-------- |
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 6 | 1 |
| 4 | 7 | 2 |
| 5 | 8 | 7 |
然后在PHP中(服务器正在运行):
// this is an example,
// the code isn't perfect
function traverse($start_id) {
global $pdo_link;
$final_ids = [];
$traverse_stmt = $pdo_link->prepare(
"SELECT `to_tag_id`
FROM tag_tag_table
WHERE `from_tag_id` = ?;"
);
$cyclic_check = [];
// In this case, I a doing a
// Breadth-First-Traverse.
$traverse_queue = new SplQueue;
$traverse_queue->setIteratorMode(SplDoublyLinkedList::IT_MODE_DELETE);
$traverse_queue->enqueue($start_id);
foreach ($traverse_queue as $id) {
// Prevent cyclic loops
// NOTE: This might need to be
// put at the end of the statement,
// that's not fully tested.
if (array_search($id, $cyclic_check) !== false) continue;
else $cyclic_check[] = $id;
// Look for any adjacent vertices
$traverse_stmt->execute([$id]);
$next_ids = $traverse_stmt->fetchAll(PDO::FETCH_COLUMN);
// If there are adjacent vertices, enqueue them.
// Else, we've reached an end.
if (count($next_ids)) foreach ($next_ids as $next_id) $traverse_queue->enqueue($next_id);
else $final_ids[] = $id;
}
$blog_lookup_tag_id_placeholders = implode(', ', array_map(function($tag_id) {
return '?';
}));
$blog_lookup_stmt = $pdo_link->prepare(
"SELECT `blog_id`
FROM tag_blog_table
WHERE `tag_id` IN ($blog_lookup_tag_id_placeholders);"
);
$blog_lookup_stmt->execute($final_ids);
return $blog_lookup_stmt->fetchAll(PDO:FETCH_ASSOC);
}
我目前看到的两个缺点是:
Blogs
完全在“图形”之外。您必须将博客链接到至少一个标签。也许这实际上是完全正确的...
严格来说,这不是在数据库中运行的查询,它需要服务器遍历“边缘”和“顶点”。
我正在尝试找到更好的解决方案来完成此标记系统。