Skip to content

Commit

Permalink
Refactor multi request
Browse files Browse the repository at this point in the history
  • Loading branch information
alecritson committed Nov 5, 2024
1 parent 6aa615b commit b2bd74d
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 116 deletions.
19 changes: 19 additions & 0 deletions src/Data/Builder/SearchQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Lunar\Search\Data\Builder;

use Lunar\Search\Data\SearchFacet\FacetValue;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\MapName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;

#[MapName(SnakeCaseMapper::class)]
class SearchQuery extends Data
{
public function __construct(
public ?string $query = '',
public array $facets = [],
public array $facetFilters = []
) {}
}
60 changes: 60 additions & 0 deletions src/Engines/AbstractEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Lunar\Search\Engines;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Lunar\Models\Product;
use Lunar\Search\Data\Builder\SearchQuery;

abstract class AbstractEngine
{
Expand Down Expand Up @@ -111,5 +113,63 @@ protected function getFacetConfig(?string $field = null): ?array
return config('lunar.search.facets.'.$this->modelType, [])[$field] ?? [];
}

protected function getSearchQueries(): Collection
{
$facets = $this->getFacetConfig();

$queries = [
SearchQuery::from([
'query' => $this->query,
'facets' => array_keys($facets),
'facet_filters' => $this->facets,
])
];

foreach ($this->facets as $facetField => $facetFilterValues) {
$queries[] = SearchQuery::from([
'query' => $this->query,
'facets' => [$facetField],
'facet_filters' => collect($this->facets)->reject(
fn ($value, $field) => $field === $facetField
)->toArray()
]);
}

return collect($queries);
}

protected function sortByIsValid(): bool
{
$sort = $this->sort;

if (! $sort) {
return true;
}

$parts = explode(':', $sort);

if (! isset($parts[1])) {
return false;
}

if (! in_array($parts[1], ['asc', 'desc'])) {
return false;
}

$config = $this->getFieldConfig();

if (empty($config)) {
return false;
}

$field = collect($config)->first(
fn ($field) => $field['name'] == $parts[0]
);

return $field && ($field['sort'] ?? false);
}

abstract public function get(): mixed;

abstract protected function getFieldConfig(): array;
}
5 changes: 5 additions & 0 deletions src/Engines/DatabaseEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ public function get(): mixed
'facets' => collect(),
]);
}

protected function getFieldConfig(): array
{
return [];
}
}
5 changes: 5 additions & 0 deletions src/Engines/MeilisearchEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ protected function mapFilter(string $field, mixed $value): string

return '('.$field.' = "'.$values->first().'")';
}

protected function getFieldConfig(): array
{
return [];
}
}
180 changes: 64 additions & 116 deletions src/Engines/TypesenseEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use Laravel\Scout\EngineManager;
use Laravel\Scout\Scout;
use Lunar\Search\Data\SearchFacet;
use Lunar\Search\Data\SearchHit;
use Lunar\Search\Data\SearchResults;
Expand All @@ -20,28 +19,44 @@ public function get(): SearchResults
$paginator = $this->getRawResults(function (Documents $documents, string $query, array $options) {
$engine = app(EngineManager::class)->engine('typesense');

$searchRequests = [
'searches' => [
$this->buildSearchOptions($options, $query),
$this->buildSearchOptions($options, $query, useFacetFilters: false)
]
$request = [
'searches' => $this->buildSearch(
$options
)
];

$response = $engine->getMultiSearch()->perform($searchRequests, [
$response = $engine->getMultiSearch()->perform($request, [
'collection' => (new $this->modelType)->searchableAs(),
]);

$completeResults = $response['results'][0];

unset( $response['results'][0]);
$otherResults = $response['results'];

$facets = collect($completeResults['facet_counts'])->mapWithKeys(
fn ($facets) => [$facets['field_name'] => $facets]
);

foreach ($otherResults as $result) {
foreach ($result['facet_counts'] as $facet) {
$facets->put($facet['field_name'], $facet);
}
}

return [
...$response['results'][0],
'unfaceted_response' => $response['results'][1],
...$completeResults,
'facet_counts' => $facets->toArray()
];
});


} catch (\GuzzleHttp\Exception\ConnectException|ServiceUnavailable $e) {
Log::error($e->getMessage());
$paginator = new LengthAwarePaginator(
items: [
'hits' => [],
'facet_counts' => [],
],
total: 0,
perPage: $this->perPage,
Expand All @@ -62,38 +77,7 @@ public function get(): SearchResults
'document' => $hit['document'],
]));

// $facets = [];
// $hierarchyFacets = [];
//
// foreach ($preResults['facet_counts'] ?? [] as $facet) {
// $facetConfig = $this->getFacetConfig($facet['field_name']);
//
// $nested = count(explode('.', $facet['field_name'])) > 1;
//
// if ($nested) {
// $nestedField = explode('.', $facet['field_name'])[0];
//
// $hierarchyFacets[$nestedField][] = $facet;
//
// continue;
// }
//
// $facets[] = SearchFacet::from([
// 'label' => $this->getFacetConfig($facet['field_name'])['label'] ?? '',
// 'field' => $facet['field_name'],
// 'hierarchy' => $nested,
// 'values' => collect($facet['counts'])->map(
// fn ($value) => SearchFacet\FacetValue::from([
// 'label' => $value['value'],
// 'value' => $value['value'],
// 'count' => $value['count'],
// ])
// ),
// ]);
// }


$facets = collect($paginator['unfaceted_response']['facet_counts'] ?? [])->map(
$facets = collect($results['facet_counts'] ?? [])->map(
fn ($facet) => SearchFacet::from([
'label' => $this->getFacetConfig($facet['field_name'])['label'] ?? '',
'field' => $facet['field_name'],
Expand Down Expand Up @@ -126,7 +110,7 @@ public function get(): SearchResults

$newPaginator = clone $paginator;

$data = [
return SearchResults::from([
'query' => $this->query,
'total_pages' => $paginator->lastPage(),
'page' => $paginator->currentPage(),
Expand All @@ -139,33 +123,43 @@ public function get(): SearchResults
)->appends([
'facets' => http_build_query($this->facets),
])->links(),
];

return SearchResults::from($data);
]);
}

protected function buildSearchOptions(array $options, string $query, $useFacetFilters = true): array
protected function buildSearch(array $options): array
{
$filters = collect($options['filter_by']);
$facets = $this->getFacetConfig();
$searchQueries = $this->getSearchQueries();

$facetQuery = collect();
$requests = [];

foreach ($facets as $facetConfig) {
if (empty($facetConfig['facet_query'])) {
continue;
}
$facetQuery->push($facetConfig['facet_query']);
}
$facets = $this->getFacetConfig();

$facetQuery = $facetQuery->join(',');
$filters = collect($options['filter_by']);

foreach ($this->filters as $key => $value) {
$filters->push($key.':'.collect($value)->join(','));
}

if ($useFacetFilters) {
foreach ($this->facets as $field => $values) {
foreach ($searchQueries as $searchQuery) {

$filters = collect();

$facetQuery = collect();

$facetConfig = collect($facets)->filter(
fn ($facet, $field) => in_array($field, $searchQuery->facets)
);

foreach ($facetConfig as $facetConfigValue) {
if (empty($facetConfigValue['facet_query'])) {
continue;
}
$facetQuery->push($facetConfigValue['facet_query']);
}

$facetQuery = $facetQuery->join(',');

foreach ($searchQuery->facetFilters as $field => $values) {
$values = collect($values)->map(function ($value) {
if ($value == 'false' || $value == 'true') {
return $value;
Expand All @@ -182,70 +176,24 @@ protected function buildSearchOptions(array $options, string $query, $useFacetFi

$filters->push($field.':'.collect($values)->join(','));
}
}

$options['q'] = $query;
$options['facet_query'] = $facetQuery;
$facets = $this->getFacetConfig();
$facetBy = array_keys($facets);

foreach ($facets as $field => $config) {
if (!($config['hierarchy'] ?? false)) {
continue;
}
unset(
$facetBy[array_search($field, $facetBy)]
);
$facetBy = [
...$facetBy,
...array_map(
fn ($value) => "{$field}.{$value}",
$config['levels'] ?? []
)
$params = [
...$options,
'q' => $searchQuery->query,
'facet_query' => $facetQuery,
'max_facet_values' => 50,
'sort_by' => $this->sortByIsValid() ? $this->sort : '',
'facet_by' => implode(',', $searchQuery->facets),
];
}

$options['facet_by'] = implode(',', $facetBy);
$options['max_facet_values'] = 50;

$options['sort_by'] = $this->sortByIsValid() ? $this->sort : '';

if ($filters->count()) {
$options['filter_by'] = $filters->join(' && ');
}

return $options;
}

protected function sortByIsValid(): bool
{
$sort = $this->sort;

if (! $sort) {
return true;
}

$parts = explode(':', $sort);

if (! isset($parts[1])) {
return false;
}

if (! in_array($parts[1], ['asc', 'desc'])) {
return false;
}

$config = $this->getFieldConfig();
if ($filters->count()) {
$params['filter_by'] = $filters->join(' && ');
}

if (empty($config)) {
return false;
$requests[] = $params;
}

$field = collect($config)->first(
fn ($field) => $field['name'] == $parts[0]
);

return $field && ($field['sort'] ?? false);
return $requests;
}

protected function getFieldConfig(): array
Expand Down

0 comments on commit b2bd74d

Please sign in to comment.