Count how much specific words are in a database row - php

I have two lists of words and a database with more than thousand news articles.
I want to count how many of the words from the lists $badwords and $goodwords are in each article in my database. Next, I would like to save the two outcomes (of $badwords and $goodwords) per row in the columns badwords and goodwords. I will run this script with a cronjob.
My current table structure The last two rows are empty
TABLE news
-----------------
|ID|newstitle|newscontent|badwords|goodwords|
|1| Rain in London | It is horrible depressive weather in this nice city. | EMPTY | EMPTY |
|2| France wins the WorldCup | The player made a great goal. | EMPTY | EMPTY |
My desired table structure
The number of $badwords and $goodwords in the last two columns
TABLE news
-----------------
|ID|newstitle|newscontent|badwords|goodwords|
|1| Rain in London | It is horrible depressive weather in this nice city. | 2 | 1 |
|2| France wins the WorldCup | The player made a great goal. | 0 | 1 |
My current PHP code
<?php
//the wordlists
$badwords = "depressive horrible";
$goodwords = "great";
//connection to the database
$servername = "localhost";
$username = "user";
$password = "pass";
$dbname = "db";
$conn = new mysqli($servername, $username, $password, $dbname);
// here is my sql query
$sql = " UPDATE news
set badwords = (SELECT count (*) from news
where newscontent LIKE '.%$badwords%.')";
//close the connection
$conn->close();
?>

If I understand your question correctly you want to check if a certain wordlist exists in your database. In that case you're looking for a query like so (also use escaping in your queries depending on the db-class you use, e.g. mysqli_real_escape_string()):
SELECT COUNT(*) AS `count`
,`newscontent`
FROM `news`
WHERE `newscontent` = '" . $wordlist . "'
If you want to display how many times each wordlist exists in your database this is what you need:
SELECT COUNT(*) AS `count`
,`newscontent`
FROM `news`
GROUP BY `newscontent`
If you want to display how many strings you have for a given number of words this is what you're looking for:
<?php
$sql = new mysqli($host, $user, $password, $database);
$query = $sql->query('select * from `news`');
$summary = [];
while($record = $query->fetch_object()) {
$summary[count(explode(' ', $record->newscontent))]++;
}
echo '<pre>';
print_r($summary);
echo '</pre>';
If none of the above are what you're looking for than, after 4 times of reading your question, I literally have no clue what you're after.
Updated answer
Since you've updated your question I understand what you want. See the updated answer below.
<?php
// your db connection ...
// array with good and bad words
$good = [
'awesome',
'neat',
'fantastic',
'great',
// and so on
];
$bad = [
'horrible',
'worst',
'bad',
'terrific',
// and so on
];
// if you keep using your string approach you can set $good and $bad with $good = explode(' ', $goodwords); and $bad = explode(' ', $badwords);
// fetch the record you need
$query = $sql->query('select * from `news` where `ID` = 1'); // insert parameter for your ID here instead of just 1
$newsitem = $query->fetch_object();
// set up good and bad word counters
$totalGood = 0;
$totalBad = 0;
// check how many times each word is mentioned in newscontent
foreach($good as $word) {
// add spaces arround the word to make sure the full word is matched, not a part
$totalGood += substr_count($newsitem->newscontent, ' ' . $word . ' ');
}
// check how many times each word is mentioned in newscontent
foreach($bad as $word) {
// add spaces arround the word to make sure the full word is matched, not a part
$totalBad += substr_count($newsitem->newscontent, ' ' . $word . ' ');
}
// update the record
$sql->query("
update `news`
set `badwords` = " . $totalBad . ",
`goodword` = " . $totalGood . "
where `ID` = " . $newsitem->ID);
An interesting thing on text interpretation remains sarcasm. How would you deal with something like "Well, the weather in England is great again - as usual!" ;) Good luck though!

Related

Search bar should ignore “-”, Caps and spaces (132D123 and 132 d 123 should return true) ? php

Currently when i search the registration number 131-D-12345 I have to type the hyphens to get the results, but I wish to ignore “-”, caps and spaces (so for instance "132D123" and "132 d 123" should return true).
How can I do that in PHP?
<?php
require('/home/s3022041/sqlC/dbConnect.php');
if(isset($_POST['searchById']))
{
$search = mysqli_real_escape_string($connection, $_POST['get_id']);
$id = $_POST['get_id'];
$query = "SELECT * FROM cars WHERE Registration_Number = '$id' ";
$query_run = mysqli_query($connection, $query);
?>
If I understood your question correctly
Your database looks something like this example
1 | 987-D-987
2 | 654-E-456
3 | 789-D-123
4 | 678-Z-123
And for example,if you search for 654 e 456 you are expecting to get entry 2.
Well you could use good old string functions...
<?php
$id = $_POST['get_id'];
$stp1 = preg_replace("/[^a-zA-Z0-9]/", "", $id); //grab only the alphanumerics
$stp2 = strtoupper($stp1); //Make all alphabets uppercase
$stp3 = preg_replace('/\d+/', '',$stp2); //extract the alphabets part
$newsearchid = str_replace($stp3,"-".$stp3."-",$stp2); //put hyphens before and after the alphabet part
echo "spaces or lower case .i will return this".$newsearchid; //now the search string looks like one in the database
require('/home/s3022041/sqlC/dbConnect.php');
if(isset($_POST['searchById']))
{
$search = mysqli_real_escape_string($connection, $newsearchid);
$query = "SELECT * FROM cars WHERE Registration_Number = '$newsearchid' ";
$query_run = mysqli_query($connection, $query);
?>
if you search for "987 d 987" or "987 d987" or "987d987" or "987D987" or "987-d-987" or "987,d#987" you will still get entry 1 from db.
Basically any search in the format [number][alphabet][number] will always return true
Do I understand correctly that the registration numbers in the database dò contain the hyphens ?
In that case, you should modify your query to hold wildcards (e.g. https://www.guru99.com/wildcards.html)
As for the case, either convert the case of the PHP variable before entering it in the query ( strtoupper(), strtolower() ), or try to solve your case in your table definition.
If the database entries do not hold the hyphens, you can filter them out the variable using a replace or preg_replace() to remove them.
Edit
if I assume your database holds very uniform inputs, like this :
ID | registration
----+---------------
1 | 132D123
2 | 234D324
3 | 456D546
4 | 678D123
then you can prepare your search term as follows :
$searchReg = $_POST['get_id'] ; // take your unmodified POST-ed registration
$searchReg = strtoupper($searchReg) ; // make all characters upper-case, as DB entries are all uppercase
$searchReg = preg_replace("/[\s-]?/", "", $searchReg) ; // replace all whitespaces and hyphens by an empty string - i.e. remove them
if(isset($_POST['searchById']))
{
$search = mysqli_real_escape_string($connection, $searchReg);
$id = $searchReg;
...
If however, your database is not formatted very strictly, like :
ID | registration
----+---------------
1 | 132D123
2 | 234d324
3 | 456-D-546
4 | 678-d-123
Then you need to make your SQL query cover the searchterm using wildcards :
$searchReg = $_POST['get_id'] ; // take your unmodified POST-ed registration
$searchReg = preg_replace( "/[\s-]?/", "%", $searchReg) ; // replace all whitespaces and hyphens by a SQL wildcard
if(isset($_POST['searchById']))
{
$search = mysqli_real_escape_string($connection, $searchReg);
$id = $searchReg;
$query = "SELECT * FROM cars WHERE Registration_Number LIKE '" . strtoupper($id) ."' OR Registration_Number LIKE '" . strtolower($id) . "'" ;
...

Include corresponding data from table to json from another table

I have two tables-food and tags. Each row from food has corresponding tags.
I want to output each row with those tags, ie:
table food:
id | name
1 | bread
2 | meat
table tags:
reference_id | tag
1 | bakery
1 | wheat
2 | cow
desired output is:
{"results":
[{"id":"1","name":"bread","tags":["bakery","wheat"]},
{"id":"2","name":"meat","tags":["cow"]}]
}
So far I have this:
$db = getConnection();
$stmt = $db->query($sql);//get every column from every food
$food = $stmt->fetchAll(PDO::FETCH_OBJ);
$tagsSql="select id_reference,tag FROM tags T,food F WHERE F.id=T.food_id_reference";
$stmt = $db->query($tagsSql);
$tags=$stmt->fetchAll(PDO::FETCH_OBJ);
echo '{"results":' . json_encode($food) . '}';
I was thinking about cycling through every food and ever tag and find matching pairs, but it seems pretty heavyweight to me (considering the fact, that I could have thousands of rows). Do you have any suggestions?
Untested but I think something like this should work for you
$db = getConnection();
$stmt = $db->query($sql);//get every column from every food
$tagsSql="select F.id as id, F.name as name, group_concat(T.tag SEPARATOR ',') as tags FROM tags T,feeds F WHERE F.id=T.feed_id_reference group by feed_id_reference";
$stmt = $db->query($tagsSql);
for($x = 0; $x < count($tags); $x++){
$tags[$x]->{"tags"} = explode(",", $tags[$x]->{"tags"});
echo '{"results":' . json_encode($tags) . '}';
}

PHP search multiple fields issue

I'm working through this tutorial online: http://goo.gl/qnk6U
The database table: ajax_search
Firstname | Lastname | Age | Hometown | Job
-------------------------------------------
Joe | Smith | 35 | Boulder | CIA
Steve | Apple | 36 | Denver | FBI
(Types are all varchar except age is an int.)
My question. Is the sql select statement below written correctly to query "Joe 35"? For some reason, I can only query "Joe" and it works, but not combining search terms.
$sql = "select * from ajax_search where FirstName like '%$rec%' or LastName like '%$rec%' or Age like '%$rec%' or Hometown like '%$rec%'";
Assuming your query is "Joe 35", then no. Your query matches any row where any of the four fields contains "Joe 35" (in a single field). To query for a user with name Joe and age 35, you'd need to split the search query and do something like:
WHERE Firstname LIKE "$firstname" AND Age LIKE "$age"
You need to split that sting from search query:
$columns = array("Firstname", "Lastname", "Age", "Hometown", "Job");
$string = ""; // acquire query string
$split = explode(" ", $string);
$search_words = array();
foreach ($split as $word) {
$search_words[] = $word;
}
Create query to search all over the fields:
$first = true;
$sql = "select * from ajax_search where";
foreach ($search_words as $word) {
foreach ($columns as $col) {
$sql .= (($first) ? " " : " OR") . "`$col` LIKE \"%" . $word . "%\"";
$first = false;
}
}
Then run this query.
Another and also a better solution would be more complicated word(tag) based indexing, because this can generate morbid queries when used with more words.

PHP MySQL - An attempt at recursive functions

I'm trying out my first recursive function (at least I think I am!) and it only half works. First, the code:
function check_title($i,$title) {
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
if($num==0) {
return $title;
}else {
$title=$title.' ('.$i++.')';
check_title($i,$title);
}
}
What I'm doing is taking a string (title) and checking if that title exists in the db already. If it does, I want to append a number to the newer of the duplicates (e.g. 'I Am A Title' becomes 'I Am A Title-2'). I then need to run the function again to check this new version of my title, and increase the appended value as required ('I Am A Title-3'). Once no duplication is discovered, return the Title in its acceptable form.
It works when no duplication is found (the easy bit), but fails when duplication is found. Instead of appending a number, the entire title variable is emptied.
Any help would by greatly appreciated!
As Mchl stated, the empty title is due to a lack of return in the else branch.
However, there is a problem with the function as it does not do what you intend. Currently, your function is building $title as 'Title-1-2-3-4-etc' the way you currently append the number to the title and check again. Instead of passing a modified title on the recursed call you should just pass the base title. Then, for the query, modify the title.
function check_title($title, $i = 0) {
$qtitle = $title . ($i == 0 ? '' : "-$i");
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$qtitle'");
$num=mysql_num_rows($q);
if($num==0) {
return $title . ($i == 0 ? '' : "-$i");
}else {
return check_title(++$i,$title);
}
}
PS, I also changed the order of parameters that way your initial call doesn't need to specify 0.
$title = check_title($title);
PPS, I should mention this is a solution to do it via recursion. However, a recursive solution is not the proper solution here as it needlessly makes return trips to the DB. Instead, you should use an sql query that selects all titles LIKE "$title%" Order by title asc. Then, iterate through each result and do a regex comparison with the title to see if it matches a pattern <title>|<title>-<#>. If it does you increment a duplicate counter. At the end you spit out the title with an appended counter value. I'll leave that solution as an exercise for the original poster.
Use a loop instead...
$record_exists = true;
$title_base = "I Am A Title";
$title = $title_base;
$i = 0;
while($record_exists) {
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
if($num==0) {
$record_exists = false;
// Exit the loop.
}
else {
$i++;
$title = $title_base . "-" . $i;
}
}
echo $title; // last existing title
However, optimally you'd do more work with a single SQL query and iterate the result, saving a lot of trips to and from the database.
And just for fun...
$title_base = "I Am A Title";
$title = $title_base;
for ($i=1, $num=1; $num != 0; $i++)
{
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
$title = $title_base . "-" . $i;
}
echo $title; // next title in sequence (doesn't yet exist in the db)
You lack a return in else branch.
Recursion is not the best idea for this application. Hint: do a query like this SELECT MAX(Title) FROM posts WHERE Title LIKE '$title%');
Your recursive function is fine except for 2 things:
The original title isn't maintained between recursive calls. Hence each time $title = $title . ' (' . $i++ . ')' runs, another parenthesis is appended to the title, like "abc", "abc (1)", "abc (1) (2)" and so on.
You are returning $title when no more matches are found but no title is returned in the ELSE. It is important to do so. When the execution reaches the IF, it returns the title but the returned title is not assigned anywhere and hence is lost.
Here is the revised code:
$orgTitle = 'I am a title';
function check_title($i, $title = '') {
global $orgTitle;
$q = mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num = mysql_num_rows($q);
if ($num == 0) {
return $title;
} else {
$title = $orgTitle . ' (' . ++$i .')';
return check_title($i, $title);
}
}
echo check_title(0, $orgTitle);
Note the addition of new variable $orgTitle. I've replaced it in the assignment statement inside the ELSE. This does the fix for point 1 above.
Also note the return added before check_title call in the ELSE. This solves point 2.
Hope it makes sense!
Add-on: Recursions are confusing, logically complex and tricky to debug. Also, recursive calls consume more memory (not in case of simple operations like your example) because the compiler/interpreter had to maintain the state variables for all steps in a recursion.
In order to minimise MySql interactions I'd recommend something similar to the following.
function checkTitle($title)
{
/*Return all iterations of the title*/
$res = mysql_query("SELECT COUNT(title) AS titleCount FROM posts
WHERE SUBSTR(title, 1,". strlen($title) .") = '$title' ");
/*Return the incremented title*/
return $title. (mysql_result($res, 0, "titleCount") + 1);
}
Example:
mysql> select title from posts;
+----------+
| title |
+----------+
| firefox1 |
| firefox2 |
| shoe |
| firefox3 |
+----------+
4 rows in set (0.00 sec)
mysql> SELECT COUNT(title) AS titleCount FROM posts WHERE SUBSTR(title, 1,7) = 'firefox' ;
+------------+
| titleCount |
+------------+
| 3 |
+------------+
1 row in set (0.00 sec)
mysql>
---- Follow up test
Test table structure.
mysql>SHOW COLUMNS FROM posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(12) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
/*Test code and output*/
function checkTitle($title)
{
/*Return all iterations of the title*/
$res = mysql_query("SELECT COUNT(title) AS titleCount FROM posts
WHERE SUBSTR(title, 1,". strlen($title) .") = '$title' ");
/*Return the incremented title*/
return $title. (mysql_result($res, 0, "titleCount") + 1);
}
mysql_connect("localhost","root", "password");
mysql_select_db("test");
echo checkTitle("firefox");
Output: firefox4

Swapping a string where space is found in php

I need to implement the following functionality:
I have a name field which contains both name and surname. This is stored in a database and is in the order 'surname name'.
I am implementing a script which searches through these records. Currently, I managed to check if a string contains a space, if it contains a space it means it is a name and not an ID Card Number for instance. Here is the code:
$query = "John Doe";
$checkIfSpaceExists = strpos($query, " ");
if ($checkIfSpaceExists == "")
{
//No Space therefore it is not a name
}
else
{
//Contains space
$queryExploded = explode(" ", $query);
foreach ($queryExploded as $q)
{
//Here I need the functionality so that if someone entered John Doe
//2 different strings are saved, which are
//$string1 = John Doe
//$string2 = Doe Johns
//If the name consists of 3 parts, strings for every combination is saved
}
Then I will insert these strings in an SQL statement with the LIKE attribute and there will be a LIKE for both JOHN DOE and DOE JOHN. Hence, if the user can either enter John Doe or Doe John in order to find the result.
Any suggestions on how to do this?
Many thanks
chris
Ok, from the start - be sure to read the manual carefully. strpos doesn't do exactly what you think it's doing. Here's how you should check for a space:
if (strpos($query, ' ') === false) // the triple-equals is important!
After that, it's simply a matter of permutations and combinations. Here's another answer on Stack Overflow which shows you how to do it: algorithm that will take number or words and find all possible combinations
What about using these exploded 3 strings in separate AND-combined LIKE-constraints?
Something like
"... WHERE name LIKE '%$name[0]%' AND name LIKE '%$name[1]%' AND name LIKE '%$name[2]%'"
You could build this String in a foreach loop.
echo preg_replace('/^(\w+) (\w+)$/', '$2 $1', "John Doe");
Doe John
I guess you cannot split this field into name and surname? I suggest creating new table, tags in database. Tags will be any word - might be surname, may be name, may be ID number... Then make a connection record_tags with record_id and tag_id. Then the query will look like
SELECT record.* FROM record
INNER JOIN record_tags rt1 ON rt1.record_id = record.id
INNER JOIN tag t1 ON t1.id = rt1.tag_id
INNER JOIN record_tags rt2 ON rt2.record_id = record.id
INNER JOIN tag t2 ON t2.id = rt2.tag_id
WHERE
t1.name = "John"
AND t2.name = "Doe"
This will be better approach to searching, as you can then use any amount of words/tags. I think the SQL can be even easier. the multiple-like approach is I think much slower, especially as your database grows.
With some looping and string manipulation, I managed to implement this script.
Here is what I did:
I checked if the query contains a space using strpos. If it contains a space, then it means its a name with both name and surname so I enter a loop in order to output one string with 'name surname' and the other string with 'surname name'
Here is the code:
$like = ""; //This is the LIKE sql command.
foreach ($selectedFields as $field)
{
$checkIfSpaceExists = strpos($query," ");
if ($checkIfSpaceExists != "")
{
$query1 = $query; //No space, so query1 is equal ta original query
$queryExploded = explode(" ", $query);
for ($i=0; $i<count($queryExploded); $i++) //First loop (name surname)
{
$tmp1 = $tmp1 . " " . $queryExploded[$i];
}
for ($i=count($queryExploded); $i>=0; $i--) //Second loop (surname name)
{
$tmp2 = $tmp2 . " " . $queryExploded[$i];
}
$query2 = $tmp2;
$query2 = trim($query2);
$like = $like . $field . " LIKE '%" . $query1 . "%' or " . $field . " LIKE '%" . $query2 . "%' or ";
I hope this helps someone else in need :)

Categories