Skip to content

Instantly share code, notes, and snippets.

@Sekaiichi
Created July 29, 2022 12:15
Show Gist options
  • Save Sekaiichi/dbf2743f80f2d5286bda7d75d55f6299 to your computer and use it in GitHub Desktop.
Save Sekaiichi/dbf2743f80f2d5286bda7d75d55f6299 to your computer and use it in GitHub Desktop.
<?php
namespace App\Core\Products;
use App\DTO\Product\ProductGetDTO;
use App\Models\Product;
use App\Models\ProductDisplayData;
use Elastic\Elasticsearch\Client;
use Elastic\Elasticsearch\Response\Elasticsearch;
use Http\Promise\Promise;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Arr;
use Illuminate\Database\Eloquent\Collection;
class ElasticsearchRepository implements ProductsRepository
{
public function __construct(public Client $elasticsearch)
{}
public function search(ProductGetDTO $productGetDTO): Collection
{
$items = $this->searchOnElasticsearch($productGetDTO, 0);
return $this->buildLightCollection($items);
}
public function pagination(ProductGetDTO $productGetDTO): LengthAwarePaginator
{
$items = $this->searchOnElasticsearch($productGetDTO, 11);
$buildCollection = $this->buildCollection($items, $productGetDTO);
return new LengthAwarePaginator(
$buildCollection,
$items['hits']['total']['value'],
$productGetDTO->getLimit(),
Paginator::resolveCurrentPage(),
['path' => Paginator::resolveCurrentPath()]
);
}
private function searchOnElasticsearch(ProductGetDTO $productGetDTO, int $minScore): Elasticsearch|Promise
{
$from = ($productGetDTO->getPage() - 1) * $productGetDTO->getLimit();
$model = new ProductDisplayData();
$searchArray = [
'size' => $productGetDTO->getLimit(),
'from' => $from,
'index' => $model->getSearchIndex(),
'type' => $model->getSearchType(),
'body' => [
'query' => [
'bool' => [
'filter' => [
[
'term' => [
'city_id' => $productGetDTO->getCityId()
]
]
]
]
],
],
];
if ($productGetDTO->getSearch()) {
$searchArray['body']['query']['bool']['must'] = [
'multi_match' => [
'fields' => ['name', 'slug'],
'query' => $productGetDTO->getSearch(),
'fuzziness' => 'AUTO'
],
];
$searchArray['body']['min_score'] = $minScore;
}
if ($productGetDTO->getSortType()) {
$searchArray['body']['sort'][] = match ($productGetDTO->getSortType()) {
'asc_by_price' => ['min_price' => 'asc'],
'desc_by_price' => ['min_price' => ['order' => 'desc']],
'desc_by_popularity' => ['popularity' => 'desc'],
'desc_by_date' => ['created_at' => 'desc'],
};
$searchArray['body']['sort'][] = "_score";
}
return $this->elasticsearch->search($searchArray);
}
private function buildLightCollection($items): Collection
{
$productIds = $this->getProductIds($items);
return Product::findMany($productIds)
->sortBy(function ($product) use ($productIds) {
return array_search($product->getKey(), $productIds, true);
});
}
private function buildCollection($items, ProductGetDTO $productGetDTO)
{
$productIds = $this->getProductIds($items);
return Product::select([
'products.id as id',
'products.name as name',
'products.slug as slug',
'product_display_data.display_price as display_price',
'product_display_data.shop_condition_duration as shop_condition_duration',
'product_display_data.shop_condition_commission_client as shop_condition_commission_client',
'product_display_data.shop_condition_id as shop_condition_id',
'product_images.url as title_image'
])->leftJoin('product_display_data', function ($join) {
$join->on('product_display_data.product_id', '=', 'products.id');
})->leftJoin('product_images', function ($join) {
$join->on('product_images.product_id', '=', 'products.id');
})->where('product_images.is_default', true)
->where('product_display_data.city_id', $productGetDTO->getCityId())
->findMany($productIds)->sortBy(function ($product) use ($productIds) {
return array_search($product->getKey(), $productIds, true);
});
}
private function getProductIds($items): array
{
return Arr::pluck($items['hits']['hits'], '_source.product_id');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment