Filtering with multiple categories, and doing an OR search

Hi all,

I’m new to Algolia, and currently struggling to implement Algolia to achieve what I’m trying to do. I’m also unsure if my Model has been set up properly.

I have 2 problems at the moment:

  1. My model has multiple categories. The categories should be assigned to the model by a many-to-many relationship (max 3 categories per model). I have 3 subcategories per model too but I’ll get to that issue when I figure out the categories bit.

I’ve done something like this (sample category ids):

public function toSearchableArray(): array
{
    $array = $this->toArray();
    $array = $this->transform($array);
    $array['state_id'] = $this->state->id;
    $array['region_id'] = $this->region->id;
    $array['categories'] = [
        1,
        2,
        3,
    ];
}

When importing, the records will appear instead with:

category_1: 1,
category_2: 2,
category_3: 3,

I was hoping that it would appear as configured in the array. As I’m doing a keyword + category search like:

$query = Listing::search($keywords)
        ->whereIn('categories', [1]) // this line won't work for sure, this is what i'm trying to figure out
        ->get();

When using Eloquent, I implement this by doing


$query->whereHas('categories', function($subquery) use($input_categories) {
    $subquery->whereIn('category_id', $input_categories);
});

  1. You’ll notice a region_id and state_id in the Model too. I’m trying to implement something that’s similar to an orWhere in Eloquent. Similar to:
$query = Listing::search($keywords)
        ->where('state_id', $state_id)
        ->orWhere('region_id', $region_id)
        ->get();

The results should return records that have the selected state ID, or the selected region ID. At the moment, I receive is this error:

Method Algolia\ScoutExtended\Builder::orWhere does not exist.

It looks like there’s no way to do an OR search? Or is there a better way to implement what I’m doing?

To ensure that the categories appear as configured in the array when importing, you need to adjust the structure of the $array['categories'] assignment. Instead of assigning individual category IDs directly, you can structure the array to represent the desired format. Here’s an example of how you can modify your code:

public function toSearchableArray(): array
{
$array = $this->toArray();
$array = $this->transform($array);
$array[‘state_id’] = $this->state->id;
$array[‘region_id’] = $this->region->id;
$array[‘categories’] = [
[
‘id’ => 1,
],
[
‘id’ => 2,
],
[
‘id’ => 3,
],
];

return $array;

}

In this modified version, instead of assigning the category IDs directly, you create an array for each category with an 'id' key. By structuring the array in this way, the resulting imported records should reflect the desired format:

category_1: [
‘id’ => 1,
],
category_2: [
‘id’ => 2,
],
category_3: [
‘id’ => 3,
],

It’s been a while. I’m happy to report that I was able to fix this some time ago.

For my first problem, what I did was make use of hierarchical facets. I mentioned I have 3 categories and subcategories per model. I thought this would be an issue but I found that lvl0 of the hierarchical facets can also be an array. This is how it looks:

public function toSearchableArray(): array
{
    ...
        // categories
        $array['categories']['lvl0'] = [
            'Category 1',
            'Category 2',
            'Category 3',
        ];

        // subcategories
        $array['categories']['lvl1'] = [
            'Category 1 > Subcategory 1',
            'Category 2 > Subcategory 2',
            'Category 3 > Subcategory 3',
        ];
    ...
}

So to be able to do a search using the said categories, I needed to use the ‘filters’ option. It wasn’t straightforward for me to figure out exactly how to do this in Laravel but I eventually got it.


$keywords = 'random keywords';
$query = Listing::search($keywords, function ($algolia, $keywords)  {

    $options = [
        "filters" => "(categories.lvl0: 'Category Name')",
    ];

    // searching for a subcategory would be
    // (categories.lvl1: 'Category Name > Subcategory Name')

    return $algolia->search($keywords, $options);
})->get();

For my second problem, it was mainly about doing an OR search. I found that to achieve this, I shouldn’t use Eloquent. Instead, it had to be processed via the same ‘filters’ option.

(Note that instead of using a separate state_id and region_id like previously, I also combined them into a hierarchical facet and named it ‘location’)

Below is how I got it to work. (Also includes an ‘AND’ which I needed):

$keywords = 'random keywords';
$query = Listing::search($keywords, function ($algolia, $keywords) use ($page)  {

    // side note, pagination has to be processed in the subquery
    $options = [
        "filters" => "(location.lvl0: 'State Name 1' " .
            "OR location.lvl1: 'State Name 2 > Region Name 1' " .
            "OR location.lvl1: 'State Name 3 > Region Name 2')" .
            "AND (categories.lvl0: 'Category Name')",
        "page" => $page - 1,
    ];

    return $algolia->search($keywords, $options);
})->paginate(10);

Now I’m not sure if these are the ideal implementations for my questions. But this is how I got them to work. Hopefully this will be of assistance for others!