algolia / scout-extended

Scout Extended: The Full Power of Algolia in Laravel
https://www.algolia.com/doc/framework-integration/laravel/getting-started/introduction-to-scout-extended/
MIT License
397 stars 84 forks source link

Implement builder helper for optional filters #284

Closed ksntcrq closed 3 years ago

ksntcrq commented 3 years ago

Adding optional filters to the query can now be done with the whereOptional builder helper. It is still also possible to use with.

Q A
Bug fix? no
New feature? yes
BC breaks? no
Related Issue no
Need Doc update yes

Describe your change

A new whereOptional builder helper is introduced to add optional filters to a search.

What problem is this fixing?

Using the with helper to send optional parameters can be tedious when you have multiple of them - especially if they're conditional. With that Pull Request, it is now possible to use whereOptional for optional filters just like where can be used for filters. It also removes the necessity to build strings in the specific format Algolia expects, as it is done under the hood by the whereOptional implementation.

Here is an example:

// Before

$optionalFilters = collect("email.provider:gmail");

if (!empty($city)) {
    $optionalFilters->push("city.code:{$city->code}");
}

if (!empty($job)) {
    $optionalFilters->push("job.title:{$job->title}");
}

User::search('killian')
    ->with([
        'optionalFilters' => $optionalFilters->join(',')
    ])
    ->get();

// After

User::search('killian')
    ->whereOptional('email.provider', 'gmail')
    ->when(!empty($city), fn ($query) => $query->whereOptional('city.name', $city->name))
    ->when(!empty($job), fn ($query) => $query->whereOptional('job.title', $job->title))
    ->get()

Implementation choice

The order in which with and whereOptional are used matters. The last used will always erase the optional filters set by the other one (see unit tests). I think this is an acceptable pitfall as you should only use one or the other, but the implementation could be smarter and always override the optional filters with what's provided in with, for instance.

ksntcrq commented 3 years ago

Hey @DevinCodes, thanks for your comment!

I was wondering, do you think there's a way to make it possible to merge the optionalFilters when you use with and whereOptional calls? As a user, I would probably expect them all to be merged independent of the order in which I'd call them. Love to hear your thoughts on this.

Let's say you read the following piece of code:

User::search('killian')
    ->whereOptional('email.provider', 'gmail')
    ->with([
        'optionalFilters' => "job.title:{$job->title}",
    ])
    ->get()

Don't you expect with to set the optional filters, thus overriding anything passed in whereOptional? To me, it's a bit of the same as doing:

User::search('killian')
    ->with([
        'optionalFilters' => "email.provider:gmail",
    ])
    ->with([
        'optionalFilters' => "job.title:{$job->title}",
    ])
    ->get()

And, in the current implementation, the second with will override the first one - it won't merge both optional filters.


However, if you still feel like with and whereOptional should be used together, let me know and I'll be more than happy to implement it!

ksntcrq commented 3 years ago

Thanks @DevinCodes!

Will you guys update the documentation or should I do something about it?

DevinCodes commented 3 years ago

@ksntcrq I'll update the documentation next week, nothing to do on your side 🙂

DevinCodes commented 3 years ago

This is released in v1.20.0. Thank you again! 🎉