Wordpress include post associated to specific term in search results - mysql

I'm working on a custom search engine for my WordPress site. The DB for the project is pretty big ( 300k+ posts ) so performance is key.
I have a searchbar returning posts ('clients' & 'professionnels') and i'm trying to limit search results to post title and terms name from 'specialites' taxonomy. So far i'm using a filter (https://stackoverflow.com/a/59537500/19181295).
function and_extend_search( $search, &$wp_query ) {
global $wpdb;
if ( empty( $search ))
return $search;
$terms = $wp_query->query_vars[ 's' ];
$exploded = explode( ' ', $terms );
if( $exploded === FALSE || count( $exploded ) == 0 )
$exploded = array( 0 => $terms );
$search = '';
foreach( $exploded as $tag ) {
$search .= " AND (
($wpdb->posts.post_title LIKE '%$tag%')
OR EXISTS
(
SELECT
*
FROM
$wpdb->term_relationships
LEFT JOIN
$wpdb->terms
ON
$wpdb->term_relationships.term_taxonomy_id = $wpdb->terms.term_id
WHERE
$wpdb->terms.name LIKE '%$tag%'
AND
$wpdb->term_relationships.object_id = $wpdb->posts.ID
)
)";
}
return $search;
}
add_filter('posts_search', 'and_extend_search', 500, 2);
The filter is pretty good but it's searching inside all taxonomies. What i want is keeping the filter but only return results in terms associated with 'specialites' taxonomy.
Can you guys help me please ? :)
from Comment
CREATE TABLE xmo_term_relationships (
object_id bigint(20) unsigned NOT NULL DEFAULT 0,
term_taxonomy_id bigint(20) unsigned NOT NULL DEFAULT 0,
term_order int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (object_id,term_taxonomy_id),
KEY term_taxonomy_id (term_taxonomy_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

If the tables are big and you want performance,
Do not use LIKE with leading wildcards
Do consider using FULLTEXT index(es).
Do install this plugin: WP Index Improvements
Get rid of the second if statement; $exploded will be as you want it anyway.
Try to avoid ORs; they do not Optimize well.
After you have done those, come back with some revised queries; I will probably have more suggestions. At that time, please provide SHOW CREATE TABLE and EXPLAIN.

Related

how can i get mysql to return an array that is inside JSON

im trying to return a product that can have three images per product.
something like this:
{
product_id: 1,
product_name:"test",
category_id_fk: 1,
product_price: 12,
product_desc:"lorem-ipsum",
product_images:[path1, path2, path3]
}
Here is what i have got so far,
create table product(
product_id serial not null primary key,
category_id_fk int not null references category(category_id) ,
product_image_fk int not null references product_images(product_images_id),
product_name varchar(50) not null,
product_price int not null,
product_desc varchar(200) not null);
create table product_images(
product_images_id serial not null primary key,
product_image_one varchar(150) not null,
product_image_two varchar(150) not null,
product_image_three varchar(150) not null);
this is the query im using for the product details page
SELECT * FROM product WHERE category_id_fk = ? AND product_id = ?;
Im new to SQL and have been really having a tough time with this specifically, would
appreciate it a lot if anyone could explain to me what im doing wrong or if you have any tips for me.
I don't know what programming language you are using, but in this case I assume you are using a programming language like PHP as a REST API provider because you are using prepared statements
Here is the query:
SELECT * FROM `product_images` `pi` LEFT JOIN `product` `p`
ON `pi`.`product_images_id` = `p`.`product_image_fk` WHERE
`p`.`product_id` = ? AND `p`.`category_id_fk` = ?
Then on your PHP scripts you can make it like this:
$sql = "SELECT * FROM `product_images` `pi` LEFT JOIN `product` `p`
ON `pi`.`product_images_id` = `p`.`product_image_fk` WHERE
`p`.`product_id` = ? AND `p`.`category_id_fk` = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $productId, $categoryId);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$jsonArray = [
"product_id" => $row["product_id"],
"product_name" => $row["product_name"],
"category_id_fk" => $row["category_id_fk"],
"product_price" => $row["product_price"],
"product_desc" => $row["product_desc"],
"product_images" => [$row["product_image_one"], $row["product_image_two"], $row["product_image_three"]]
];
$expectedJson = json_encode($jsonArray);
And you can do whatever you want to the json maybe by storing it on the json file or to print it as http response
SELECT JSON_OBJECT( 'product_id', p.product_id,
'product_name', p.product_name,
'category_id_fk', p.category_id_fk,
'product_price', p.product_price,
'product_desc', p.product_desc,
'product_images', JSON_ARRAY(pi.product_image_one,
pi.product_image_two,
pi.product_image_three) ) output
FROM product p
JOIN product_images pi ON p.product_image_fk = pi.product_images_id;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=16f3a1fe3619c6c3601983d4c00de07d

how to delete huge mysql rows depending on not equal

the problem that its a huge amount the query would be like
DELETE FROM `members` WHERE `membership` = 'C' AND (`id` != '1' AND `id` !='2' AND ...... thousands of ids );
how I can do that ?
I did also WHERE id NOT IN ("1","2"); but also did not work
can I use loop or something like that
the IDs that I want to keep and do not delete comes from another table contains a field holds the user ID that I don't want to delete I used PHP script to help me to generate the SQL query
like
<?php
require_once("inc.php");
$realty_uids = $db->query("SELECT `uid` from `realty2` ORDER BY `uid`");
$r = $db->fetch_assoc($realty_uids);
while($r = $db->fetch_assoc($realty_uids)){
$array[] = $r['uid'];
}
$input = array_unique($array);
echo "DELETE from `members` WHERE `membership` = 'C' ";
foreach($input as $key => $value){
echo " AND `id` != '".$value."' <br>";
}
echo ";";
DELETE FROM `members` WHERE `membership` = 'C' AND NOT EXISTS (SELECT at.uid FROM `realty2` at WHERE at.uid = `members`.id);
This way worked good thanks for every one tried :)

special mysql query from a joomla website

I have this context:
CREATE TABLE `atdees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`params` text NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `atdees` (`id`, `params`) VALUES
(1,'{"field282":"0","field347":"1"}'),
(2,'{"field282":"0","field347":"0"}'),
(3,'{"field282":"0"}');
I have to extract from the table the rows where :
an atdee must have the string '"field282":"0"'
an atdee has the string '"field282":"0"' but not the string '"field347":"0"'
an atdee has both string '"field282":"0"' and '"field347":"0"'
In other words I have to extract the Id 2 and 3.
Thank you.
Ps: Sorry for my english, I am not a native speaker ;)
edit: well i found my query
SELECT id
FROM atdees
WHERE
INSTR(`params`, '"field282":"0"') > 0 and
( params LIKE '%"field347":"0"%' OR
INSTR(`params`, '"field347"') = 0 )
If it's simply getting data from the database, then you can use something like this:
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName('id'));
$query->from($db->quoteName('#__atdees'));
$query->where($db->quoteName('params') . " = " . $db->quote('"field282":"0"') . "OR" . $db->quote('"field347":"0"'));
$db->setQuery($query);
$results = $db->loadObjectList();
foreach ( $results as $result ) {
echo "<p>" . $result->id . "</p>";
}
Not sure if the database table is for a Joomla extensions but if so, keep it as #__atdees in your query, else change to atdees
Hope this helps

MySQL query optimization in codeigniter

I have function in CodeIgniter to retrieve latest posts from 2 tables:
public function get_latest_comments($amount)
{
$query = $this->db->query('
SELECT *, FROM_UNIXTIME(date) AS timestamp
FROM comments
ORDER BY timestamp DESC
LIMIT 5
');
if ($query->num_rows() > 0) {
$result = $query->result_array();
for ($i = 0; $i < sizeof( $result ); $i++) {
$result[$i]['author_info'] = $this->comments_model->get_comment_author( $result[$i]['id'] );
$result[$i]['date'] = mdate( "%M %m, %Y", $result[$i]['date'] );
if ($result[$i]['section'] === 'blog') $loc = 'blog_posts';
if ($result[$i]['section'] === 'core') $loc = 'public_posts';
$this->db->select( 'title, slug' );
$query = $this->db->get_where( $loc, array('id' => $result[$i]['location']) );
$result[$i]['post_title'] = $query->row( 'title' );
$result[$i]['url'] = base_url() . $result[$i]['section'] . '/view/' . $query->row( 'slug' ) . '/';
}
return $result;
}
return false;
}
The problem is that it runs too slow. My page sometimes loads 7-8 seconds. I suspect this query running 2 times + similar query gathering latest comments slows down my page.
I have a bad feeling about queries inside the loop. How can I avoid that?
The structure of my table is:
users (id, username, mail ...
user_info ( user_id, name, surname
public_posts ( id, slug, text, author(user id) ...
blog_posts ( id, slug, text ...
comments ( id, text, author, location(post_id_, section(post_table) ...
Check by expalining your Query , Go to the mysql command line and type
EXPLAIN SELECT *, FROM_UNIXTIME(date) AS timestamp FROM comments ORDER BY timestamp DESC LIMIT 5
Explain will tell you everything about the query, On its bases you can decide for the indexing also.
Make a practice to expalin every select query before using it in the code.
Plus you can also do profiling when ever you think your code is taking time. In codeigniter Profiler class is available, please go through the below link.
https://www.codeigniter.com/userguide3/general/profiling.html

Query 2 tables with one field linked to 2 different values

I'm trying to make a SQL query and I have some problems with it.
CREATE table entries (
id_entry INT PRIMARY KEY,
);
CREATE table entry_date (
entry_date_id INT PRIMARY KEY,
entry_id INT,
entry_price INT,
entry_date TEXT,
);
for each entry, there is several dates.
I'd like to select the entries.entry_id where that entry have, for example, the dates '23/03/2013' and '24/03/2013' linked to it.
The two dates are stored into an array:
$data = array('ci' => '23/03/2013', 'co' => '24/03/2013');
I store the dates in text for practical purpose in my treatment.
I use Zend_Db so my query is constructed like that:
$select = $table->select ()->from ( 'entries' )->setIntegrityCheck ( false );
if ($data ['ci'] != NULL) {
$select->join ( array (
'entry_dates' => 'entry_dates'
), 'entries.id_entry = entry_dates.entry_id' );
$select->where ( 'entry_dates.entry_date = ?', $data ['ci'] );
}
if ($data ['co']) {
if ($data['ci'] == NULL) {
$select->join ( array (
'entry_dates' => 'entry_dates'
), 'entries.id_entry = entry_dates.entry_id' );}
$select->where ( 'entry_dates.entry_date = ?', $data ['co'] );
}
which gives :
SELECT `entries`.*, `entry_date`.*
FROM `entries`
INNER JOIN `entry_dates`
ON entries.id_entry = entry_dates.entry_id
WHERE (entry_dates.entry_date = '23/03/2013')
AND (entry_dates.entry_date = '24/03/2013')
And, well ... It doesn't work.
When I fetch my $select, I get nothing.
I guess I miss something in my request when I do the WHERE ... AND , what should I do to get the correct output ? The real request being really long, I'd like to avoid another long subselect if possible.
It can be done in two way, either with a self-join on the entry_date table:
SELECT `entries`.entry_id
FROM `entries`
INNER JOIN `entry_dates` AS ed1
ON entries.id_entry = ed1.entry_id
INNER JOIN `entry_dates` AS ed2
ON entries.id_entry = ed2.entry_id
WHERE ed1.entry_date = '23/03/2013'
AND ed2.entry_date = '24/03/2013'
Or with an aggregate
SELECT `entries`.entry_id
FROM `entries`
INNER JOIN `entry_dates` AS ed
WHERE ed.entry_date = '23/03/2013'
OR ed2.entry_date = '24/03/2013'
GROUP BY `entries`.entry_id
HAVING COUNT(DISTINCT ed.entry_date)=2