Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I implement an "interesting tags" feature like that on Stack Overflow?

Check my other question with bounty: Finding similar number patterns in table

I'm trying to implement an Interesting Tags feature. For reference, this is how it works on SO:

  1. I add into the "interesting" list my interested tags (like php, mysql, jquery and so on).
  2. Then, if any of the displayed questions has some of the tags in my list, it makes the background orange.

I understand how to use jQuery to do that (there are related questions on that), but can't figure out how to implement the back-end portion using MySQL!

So here's my question: How is it done? I imagine it working like this:

  • There is a row in mysql for every member, let's call it "interested_tags".
  • After I write and submit my tag through input, it is being written in a row "interested_tags".
  • Then, the main page has a query which shows all answers and it always checks question's tags with mine tags using strpos like this:

    if(strpos($question_tags, $my_tags) === true) {    //and here will be made background orange } 

Am I thinking right or is there any way to do it?

EDIT: So, can you show me an example or give me some tips how to implement this with many-to-many relationships? Thanks.

like image 627
good_evening Avatar asked May 26 '10 16:05

good_evening


1 Answers

As mentioned in the other answers, there's most likely a many-to-many relationship with between users and tags, represented as an own table. I made a SQL demo of a simplified case. The InterestingTags table is the table connecting what user is interested in what tags.

/* 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; 


Update: Added php demo.
Remember to change your mysql constants before running the demo

What this does is running two queries to the DB:

  • One asking for all questions and their tags
  • One asking for what tags the user is interested in.

To "mark" a question with its tags, it adds a class for each tag it belongs to -- e.g. a question tagged with jQuery (where jQuery has the ID 1) and php (with ID 2) will have the classes tagged-1 and tagged-2.

Now combining this with the other query, fetching the interesting tags, you just have to select the questions having classes corresponding to the interesting tags and style them. For example, if you're interested in the tags with ID 1 and 3, it would be the following jQuery code $('.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     } ?> 
like image 109
Simen Echholt Avatar answered Sep 21 '22 06:09

Simen Echholt