Как я могу реализовать подобную функцию "интересных тегов" при переполнении стека?
Проверьте мой другой вопрос с помощью bounty: Нахождение аналогичных числовых шаблонов в таблице
Я пытаюсь реализовать Интересную функцию тегов . Для справки, вот как это работает на SO:
- Я добавляю в список "интересных" свои интересующие теги (например, php, mysql, jquery и так далее).
- Затем, если какой-либо из отображаемых вопросов имеет некоторые теги в моем списке, он делает фон оранжевым.
Я понимаю, как используйте для этого jQuery (есть связанные с этим вопросы), но не можете понять, как реализовать внутреннюю часть с помощью MySQL!
Итак, вот мой вопрос: как это делается? Я представляю, как это работает:
- В mysql есть строка для каждого участника, давайте назовем ее "interested_tags".
- После того, как я напишу и отправлю свой тег через ввод, он будет записан в строке "interested_tags".
-
Затем на главной странице есть запрос, который показывает все ответы, и он всегда проверяет теги вопроса с моими тегами, используя strpo следующим образом:
if(strpos($question_tags, $my_tags) === true) { //and here will be made background orange }
Правильно ли я думаю или есть какой-то способ сделать это?
РЕДАКТИРОВАТЬ: Итак, можете ли вы показать мне пример или дать несколько советов, как реализовать это с отношениями "многие ко многим"? Спасибо.
6 answers
Как упоминалось в других ответах, скорее всего, существует связь "многие ко многим" между пользователями и тегами, представленная в виде собственной таблицы. Я сделал демонстрацию SQL упрощенного случая. Таблица InterestingTags
- это таблица, соединяющая, какой пользователь заинтересован в каких тегах.
/* Create tables */
CREATE TABLE User (id INT NOT NULL AUTO_INCREMENT, name varchar(50), PRIMARY KEY(id));
CREATE TABLE Tag (id INT NOT NULL AUTO_INCREMENT, name varchar(50), PRIMARY KEY(id));
CREATE TABLE InterestingTags (user_id INT NOT NULL REFERENCES User(id), tag_id INT NOT NULL REFERENCES Tag(id), PRIMARY KEY(user_id,tag_id));
/* Insert some data */
/* 3 users, 5 tags and some connections between users and tags */
INSERT INTO User (name) VALUES ('jQueryFreak'), ('noFavoriteMan'), ('generalist');
INSERT INTO Tag (name) VALUES ('jQuery'), ('php'), ('asp.net'), ('c#'), ('ruby');
INSERT INTO InterestingTags (user_id, tag_id) VALUES (1,1), (3,1), (3,2), (3,3), (3,4);
/* Select all the users and what tags they are interested in */
SELECT u.name, t.name FROM User u
LEFT JOIN InterestingTags it ON it.user_id = u.id
LEFT JOIN Tag t ON t.id = it.tag_id;
/* Select all tag ids that are interesting to user 3 ("generalist") */
SELECT tag_id FROM InterestingTags WHERE user_id = 3;
/*
Now let's introduce a questions table.
For simplicity, let's say a question can only have one tag.
There's really a many-to-many relationship here, too, as with user and tag
*/
CREATE TABLE Question (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(50) NOT NULL, tag_id INT NOT NULL REFERENCES Tag(id), PRIMARY KEY(id));
/* Insert some questions */
INSERT INTO Question (title, tag_id) VALUES
('generating random numbers in php', 2), /*php question*/
('hiding divs in jQuery', 1), /*jQuery question*/
('how do i add numbers with jQuery', 1), /*jQuery question 2*/
('asp.net help', 3), /*asp.net question */
('c# question', 4), /*c# question */
('ruby question', 5); /*ruby question */
/* select all questions and what users are interested in them */
SELECT q.title, u.name FROM Question q
LEFT JOIN InterestingTags it ON it.tag_id = q.tag_id
LEFT JOIN User u ON u.id = it.user_id;
/* select all questions a user will be interested in. Here the user is jQueryFreak with id = 1 */
SELECT q.id, q.title FROM Question q
LEFT JOIN InterestingTags it ON it.tag_id = q.tag_id
LEFT JOIN User u ON u.id = it.user_id
WHERE u.id = 1;
/* Select all questions and indicate whether or not jQueryFreak (with id = 1) is interested in each one */
/* TODO: make SO question about how to do this as efficient as possible :) */
SELECT q.id, q.title,
(SELECT COUNT(*) FROM InterestingTags it
WHERE it.tag_id = q.tag_id AND it.user_id = 1)
AS is_interested
FROM Question q;
/* Let's add a many-to-many relationship between questions and tags.
Questions can now have many tags
*/
ALTER TABLE Question DROP COLUMN tag_id;
CREATE TABLE Question_Tag (
question_id INT NOT NULL REFERENCES Question (id),
tag_id INT NOT NULL REFERENCES Tag (id),
PRIMARY KEY (question_id, tag_id)
);
/* Insert relationships between questions and tags */
INSERT INTO Question_Tag VALUES
/* First the tags as in the above examples */
(1,2), (2,1), (3,1),(4,3),(5,4),(6,5),
/* And some more. ASP.NET question is also tagged C#
and php question is tagged jQuery */
(1,1), (4,4);
/* select all questions and what users are interested in them
(Some combinations will show up multiple times. This duplication is removed in the
two following queries but I didn't find a solution for it here)*/
SELECT q.title, u.name FROM Question q
LEFT JOIN Question_Tag qt ON qt.question_id = q.id /* <-- new join */
LEFT JOIN InterestingTags it ON it.tag_id = qt.tag_id
LEFT JOIN User u ON u.id = it.user_id;
/* select all questions a user will be interested in. Here the user is jQueryFreak with id = 1 */
SELECT q.id, q.title FROM Question q
LEFT JOIN Question_Tag qt ON qt.question_id = q.id /* <-- new join */
LEFT JOIN InterestingTags it ON it.tag_id = qt.tag_id
LEFT JOIN User u ON u.id = it.user_id
WHERE u.id = 1
GROUP BY q.id; /* prevent duplication of a question in the result list */
/* Select all questions and indicate whether or not jQueryFreak (with id = 1) is interested in each one */
/* STILL TODO: make SO question about how to do this as efficient as possible :) */
SELECT q.id, q.title,
(SELECT COUNT(*) FROM InterestingTags it
WHERE it.tag_id = qt.tag_id AND it.user_id = 1)
AS is_interested
FROM Question q
LEFT JOIN Question_Tag qt ON qt.question_id = q.id /* <-- new join */
GROUP BY q.id;
Обновление: Добавлена демонстрация php.
Не забудьте изменить свои константы mysql перед запуском демо-версии
Для этого выполняется два запроса к БД:
- Один, задающий все вопросы и их теги
- Один спрашивает, какие теги интересуют пользователя.
Чтобы "пометить" вопрос своими тегами, он добавляет class
для каждого тега, к которому он принадлежит - например, вопрос, помеченный jQuery
(где jQuery имеет идентификатор 1
) и php
(с идентификатором 2
), будет иметь классы tagged-1
и tagged-2
.
Теперь, объединив это с другим запросом, выбрав интересные теги, вам просто нужно выбрать вопросы, имеющие классы, соответствующие интересным тегам, и их стиль. Например, если вас интересуют теги с идентификаторами 1
и 3
, это будет следующий код jQuery $('.tagged-1, .tagged-3').addClass('interesting-tag');
<?php
const mysql_host = "localhost";
const mysql_username = "";
const mysql_password = "";
const mysql_database = "INTERESTINGTEST";
const user_id = 1; //what user is viewing the page?
class Question {
public $id;
public $title;
public $tags;
function __construct($id,$title) {
$this->id = $id;
$this->title = $title;
$this->tags = array();
}
}
class Tag {
public $id;
public $name;
function __construct($id,$name) {
$this->id = $id;
$this->name = $name;
}
}
/**************************
Getting info from database
****************************/
mysql_connect(mysql_host,mysql_username,mysql_password);
mysql_select_db(mysql_database);
//Fetch interesting tags
$result = mysql_query("SELECT tag_id FROM InterestingTags WHERE user_id = " . user_id);
$interesting_tags = array();
while($row = mysql_fetch_array($result))
{
$interesting_tags[] = $row['tag_id'];
}
//Fetch all questions and their tags
$query_select_questions =
'SELECT q.id AS q_id, q.title AS q_title, t.id AS t_id, t.name AS t_name FROM Question q
LEFT JOIN Question_Tag qt ON qt.question_id = q.id
LEFT JOIN Tag t ON t.id = qt.tag_id';
$result = mysql_query($query_select_questions);
$questions = array();
while($row = mysql_fetch_array($result))
{
$q_id = $row['q_id'];
$q_title = $row['q_title'];
$t_id = $row['t_id'];
$t_name = $row['t_name'];
if (!array_key_exists($q_id, $questions))
$questions[$q_id] = new Question($q_id, $q_title);
$questions[$q_id]->tags[] = new Tag($t_id, $t_name);
}
mysql_close();
/**************************
Write document
****************************/
?>
<style>
.question { padding:0px 5px 5px 5px; border:1px solid gray; margin-bottom: 10px; width:400px }
.interesting-tag { background-color: #FFEFC6 }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
<script>
var interesting_tags = [ <?php echo implode($interesting_tags,',') ?> ];
var tagclass_prefix = ".tagged-";
var tags_selector = tagclass_prefix + interesting_tags.join(", " + tagclass_prefix);
$(function() {
$(tags_selector).addClass("interesting-tag");
});
</script>
<?php
foreach ($questions as $q) {
$tagsIDs = array();
$tagNames = array();
foreach ($q->tags as $tag) {
$tagsIDs[] = $tag->id;
$tagNames[] = $tag->name;
}
$classValue = "tagged-" . implode($tagsIDs," tagged-");
$tagNames = implode($tagNames, ", ");
?>
<div id="question-<?php echo $q->id ?>" class="question <?php echo $classValue ?>">
<h3><?php echo $q->title ?></h3>
Tagged with <strong><?php echo $tagNames ?></strong>
</div>
<?php
}
?>
В mysql есть строка для каждого участника, давайте назовем ее "interested_tags".
Скорее всего, существует дополнительная таблица, которая представляет отношение "многие ко многим" между пользователями и тегами. С другой таблицей, которая связывает теги с вопросами.
Тогда вам просто понадобится запрос (или, скорее всего, хранимая процедура), который сравнивает теги пользователя с тегами вопроса и возвращает логическое значение true или false.
Теги переполнения стека работают по крайней мере с *
в теге, поэтому храните свои теги в массиве и повторяйте их, используя сопоставление с шаблоном (не имеет значения, используете ли вы glob, SQL или регулярное выражение, пока пользователь знает, что будет использоваться).
Это может вас просветить. Как сказал Келли, это делается на Javascript после загрузки страницы. Насколько я могу судить, они загружают все теги для всех вопросов, и те, у которых есть те же теги, что и на правой стороне, они выделяются. Видишь
@новый вопрос: http://skmzq.qiniucdn.com/data/20060423142114/index.html содержит (старое, но полезное) сравнение различных видов тегирования. Не забудьте прочитать комментарии, они очень полезны.
По тому, как отображаются страницы, я догадался, что сравнение тегов выполняется в JavaScript. Таким образом, шаги будут следующими:
- запрос интересных тегов пользователей
- сделать результаты доступными для JS на стороне клиента
- JS повторяет каждый вопрос и изменяет атрибут на основе соответствия (включает удаление сообщения, если оно соответствует тегу "игнорируется".