You can try excluding the tag name filter from your query and filter by tag name on the frontend app.
Query optimization
Hello, hope you are doing good!
I have more than 4500 businesses in my database. Each business hasMany tags. So I have my main search input in navbar. When user submits any string via that search input I want to display all businesses that contain this string in any of their tags or/and in it's name field.
Example: When string = ab. It must display first 20 businesses that contains string ab in any of their tag name or/and name field.
Result:
-
Name: NurabisTags: Alcoholic Beverages -
Name: BoirsTags: Mobile Phones, Mobile Phone Accessories, Tablets -
Name: BabilonTags: Cable TV, Mobile Network Operators - ......
This Eloquesnt/DB query was generated and executed
$businesses = Business::
->select('businesses.*')
->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
->orWhere("tags.{$this->lang}_name", 'LIKE', "%$str%")
->orWhere('businesses.name', 'LIKE', "%$str%")
->where('businesses.status', true)
->groupBy('businesses.id')
->with(['tags'])
->withCount(['reviews as rating' => function($query){
$query->select(DB::raw('round(avg(rating), 1)'));
}])
->withCount('reviews')
->with(['reviews' => function($query){
$query->latest();
}])
->with(['images' => function ($query)
{
$query->where('avatar', true);
}])
->paginate(20);
The problem is that it took 21.05s to execute. This is what my debugger shows. These are two queries that took most of the time:
6.24s
select count(*) as aggregate from `businesses`
left join `business_tag` on `businesses`.`id` = `business_tag`.`business_id`
left join `tags` on `business_tag`.`tag_id` = `tags`.`id`
where (`tags`.`en_name` LIKE '%ab%' or `businesses`.`name` LIKE '%ab%')
and `businesses`.`status` = 1 group by `businesses`.`id`
and 14.78s
select `businesses`.*,
(select round(avg(rating), 1) from `reviews`
where `businesses`.`id` = `reviews`.`business_id` and `status` = 1) as `rating`,
(select count(*) from `reviews` where `businesses`.`id` = `reviews`.`business_id`
and `status` = 1) as `reviews_count` from `businesses`
left join `business_tag` on `businesses`.`id` = `business_tag`.`business_id`
left join `tags` on `business_tag`.`tag_id` = `tags`.`id`
where (`tags`.`en_name` LIKE '%ab%' or `businesses`.`name` LIKE '%ab%')
and `businesses`.`status` = 1 group by `businesses`.`id` limit 20 offset 0
But when I comment that tag part like so
$businesses = Business::
->select('businesses.*')
// ->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
// ->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
// ->orWhere("tags.{$this->lang}_name", 'LIKE', "%$str%")
->orWhere('businesses.name', 'LIKE', "%$str%")
I takes 40.47ms to execute. And if I comment name part instead like so
$businesses = Business::
->select('businesses.*')
->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
->orWhere("tags.{$this->lang}_name", 'LIKE', "%$str%")
// ->orWhere('businesses.name', 'LIKE', "%$str%")
It takes 90.84ms
Now the question is: Is there any way to optimize this query? Thank you in advance! Have a nice day!
Please or to participate in this conversation.