3

I have this code to search for a matching result in a MYSQL database:

$where[] = 'p.id IN (
    SELECT adcfvc.advert_id
      FROM #__koparent_advert_specific_fields_values AS adcfvc
     WHERE adcfvc.advert_id = p.id
       AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
       AND ' . $db->Quote(JString::strtolower($sf_value)) . ' = adcfvc.field_value
)';

I want to change the above search query from selecting exact match using the equal sign "=" operator to selecting any matching result using the "LIKE" operator with two wildcards "%adcfvc.field_value%".

In other words: the current role of the above code is that when the user search for "Hello my people" the query will search for the exact word. However, I want the user to be able to search using the word "Hello" or "people" only, and he get all results including "Hello my people".

Knowing that I cannot change any of the database structure in anyway, just modify the above code.

The entire code file called "query.php" is available at: http://123dizajn.com/boltours/stackex/query.txt I couldn't paste the whole code here as it exceeded body limits, and it was renamed to query.txt just to be viewable.


Trial#1

So, I tried to just replace (at the very end of the code):

= adcfvc.field_value

With:

LIKE %adcfvc.field_value%

with no success :(


Trial#2

I tried to reverse the lookup order and use multiple logical operators:-

$where[] = 'p.id IN (
    SELECT adcfvc.advert_id
      FROM #__koparent_advert_specific_fields_values AS adcfvc
     WHERE adcfvc.advert_id = p.id
       AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
       AND
         (adcfvc.field_value > ' . $db->Quote(JString::strtolower($sf_value)) . '
         OR adcfvc.field_value < ' . $db->Quote(JString::strtolower($sf_value)) . '
         OR adcfvc.field_value = ' . $db->Quote(JString::strtolower($sf_value)) . ')
)';

But this returns all items, not the searched ones!


Trial#3

I also tried to reverse and use the LIKE %...%:-

$where[] = 'p.id IN (
    SELECT adcfvc.advert_id
      FROM #__koparent_advert_specific_fields_values AS adcfvc
     WHERE adcfvc.advert_id = p.id
       AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
       AND adcfvc.field_value LIKE %' . $db->Quote(JString::strtolower($sf_value)) . '%
)';

But this returns an error:

1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%'apartment'%) GROUP BY p.id ORDER BY ap.price DESC LIMIT 0, 20' at line 12 SQL=SELECT p., p.id AS id, p.title AS title, p.street_num, p.street, p.description as description,ap.price, pr.name as priceName,usr.id as advert_user_id,countries.title as country_name,states.title as state_name,date_format(p.created, '%Y-%m-%d') as fcreated,c.title as category_title, c.id as category_id FROM b1yvu_koparent AS p LEFT JOIN b1yvu_koparent_advert_prices AS ap ON ap.advertId = p.id AND ap.advertDateRangeGroupId = 0 AND ap.priceId = p.priceUnitId LEFT JOIN b1yvu_koparent_prices AS pr ON pr.id = p.priceUnitId LEFT JOIN b1yvu_koparent_advertmid AS pm ON pm.advert_id = p.id LEFT JOIN b1yvu_koparent_usermid AS am ON am.advert_id = p.id LEFT JOIN b1yvu_koparent_users AS usr ON usr.id = am.user_id LEFT JOIN b1yvu_koparent_categories AS c ON c.id = pm.cat_id LEFT JOIN b1yvu_koparent_advert_specific_fields_values AS asfv ON asfv.advert_id = p.id LEFT JOIN b1yvu_koparent_countries AS countries ON countries.id = p.country LEFT JOIN b1yvu_koparent_states AS states ON states.id = p.locstate WHERE p.published = 1 AND p.approved = 1 AND c.published = 1 AND (p.publish_up = '0000-00-00' OR p.publish_up <= '2015-09-05') AND (p.publish_down = '0000-00-00' OR p.publish_down >= '2015-09-05') AND (c.publish_up = '0000-00-00' OR c.publish_up <= '2015-09-05') AND (c.publish_down = '0000-00-00' OR c.publish_down >= '2015-09-05') AND p.access IN (1,9) AND c.access IN (1,9) AND c.language IN ('en-GB','') AND p.language IN ('en-GB','*') AND p.id IN (SELECT adcfvc.advert_id FROM b1yvu_koparent_advert_specific_fields_values AS adcfvc WHERE adcfvc.advert_id = p.id AND adcfvc.field_name = 't4_cust_AdvertTitleEN' AND adcfvc.field_value LIKE %'apartment'%) GROUP BY p.id ORDER BY ap.price DESC LIMIT 0, 20

Any help or suggestions appreciated.

Cœur
  • 37,241
  • 25
  • 195
  • 267
MHDSKY
  • 39
  • 7
  • 1
    Think you've got that backwards, try `adcfvc.field_value LIKE "%' . $db->Quote(JString::strtolower($sf_value)) . '%"` – swornabsent Sep 04 '15 at 15:28
  • have you tried to add single quotes around the string? LIKE '%adcfvc.field_value%' You'll have to escape the single quotes in the php code. – mk97 Sep 04 '15 at 15:32
  • @swornabsent Well, the page was successfully loaded, but the suggested query did not function at all. – MHDSKY Sep 04 '15 at 15:34
  • @mk97 I do not know how to "escape the single quotes in the php code". Would you please tell me what to try directly? – MHDSKY Sep 04 '15 at 15:38
  • http://stackoverflow.com/questions/7999148/escaping-quotation-marks-in-php – mk97 Sep 04 '15 at 15:47
  • @mk97 Wow, that a long article to study! Can you please just tell me what to replace directly? Thank you. – MHDSKY Sep 04 '15 at 16:00
  • It would really be great to show what you are getting (an error or the wrong results). Also to use `$query->dump()` to show the generated query which you can then test directly. – Elin Sep 04 '15 at 18:56
  • Also I'm really not getting this `$where[] ` What is that doing in the middle of a JDatabaseQuery? You don't need to worry about escaping if you use `quote()` and `quoteName()` correctly. – Elin Sep 04 '15 at 19:02
  • `'%'apartment'%` is one reason why you are having a problem. You have included the single quotes in the search. – Elin Sep 05 '15 at 16:36

3 Answers3

2

First of all it's quote not Quote. Second you should use quoteName() for names of fields. Third there's no reason to stop using the API just because you have a subquery. Also your code is very confusing about which is the field name and which is the value. I assume that $sf_value represents the value that you are trying to match and adcfvc.field is the name of the field that stores the data you are trying to match.

Replace

 AND ' . $db->Quote(JString::strtolower($sf_value)) . ' = adcfvc.field_value

with

 AND ' .  $db->quoteName( 'adcfvc.field_value' ) . ' LIKE  ' .  $db->quote('%' . JString::strtolower($sf_value) . '%') 

I'm not sure why you are using JString there but if you feel it's necessary then fine.

Here is your subquery

SELECT adcfvc.advert_id
  FROM #__koparent_advert_specific_fields_values AS adcfvc
 WHERE adcfvc.advert_id = p.id
   AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
   AND ' . $db->Quote(JString::strtolower($sf_value)) . ' = adcfvc.field_value

So you since you have $db already.

$subquery = $db->getQuery(true); 
// Assuming p.id is an integer
$subquery->where($db->quoteName(adcfvc.advert_id) = p.id)
//Assuming $sf_key is an integer
->where($db->quoteName(adcfvc.field_name) . ' = ' . $sf_key)
->where($db->quoteName(adcfvc.field_value) . ' LIKE ' 
    .   $db->Quote('%'. JString::strtolower($sf_value) . '%')) ;

Then in the top level query, which you have just shown us one part of, something like

$query->where('p.id IN (' . $subquery . ')' );
Elin
  • 6,507
  • 3
  • 25
  • 47
  • This piece of code is part of a very big component working on Joomla 3.4. However, you can check the output at 123dizajn.com/boltours/en/contact and use the search field on the top left. To understand what I mean, search for "Katarina 1 Apartments A2" and you will get a result, but if you search for only "Katarina" you will not get any results! Although your assumption is right, I also tried your suggestion but it gives a blank page! Finally, I know this piece of code I posted is bit confusing. Let me know what you think, then I'll update it and let you check again. Help appreciated. – MHDSKY Sep 04 '15 at 19:45
  • Turn error reporting to development. Why don't you just make a finder plugin? Add `echo $query->dump();` after you finish building the query. Are you positive that your query works in PHPMyAdmin? – Elin Sep 04 '15 at 21:13
  • Also please update the question to show your whole query starting with defining `$db`. – Elin Sep 04 '15 at 21:18
  • You should read this link about how to do subqueries with the Joomla api http://joomla.stackexchange.com/questions/1/method-for-creating-a-subquery-using-jdatabase – Elin Sep 04 '15 at 21:21
  • I did not try to execute the same code in phpMyAdmin, but it works very fine on the functionality side, my problem is that it gives exact match, and I want a wildcard match "%...%". I've added a link for the entire file as requested. Help appreciated. – MHDSKY Sep 05 '15 at 05:13
  • I have updated the question with my trials and their returns, please check if the returned ones can be fixed. – MHDSKY Sep 05 '15 at 07:09
  • If it isnt returnig what you expect it is not working fine. – Elin Sep 05 '15 at 16:37
0

You can reverse the like logic on your column using concat

SELECT adcfvc.advert_id
FROM #__koparent_advert_specific_fields_values AS adcfvc
WHERE adcfvc.advert_id = p.id
AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
AND 'Hello my people'  like concat('%',adcfvc.field_value,'%')
order by adcfvc.field_value like '%Hello my people%' desc

This will match (Hello my people,Hello,my and people) added order by to show complete match first

DEMO

Another way if you can break you text by words you can use or conditions to match with field_value

SELECT adcfvc.advert_id
FROM #__koparent_advert_specific_fields_values AS adcfvc
WHERE adcfvc.advert_id = p.id
AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
AND  (
  adcfvc.field_value like '%Hello my people%'
  or adcfvc.field_value like '%Hello%'
  or adcfvc.field_value like '%my%'
  or adcfvc.field_value like '%people%'
)
order by adcfvc.field_value like '%Hello my people%' desc

DEMO 2

M Khalid Junaid
  • 63,861
  • 10
  • 90
  • 118
  • Please note that I only mentioned "Hello my people" as an example. I believe your solution might work if you can make it generic using only the give code (i.e. without any fixed text). – MHDSKY Sep 04 '15 at 15:45
  • @MHDSKY yes i have used this phrase "Hello my people" for example it can be anything value of php var `$sf_value` – M Khalid Junaid Sep 04 '15 at 15:48
  • @M Khalid Junaid I tried to replace `'%Hello my people%'` with `'%$sf_value%'` with no success, the page loaded empty white :( If possible, please post the exact code to try, maybe I mis replaced it. – MHDSKY Sep 04 '15 at 15:55
  • try something like `AND ' . $db->Quote('%'.JString::strtolower($sf_value).'%') . ' LIKE ....` you are not manipulating variables correctly in your query – M Khalid Junaid Sep 04 '15 at 16:02
  • @M Khalid Junaid Basically the original code I posted works fine, but only returns exact matches as perviously mentioned. I understand that you might want to suggest a better improved structure, totally welcome. Can you please edit your answer and tell me what to replace exactly? This will be much easier for me to understand and apply. – MHDSKY Sep 04 '15 at 16:13
  • @M Khalid Junaid I managed to try the suggested `AND ' . $db->Quote('%'.JString::strtolower($sf_value).'%') . ' LIKE ....`. Although the page loads, the search returns nothing at all! - Any more suggestions please? – MHDSKY Sep 04 '15 at 16:42
  • @MHDSKY can you create a sample fiddle demo with sample data set ? and also echo your generated query see whats query is generated it matches with the suggested one ? or not – M Khalid Junaid Sep 04 '15 at 16:54
  • @M Khalid Junaid Unfortunately, I am not familiar with SQLFIDDLE, in addition being this piece of code part of a very big component working on Joomla 3.4. However, you can check the output at http://www.123dizajn.com/boltours/en/contact and use the search field on the top left. To understand what I mean, search for "Katarina 1 Apartments A2" and you will get a result, but if you search for only "Katarina" you will not get any results! Finally, I know this piece of code I posted controls the situation, let me know what you think, then I'll update it and let you check again. Help appreciated. – MHDSKY Sep 04 '15 at 17:13
  • I have updated the question with your suggestions as trials and their returns, please check if the returned ones can be fixed. Also it worth mentioning that I second your opinion to _reverse the like logic_ as I feel it is more closer to be resolved that way. – MHDSKY Sep 05 '15 at 07:13
0

Using RLIKE instead of the approached LIKE was the most simple and direct solution for my question.

Final full query:-

$where[] = 'p.id IN (
    SELECT adcfvc.advert_id
      FROM #__koparent_advert_specific_fields_values AS adcfvc
     WHERE adcfvc.advert_id = p.id
       AND adcfvc.field_name = ' . $db->Quote($sf_key) . '
       AND adcfvc.field_value RLIKE ' . $db->Quote(JString::strtolower($sf_value)) . '
)';

This made the results return all items that their name included the searched value.

MHDSKY
  • 39
  • 7
  • @MKhalidJunaid Your suggestion to __reverse logic lookup__ was very helpful to solve this question; yet being fresh border couldn't allow me to vote it up. – MHDSKY Sep 05 '15 at 08:50
  • @mk97 @Elin Your answers and comments also helped me very well. But you couldn't think of `RLIKE` though ;p - Thanks. – MHDSKY Sep 05 '15 at 08:51
  • @Elin Have you seen my resolution? It resolved the problem without any further modifications. – MHDSKY Sep 05 '15 at 17:00