mirror of
https://github.com/jikan-me/jikan-rest.git
synced 2025-02-20 11:23:35 +08:00
fixed #332
- added logic to forward `order_by` parameter to the search engine - modified mongodb query when no search engine is present to use "textMatchScore" name instead of "score" for text match scores -- the text score projection was shadowing the "score" attribute
This commit is contained in:
parent
651ae88616
commit
4aa3c4c617
@ -14,7 +14,7 @@ class WhereClause extends BaseClause
|
||||
|
||||
protected function validate($value): bool
|
||||
{
|
||||
return !is_null($value);
|
||||
return !in_array(null, (array)$value);
|
||||
}
|
||||
|
||||
private function orWhere($query, $filter, $values): Builder
|
||||
|
@ -4,6 +4,7 @@ namespace App\Http\QueryBuilder;
|
||||
|
||||
use App\Http\QueryBuilder\Traits\PaginationParameterResolver;
|
||||
use App\Services\ScoutSearchService;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
@ -70,7 +71,24 @@ abstract class SearchQueryBuilder implements SearchQueryBuilderService
|
||||
$q = $requestParameters->get("q");
|
||||
|
||||
if ($this->isSearchIndexUsed() && !empty($q)) {
|
||||
$builder = $this->scoutSearchService->search($modelClass, $q);
|
||||
$orderBy = $requestParameters->get("order_by");
|
||||
$sort = $requestParameters->get("sort");
|
||||
$searchOptions = [];
|
||||
|
||||
// todo: validate whether the specified field exists on the model
|
||||
if (!empty($orderBy)) {
|
||||
$searchOptions["order_by"] = $orderBy;
|
||||
} else {
|
||||
$searchOptions["order_by"] = null;
|
||||
}
|
||||
|
||||
if (!empty($sort) && in_array($sort, ["asc", "desc"])) {
|
||||
$searchOptions["sort_direction_descending"] = $sort == "desc";
|
||||
} else {
|
||||
$searchOptions["sort_direction_descending"] = false;
|
||||
}
|
||||
$builder = $this->scoutSearchService->search($modelClass, $q, $searchOptions["order_by"],
|
||||
$searchOptions["sort_direction_descending"]);
|
||||
} else {
|
||||
// If "q" is not set, OR search indexes are disabled, we just get a query builder for the model.
|
||||
// This way we can have a single place where we get the query builder from.
|
||||
@ -104,11 +122,11 @@ abstract class SearchQueryBuilder implements SearchQueryBuilderService
|
||||
'$search' => $q
|
||||
],
|
||||
], [
|
||||
'score' => [
|
||||
'textMatchScore' => [
|
||||
'$meta' => 'textScore'
|
||||
]
|
||||
])
|
||||
->orderBy('score', ['$meta' => 'textScore']);
|
||||
->orderBy('textMatchScore', 'desc');
|
||||
}
|
||||
|
||||
// The ->filter() call is a local model scope function, which applies filters based on the query string
|
||||
@ -184,7 +202,7 @@ abstract class SearchQueryBuilder implements SearchQueryBuilderService
|
||||
];
|
||||
}
|
||||
|
||||
public function paginateBuilder(Request $request, \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $results): \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
public function paginateBuilder(Request $request, \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder $results): LengthAwarePaginator
|
||||
{
|
||||
['limit' => $limit, 'page' => $page] = $this->getPaginateParameters($request);
|
||||
|
||||
@ -197,6 +215,9 @@ abstract class SearchQueryBuilder implements SearchQueryBuilderService
|
||||
// In that method the "$limit" member variable is being check whether it's null or it has value.
|
||||
// If it's set to a number then the result set will be limited which we do the pagination on.
|
||||
// If it's set to null, then the pagination will be done on the whole result set.
|
||||
/**
|
||||
* @var LengthAwarePaginator $paginated
|
||||
*/
|
||||
$paginated = $scoutBuilder
|
||||
->jikanPaginate(
|
||||
$limit,
|
||||
|
@ -6,7 +6,8 @@ use App\Helpers\Guards;
|
||||
|
||||
class DefaultScoutSearchService implements ScoutSearchService
|
||||
{
|
||||
public function search(object|string $modelClass, string $q): \Laravel\Scout\Builder
|
||||
public function search(object|string $modelClass, string $q, ?string $orderByField = null,
|
||||
bool $sortDirectionDescending = false): \Laravel\Scout\Builder
|
||||
{
|
||||
Guards::shouldBeMongoDbModel($modelClass);
|
||||
|
||||
|
@ -16,23 +16,28 @@ class ElasticScoutSearchService implements ScoutSearchService
|
||||
* @throws \Elastic\Elasticsearch\Exception\ClientResponseException
|
||||
* @throws \Elastic\Elasticsearch\Exception\MissingParameterException
|
||||
*/
|
||||
public function search(object|string $modelClass, string $q): \Laravel\Scout\Builder
|
||||
public function search(object|string $modelClass, string $q, ?string $orderByField = null,
|
||||
bool $sortDirectionDescending = false): \Laravel\Scout\Builder
|
||||
{
|
||||
return $modelClass::search($q, function(\Elastic\ElasticSearch\Client $client, \ONGR\ElasticsearchDSL\Search $body) use ($modelClass) {
|
||||
return $modelClass::search($q, function(\Elastic\ElasticSearch\Client $client, \ONGR\ElasticsearchDSL\Search $body) use ($modelClass, $orderByField, $sortDirectionDescending) {
|
||||
$modelInstance = new $modelClass;
|
||||
|
||||
if ($modelInstance instanceof JikanApiSearchableModel) {
|
||||
// if the model specifies search index sort order, use it
|
||||
$sortByFields = $modelInstance->getSearchIndexSortBy();
|
||||
if (!is_null($sortByFields)) {
|
||||
foreach ($sortByFields as $f) {
|
||||
$direction = match ($f['direction']) {
|
||||
'asc' => FieldSort::ASC,
|
||||
'desc' => FieldSort::DESC,
|
||||
};
|
||||
if (!is_null($orderByField)) {
|
||||
$body->addSort(new FieldSort($orderByField, ['order' => $sortDirectionDescending ? FieldSort::DESC : FieldSort::ASC]));
|
||||
} else {
|
||||
// if the model specifies search index sort order, use it
|
||||
$sortByFields = $modelInstance->getSearchIndexSortBy();
|
||||
if (!is_null($sortByFields)) {
|
||||
foreach ($sortByFields as $f) {
|
||||
$direction = match ($f['direction']) {
|
||||
'asc' => FieldSort::ASC,
|
||||
'desc' => FieldSort::DESC,
|
||||
};
|
||||
|
||||
$sort = new FieldSort($f['field'], ['order' => $direction]);
|
||||
$body->addSort($sort);
|
||||
$sort = new FieldSort($f['field'], ['order' => $direction]);
|
||||
$body->addSort($sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,18 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Laravel\Scout\Builder;
|
||||
|
||||
interface ScoutSearchService
|
||||
{
|
||||
/**
|
||||
* Executes a search operation via Laravel Scout on the provided model class.
|
||||
* @param object|string $modelClass
|
||||
* @param string $q
|
||||
* @return \Laravel\Scout\Builder
|
||||
* @param string|null $orderByField
|
||||
* @param bool $sortDirectionDescending
|
||||
* @return Builder
|
||||
*/
|
||||
public function search(object|string $modelClass, string $q): \Laravel\Scout\Builder;
|
||||
public function search(object|string $modelClass, string $q, ?string $orderByField = null,
|
||||
bool $sortDirectionDescending = false): \Laravel\Scout\Builder;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Services;
|
||||
|
||||
use App\JikanApiSearchableModel;
|
||||
use Illuminate\Support\Str;
|
||||
use Typesense\Documents;
|
||||
|
||||
class TypeSenseScoutSearchService implements ScoutSearchService
|
||||
@ -25,9 +26,10 @@ class TypeSenseScoutSearchService implements ScoutSearchService
|
||||
* @throws \Http\Client\Exception
|
||||
* @throws \Typesense\Exceptions\TypesenseClientError
|
||||
*/
|
||||
public function search(object|string $modelClass, string $q): \Laravel\Scout\Builder
|
||||
public function search(object|string $modelClass, string $q, ?string $orderByField = null,
|
||||
bool $sortDirectionDescending = false): \Laravel\Scout\Builder
|
||||
{
|
||||
return $modelClass::search($q, function (Documents $documents, string $query, array $options) use ($modelClass) {
|
||||
return $modelClass::search($q, function (Documents $documents, string $query, array $options) use ($modelClass, $orderByField, $sortDirectionDescending) {
|
||||
// let's enable exhaustive search
|
||||
// which will make Typesense consider all variations of prefixes and typo corrections of the words
|
||||
// in the query exhaustively, without stopping early when enough results are found.
|
||||
@ -57,6 +59,16 @@ class TypeSenseScoutSearchService implements ScoutSearchService
|
||||
$sortBy = rtrim($sortBy, ',');
|
||||
$options['sort_by'] = $sortBy;
|
||||
}
|
||||
|
||||
// override ordering field
|
||||
if (!is_null($orderByField)) {
|
||||
$options['sort_by'] = "_text_match:desc,$orderByField:" . ($sortDirectionDescending ? "desc" : "asc");
|
||||
}
|
||||
|
||||
// override overall sorting direction
|
||||
if (is_null($orderByField) && $sortDirectionDescending && array_key_exists("sort_by", $options) && Str::contains($options["sort_by"], "asc")) {
|
||||
$options["sort_by"] = Str::replace("asc", "desc", $options["sort_by"]);
|
||||
}
|
||||
}
|
||||
|
||||
return $documents->search($options);
|
||||
|
Loading…
x
Reference in New Issue
Block a user