I'm trying to limit the size of my database by avoiding multiple records containing the same words and therefore I have build two tables used to relate keyword to rows in other tables.
As an example, we use the follwing search input: "find my"
What is wish go get as a hit, is the product_name from product_id = 106. Could I just search on the product name? No, because the product can be associated with other keywords, that are not part of the product_name.
TABLE: ec_products
product_id | product_name | is_deleted
---------------------------------------------
106 | some product | 0
TABLE: ec_keywords_index
word_id | keyword
---------------------------------------------
55 | find
61 | my
77 | product
TABLE: ec_keyword_relations
word_id | application_id | related_id
------------------------------------------------
55 | 1 | 106
61 | 1 | 106
77 | 1 | 106
So, what I want now when I enter "find my" into my search input is:
Here is what I've tried so far (and I know that I am very far from a working solution at the time being):
$searchInput = 'find my';
$keywords = explode(' ', $searchInput);
$keyword_count = count($keywords);
$n = 1;
$query = '';
foreach($keywords as $keyword) {
if ($n !== $keyword_count) {
$query_ext = ' AND ';
} else {
$query_ext = '';
}
$query .= "(ec_keywords_index.keyword LIKE '%$keyword%')" . $query_ext;
++$n;
}
$query = "
SELECT
ec_products.product_name
FROM
ec_products
LEFT JOIN ec_search_word_relations
ON ec_keyword_relations.related_id = ec_products.product_id
LEFT JOIN ec_keywords_index
ON ec_keywords_index.word_id = ec_keyword_relations.word_id
WHERE
($query)
AND
ec_products.is_deleted = '0'
AND
ec_keyword_relations.application_id = '1'
GROUP BY
ec_products.product_id
ORDER BY
ec_products.product_name ASC
LIMIT
100";
$stmt = $mysqli->prepare($query);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($name);
while ($stmt->fetch()) {
echo $name;
}
UPDATE:
After some fiddling around, I have come up with this solution which works just as expected. Is it (in terms) of performance a good or a bad solution?
$searchInput = 'find my';
$keywords = explode(' ', $searchInput);
$keyword_count = count($keywords);
$n = 1;
$where_query = '';
$having_query = '';
foreach($keywords as $keyword) {
if ($n != $keyword_count) {
$w_query_ext = ' OR ';
$h_query_ext = ' AND ';
} else {
$w_query_ext = '';
$h_query_ext = '';
}
$where_query .= "ec_keywords_index.keyword LIKE '%" . $keyword . "%'" . $w_query_ext;
$having_query .= "related_keywords LIKE '%" . $keyword . "%'" . $h_query_ext;
++$n;
}
$query = "
SELECT
ec_products.product_name,
GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'
FROM
ec_products,
ec_keywords_index
LEFT JOIN
ec_keyword_relations
ON ec_keyword_relations.word_id = ec_keywords_index.word_id
WHERE ($where_query)
AND ec_products.product_id = ec_keyword_relations.related_id
AND ec_keyword_relations.application_id = '1'
AND ec_products.is_deleted = '0'
GROUP BY
ec_products.product_name
HAVING
($having_query)
LIMIT
100";
$stmt = $mysqli->prepare($query);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($name);
while ($stmt->fetch()) {
echo $name;
}
The above example outputs a query like this:
SELECT
ec_products.product_name,
GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'
FROM
ec_products,
ec_keywords_index
LEFT JOIN
ec_keyword_relations
ON ec_keyword_relations.word_id = ec_keywords_index.word_id
WHERE (ec_keywords_index.keyword LIKE '%find%' OR ec_keywords_index.keyword LIKE '%my%')
AND ec_products.product_id = ec_keyword_relations.related_id
AND ec_keyword_relations.application_id = '1'
AND ec_products.is_deleted = '0'
GROUP BY
ec_products.product_name
HAVING
(related_keywords LIKE '%find%' AND related_keywords LIKE '%my%')
LIMIT
100
I'm using GROUP_CONCAT to create a column containing all the keywords related to the products, then my WHERE clause is used to ONLY select the keywords that are actually found in my search string... Then the HAVING clause is used to search in column build using GROUP_CONCAT.
OWN ANSWER WITH EXAMPLE:
After some fiddling around, I have come up with this solution which works just as expected. I don't know if it is best practice, but it works like a charm.
$searchInput = 'find my';
$keywords = explode(' ', $searchInput);
$keyword_count = count($keywords);
$n = 1;
$where_query = '';
$having_query = '';
foreach($keywords as $keyword) {
if ($n != $keyword_count) {
$w_query_ext = ' OR ';
$h_query_ext = ' AND ';
} else {
$w_query_ext = '';
$h_query_ext = '';
}
$where_query .= "ec_keywords_index.keyword LIKE '%" . $keyword . "%'" . $w_query_ext;
$having_query .= "related_keywords LIKE '%" . $keyword . "%'" . $h_query_ext;
++$n;
}
$query = "
SELECT
ec_products.product_name,
GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'
FROM
ec_products,
ec_keywords_index
LEFT JOIN
ec_keyword_relations
ON ec_keyword_relations.word_id = ec_keywords_index.word_id
WHERE ($where_query)
AND ec_products.product_id = ec_keyword_relations.related_id
AND ec_keyword_relations.application_id = '1'
AND ec_products.is_deleted = '0'
GROUP BY
ec_products.product_name
HAVING
($having_query)
LIMIT
100";
$stmt = $mysqli->prepare($query);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($name);
while ($stmt->fetch()) {
echo $name;
}
The above example outputs a query like this:
SELECT
ec_products.product_name,
GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'
FROM
ec_products,
ec_keywords_index
LEFT JOIN
ec_keyword_relations
ON ec_keyword_relations.word_id = ec_keywords_index.word_id
WHERE (ec_keywords_index.keyword LIKE '%find%' OR ec_keywords_index.keyword LIKE '%my%')
AND ec_products.product_id = ec_keyword_relations.related_id
AND ec_keyword_relations.application_id = '1'
AND ec_products.is_deleted = '0'
GROUP BY
ec_products.product_name
HAVING
(related_keywords LIKE '%find%' AND related_keywords LIKE '%my%')
LIMIT
100
I'm using GROUP_CONCAT to create a column containing all the keywords related to the products, then my WHERE clause is used to ONLY select the keywords that are actually found in my search string... Then the HAVING clause is used to search in column build using GROUP_CONCAT.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.