hello im just curious. about how they do stuff. what i assume they do something like this
#someone1 im stacking on stackoverflow RT #someone2 : hello guys what are you doing?
before i do it in my way i want to tell you about my database scheme
// CID = COMMENT ID, BID = BLOG ID, UID = USER ID
CID BID UID COMMENT
1 1 1 #someone1 im stacking on stackoverflow RT #someone2 : ....
2 1 4 #someone1 im stacking on stackoverflow RT #someone2 : ....
3 1 12 #someone1 im stacking on stackoverflow RT #someone2 : ....
they use regex to do like this to take the #someones name
preg_match_all("/#[a-zA-Z0-9_]+/", $text, $matches);
then they get the # off each name
foreach ($matches as $value) {
foreach ($value as $value) {
$usernames[] = substr($value, 1);
}
}
then they get the UID from the database from doing something like this
foreach ($username as $value) {
# insert database one by one ? so it will be like the example above
}
then we can just output the comment buy geting the UID.
then somhow we can get all the comments in the blog. ( without a same comment ) where blog buid = 1 and give them an notification on every user by where uid = :uid.
is there any better way doing this ? something like twitter or convore ?
Thanks for looking in
Adam Ramadhan
I have done something similar to this with an in-house application that we use for communication.
Basically, you are going to have two tables: status_updates and mentions. Each status update has many mentions. Whenever someone creates a status update, you save it to the status_updates table. During this process, you can also use Regex to detect any #username "mentions". When you find a mention, you add it to your mentions table. For example, your mentions table might look something like this:
mention_id (Auto-incrementing key) | status_message_id | username_id
That way if you want to see if someone is mentioned in a status message you can do a quick lookup in the status_messages table, as opposed to loading up the status message and running the Regex each time. The other nice thing about this approach is that it allows you to have multiple mentions in each status message. Just create a record in mentions for each.
That's the basic way that we have set it up.
EDIT: If you wanted to pull an "activity feed" for a given user, showing only the status updates in which they have been mentioned, it would be as simple as:
SELECT * FROM mentions m LEFT JOIN status_messages s ON m.status_message_id = s.id WHERE m.username_id = $username_id
I should note that this is not how they do it at Twitter, because they are dealing with issues of scale that would make this simple way of doing things impossible. However, I think this is the simplest solution that works well if you aren't worried about scaling to hundreds of thousands of users. If you are, then you probably have more issues on your hands than this.
You can use it like bb codes but instead of taken it like [foo] [/foo] you take the # and end it at the space ... before it's insert into your database you take another script and break the # after the space. and put the mention into a separate column then use bbcodes to make the mention on the fly
Example..
if ( strstr("$status", "#") ) {
$explodeat = explode("#", $status);
$explodeat1 = explode(" ", $explodeat[1]);
$status=$explodeat1[0];
}
and insert $status into your mentions column in your database... The BB code for it after that won't be so hard
I think in MySQL, you can use DISTINCT to avoid duplicates rows:
Something link this:
SELECT `CID`, `BID`, DISTINCT `COMMENT`
FROM comments
WHERE UID = :uid
AND ##Others clauses for bloc here##
Related
I'm trying to set up a for loop to print all data from a database table that starts with a certain letter. For example, let's say from all the usernames in the database, I only want to print every username starting with the letter "b". My end result I want to achieve is something along the lines of this:
A
adam
angel
apple
B
ball
bear
blue
C
car
cell
chris
#
0wen
1uis
3than
.,_
.apple.
,car,
_jeff_
I want to be able to print all usernames under the corresponding character in which they start with. I have the starting characters under heading tags, so all I really need is to print the usernames under them. I figured running a simple for loop under each heading tag that filters that data would do the trick, but I for the life of me can't figure out how to go about doing it. This my code so far (I know this will print every user in the table):
require_once 'important/connect.php';
$query = $link->prepare('select distinct usr from info order by usr');
$query->execute();
$users = $query->fetchAll(PDO::FETCH_OBJ);
foreach ($users as $user)
{
print "<center>{$user->usr}</center>";
}
This code above is merely to show what I'm working with. I'm shooting to keep each username to print as a url as well, so when their username is clicked, it will display more information in a seperate pop up window, however I already have that working. Anyway, how would I implement this or is the way I wanna go about it not possible?
For the loop you can use this PHP syntax:
foreach(range('A','Z') as $letter) {
//your own code
}
range will make you iterate through the letters as you do usually with foreach.
The point about your question is about performance.
If you have a small amount of data to return you can return the whole array and then search with php functions to manipulate arrays only the items that begins with $letter.
If you have many items it is better (IMHO) to run a query for each letter using mysql instruction LIKE (cause this will improve performance):
"SELECT usr FROM info WHERE usr LIKE '$letter%' ORDER BY usr GROUP BY usr"
LIKE will match only those usr that will start with $letter and will have anything else following. % is the wildcard to be used.
Also note that in the query I have used GROUP BY usr instead of SELECT DISTINCT usr to get the same result as this is the right way to get an unique list of users.
As Lelio Faieta pointed out, looping through the user list over and over again might be bad for performance. However, querying the database over and over again might also be bad.
So I would suggest getting the users just once, and getting them in the right order:
SELECT usr FROM info ORDER BY usr GROUP BY usr
Then loop through them, and keep track of what starting letter you're on:
$oldLetter = '';
$newLetter = '';
foreach ($users as $user)
{
$newLetter = strtoupper(substr($user->usr, 0, 1));
if($oldLetter != $newLetter)
{
//We are on a new letter.
//Print a heading for all letters between the old one and the new one.
foreach(range(++$oldLetter, $newLetter) as $letter)
print '<h2>' . $letter . '</h2>';
$oldLetter = $newLetter;
}
//Print the user, as before.
print "<center>{$user->usr}</center>";
}
This will not take care of the last group (titled # in your example) for you. To do that, you will need to check whether the first character is a letter in the SQL and sort on that somehow.
Please note that this code is not copy-paste-ready, you will need to work some on it. For instance if the old letter is Z there might be some problems. I have not tested this, so you should before you put it into production.
Pseudo code for a nasty solution:
$alphabetArray = array("A","B", etc);
foreach($alphabetArray as $letter) {
echo '<h2>'.$letter.'</h2>';
$sql = "SELECT * FROM `usertable` WHERE `username` = '".$letter."%' ORDER BY `username` ASC;"
// execute sql, loop through and output results
}
I have query I have came up with for a search bar. I know I can use OR to split the statement to check different columns:
$query = mysql_query('SELECT * FROM PRODUCTS WHERE cats LIKE "%'.$s.'%" OR sub_cats LIKE "%'.$s.'%"');
But if I want to check more than 2 or three columns is the re a way doing something like this to speed things up a bit:
$query_string = explode('&&=',$_GET['q']);
$db_str = 'SELECT * FROM products WHERE name, cats, sub_cats, desc, brand ';
for($x = 0; $x < count($query_string); $x++){
$enc = mysql_real_escape_string($query_string[$x]);
if($x == (count($query_string) -1)){
$db_str .= 'LIKE "%'.$enc.'%"';
}else{
$db_str .= 'LIKE "%'.$enc.'%" ';
}
}
$query = mysql_query($db_str);
I normally used POST for search bars but I fancy giving GET a go it looks more user friendly to me.
I don't know if this would speed things up (you would have to test on your data). However, the following is an alternative with only one comparison:
WHERE concat_ws('|', name, cats, sub_cats, desc, brand) LIKE '%$enc%'
Because your like has a wildcard at the beginning, the query does not use regular indexes. So this version should not slow things down much, if at all.
The answer to your performance problem, though, may be a full text index.
Your code will not work like that, even ignoring the deprecated API. You would need to do something like this:
$searchFields = ['name', 'cats', 'sub_cats', 'desc', 'brand'];
$searchTerm = mysql_real_escape_string($query_string[$x]);
$filters = implode(' OR ', array_map(function($field) use ($searchTerm) {
return "$field LIKE '%{$searchTerm}%'";
}, $searchFields));
$query = mysql_query('SELECT * FROM products WHERE '.$filters);
It's far from optimal though, and will quickly become slow as database and userbase grow. It is however exactly how for example phpMyAdmin implements its database-wide searches. If your database is larger than a few records, or is likely to attract more than a handful of searches simultaneously, you should implement a smarter solution like full text search or implement an external search engine like Apache SOLR, Amazon CloudSearch and the like - relational databases like MySQL on their own just aren't good at messy searching nor were they ever meant to be.
As for this remark:
I normally used POST for search bars but I fancy giving GET a go it
looks more user friendly to me.
This isn't really the good argument to choose. As a rule:
GET is for repeatable requests, that the user can F5 on as much as they want.
POST is for non-repeatable requests, that are usually considered harmful if (overly) repeated.
If your name is Google and you have a gazillion servers all over the world, use GET. If you already know your search has bad performance, stick with POST and don't tempt the lions.
I'm building a website that has records that need to be searched through. I just realized that the search function is too precise. It's a recipe website, so for example, if the user types "keylime pie", the recipe named "Key Lime Pie" won't show up in the results. I'm not sure if there's a script that I can get for this, but I'd really appreciate some help.
Here's my current query:
SELECT * FROM `recipes` WHERE
`recipe_title` LIKE '%$search%' // Key Lime Pie
OR `recipe_summary` LIKE '%$search%' // I love key lime pie
OR `recipe_categories` LIKE '%$search%' //desserts, pies
//... etc
Thanks!
Like queries will soon take down your MySQL database.
If it's not too complicated for you, use Sphinx for searching on mysql it will give you nice results based on keyword density and keyword weight etc. And it's really really fast.
Your best bet may be to look into full text searching. MySQL only supports full text search in MyISAM by default, but if you're running MySQL 5.6 or later, you can do it in InnoDB, at well.
Alternatively, you can run dedicated full text search tools such as Lucene or Sphinx. These are more sophisticated tools that include things like relevance ranking, and may even be able to handle spelling differences/errors (depending on the tool).
Not sure about those double quotes, try this:
SELECT * FROM `recipes` WHERE (`recipe_title` LIKE '%$search%'
OR `recipe_summary` LIKE '%$search%'
OR `recipe_categories` LIKE '%$search%')
I have found it useful to add a related table where you add all the keywords pertaining to a specific entry.
So if the name of your recipe is "Key Lime Pie" you could have one of the keywords be "KeyLime Pie". That allows for typos by the user or allows you to add obvious related terms that people might look for.
This very simple problem could also be solved by adding a column in your recipes table where you store the name (or other short fields) without spaces (and maybe without punctuation). So for instance the name "Key Lime Pie" would be also stored as "KeyLimePie".
Now you can find the recipe by searching for the name "keylime pie": first remove the spaces (and maybe also the punctuation) from the search term. "keylime pie" becomes "keylimepie". That way your query will return the "Key Lime Pie" entry.
You can also expand your search by adding partial LIKE specifically for the beginning or the end of the searched term.
Assuming you store the title without spaces in a column called recipe_title_no_space
"SELECT * FROM recipes
WHERE
recipe_title_no_space LIKE '%".$search."'
OR
recipe_title_no_space LIKE '".$search."%'"
That way you will fin a match for "keylime" and also for "pie keylime". Be careful that this does not return too many/poor results in some cases.
There are a few ways of customising search results as stated above, but if you wanted to experiment...
$searchArray = explode(' ', $search);
$idArray = array();
for($i=0; $i<count($searchArray) $i++) {
$query = "SELECT * FROM table WHERE recipe_title LIKE '%$search%'
AND recipe_summary LIKE '%$search%' AND recipe_categories LIKE '%$search%'";
$result = mysqli_query($link, $query);
$row = mysqli_fetch_array($result);
$idArray = array_merge($idArray, $row['id']);
}
$mostMatches = array_count_values($idArray);
foreach($mostMatches as $key => $value) {
if($value > 1) {$popularResultId[] = $key;}
}
for($i=0; $i<count($popularResultId)$i++) {
mysqli_data_seek($result, $popularResultId[$i]);
$row = mysqli_fetch_array($result);
echo "Title: {$row['recipe_title']}\n\r";
echo "Summary: {$row['recipe_summary']}\r\n";
echo "category: {$row['recipe_categories']}\r\n";
}
If my theory is correct you should be able to tune the results by changing the "if($value > 1)" up or down, you could even have it auto adjust depending on how big your database got if you wanted.
I'm new here and I wanted to get some help solving an Access SQL data merging problem.
Actually I would like to turn this
Customer Comment
James Good
James Bad
James Good
James Ugly
into this :
Customer Comments
James Good, Bad, Good, Ugly
I need To concatenate all the comments of the same customer into one field without VBA because I'm executing the query with PHP
Thanks
Well... I know how to get the Comments concatenated properly, but I'm not sure how to pull the customer. I'm also not sure if this will work in Access, because Access doesn't support stored procs.
DECLARE #Comments VARCHAR(8000)
SELECT #Comments = COALESCE(#Comments + ', ', '') + Comments FROM Customers
I'm guessing you'll have to do this for more than one customer, but all I can think to do is use a CURSOR for each customer, but I don't know if Access supports cursors at the moment.
DECLARE #Comments VARCHAR(8000)
SELECT #Comments = COALESCE(#Comments + ', ', '') + Comments
FROM Customers
WHERE Customer = #Customer (i.e. James, from the cursor)
Honestly, I think you're stuck doing this in PHP.
SELECT * FROM CUSTOMERS ORDER BY Customer, Comment
Then simply loop through the recordset and when the Customer changes, reset the Comments variable for the next customer.
The best way to do this is to load all the matching rows from the database and concatenate them in PHP.
You can get Access to do this, though inefficiently, using a custom function:
Public Function CommentsForCustomer(Customer As String)
Dim sql As String
sql = "select [Comment] from Comments where Customer = '" & Customer & "'"
Dim rs As New ADODB.Recordset
rs.Open sql, CurrentProject.Connection, adOpenSnapshot
Dim text As String
text = ""
Do Until rs.EOF
If Len(text) > 0 Then
text = text & ", "
End If
text = text & rs.Fields("Comment")
rs.MoveNext
Loop
rs.Close
CommentsForCustomer = text
End Function
Then include the expression CommentsForCustomer([Customer]) in your query.
the question might be a bit confusing, so here is what i have:
i insert in the database the previous link where a person came from like tihs:
$came_from = $_SERVER['HTTP_REFERER']; // get previous link
if the link is from google.com it will come like this:
http://www.google.com/#sclient=psy&hl=en&source=hp&q=this+is+a+test&pbx=1&oq=this+is+a+teat&aq=f&aqi=g-s1g-v1&aql=1&gs_sm=s&gs_upl=887l82702l3.10.3.1l17l0&bav=on.2,or.r_gc.r_pw.r_cp.&fp=c3d3303&biw=1920&bih=995
if we look inside we can find q=this+is+a+testas beeing the keywords that i search for.
my question is how can i create a query to return http://www.google.com/ | this+is+a+test ?
i know that the keywords have the + sign in between them.
so far i came up with this, but not exactly what i wanted:
SELECT SUBSTRING_INDEX (table, '+', 1), table FROM table.table WHERE table LIKE '%+%' LIMIT 20
any ideas?
thanks
edit: what happend is that sometimes i get some other url's that don't have q= but maybe seearch=, so i want to keep track of the + sign
As it's been pointed out, you can't reliably get the keywords without supplying the parameters to look for. Here's what I would do:
$url = 'http://www.google.com/#sclient=psy&hl=en&source=hp&q=this+is+a+test&pbx=1&oq=this+is+a+teat&aq=f&aqi=g-s1g-v1&aql=1&gs_sm=s&gs_upl=887l82702l3.10.3.1l17l0&bav=on.2,or.r_gc.r_pw.r_cp.&fp=c3d3303&biw=1920&bih=995';
$possible = array('q', 'ssearch', 'oq');
$query_str = NULL;
foreach ($possible as $search) {
if (isset($arr[$search])) {
$query_str = $arr[$search];
break;
}
}
Basically all this does is parse the url using PHP's parse_str() and look for the parameter q. If it's not there, it uses ssearch, and then oq. You can add more of them if you need to. If by the end of it it's not found, $query_str will be NULL.
Unless you have a very compelling reason to do it with MySQL only, just process everything on the PHP side. Databases are made to store data, not process it. What I would do is have PHP figure out the search engine and the keywords used and insert those into the DB, as separate fields. ie, have a table like so:
search_engine | query_str
------------- | -----------
google | test
yahoo | something
...
If you know that you need q=... then you can use regexp. I will update post if that's what you need.
As everyone is saying, you need to use the key value (in your example, q). In MySQL, you can do something like this:
SELECT SUBSTRING_INDEX(table, '?q=', -1), table FROM table.table WHERE table LIKE '?' LIMIT 20
I'd also suggest you rename your table column to something other than 'table'.