diff --git a/README.md b/README.md index 664f8f0..0bb2cb0 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,14 @@ $searchQuery->sumSortBy('field', 'asc'); $searchQuery->medianSortBy('field', 'asc'); ``` +### Pinned query + +Promotes selected documents to rank higher than those matching a given query. This feature is typically used to guide searchers to curated documents that are promoted over and above any "organic" matches for a search. The promoted or "pinned" documents are identified using the document IDs stored in the _id field. + +```php +$searchQuery->pinned(['doc-3', 'doc-1', 'doc-2']); +``` + ### Pagination #### Offset Pagination diff --git a/src/Concerns/DecoratesBoolQuery.php b/src/Concerns/DecoratesBoolQuery.php index 3f130ae..ab0754c 100644 --- a/src/Concerns/DecoratesBoolQuery.php +++ b/src/Concerns/DecoratesBoolQuery.php @@ -172,4 +172,11 @@ public function addFunctionScore(array $functions, ?DSLAware $query = null, ?Fun return $this; } + + public function pinned(array $ids, ?DSLAware $query = null): static + { + $this->forwardCallTo($this->boolQuery(), __FUNCTION__, func_get_args()); + + return $this; + } } diff --git a/src/Contracts/BoolQuery.php b/src/Contracts/BoolQuery.php index 6cd991c..6029468 100644 --- a/src/Contracts/BoolQuery.php +++ b/src/Contracts/BoolQuery.php @@ -53,4 +53,6 @@ public function whereBetween(string $field, mixed $from, mixed $to): static; * @param ?FunctionScoreOptions $options */ public function addFunctionScore(array $functions, ?DSLAware $query = null, ?FunctionScoreOptions $options = null): static; + + public function pinned(array $ids, ?DSLAware $query = null): static; } diff --git a/src/Filtering/BoolQueryBuilder.php b/src/Filtering/BoolQueryBuilder.php index 4932ee0..d848506 100644 --- a/src/Filtering/BoolQueryBuilder.php +++ b/src/Filtering/BoolQueryBuilder.php @@ -21,6 +21,7 @@ use Ensi\LaravelElasticQuery\Filtering\Criterias\MultiMatch; use Ensi\LaravelElasticQuery\Filtering\Criterias\Nested; use Ensi\LaravelElasticQuery\Filtering\Criterias\OneMatch; +use Ensi\LaravelElasticQuery\Filtering\Criterias\Pinned; use Ensi\LaravelElasticQuery\Filtering\Criterias\RangeBound; use Ensi\LaravelElasticQuery\Filtering\Criterias\Term; use Ensi\LaravelElasticQuery\Filtering\Criterias\Terms; @@ -279,6 +280,13 @@ public function addFunctionScore(array $functions, ?DSLAware $query = null, ?Fun return $this; } + public function pinned(array $ids, ?DSLAware $query = null): static + { + $this->must->add(new Pinned($ids, $query)); + + return $this; + } + public function addMustBool(callable $fn): static { $this->must->add(static::make(builder: $fn)); diff --git a/src/Filtering/Criterias/Pinned.php b/src/Filtering/Criterias/Pinned.php new file mode 100644 index 0000000..e009696 --- /dev/null +++ b/src/Filtering/Criterias/Pinned.php @@ -0,0 +1,32 @@ + $this->ids, + 'organic' => ['match_all' => new stdClass()], + ]; + + if ($this->query) { + $body['organic'] = $this->query->toDSL(); + } + + return ['pinned' => $body]; + } +} diff --git a/src/Search/SearchQuery.php b/src/Search/SearchQuery.php index ec879b0..c13e4ec 100644 --- a/src/Search/SearchQuery.php +++ b/src/Search/SearchQuery.php @@ -40,7 +40,6 @@ class SearchQuery implements SortableQuery, CollapsibleQuery, HighlightingQuery protected ?int $from = null; protected array $fields = []; protected array $include = []; - protected ?array $pinnedIds = null; protected array $exclude = []; protected ?string $searchType = null; @@ -150,19 +149,12 @@ protected function execute( $dsl = [ 'size' => $size, 'from' => $from, - 'query' => [], + 'query' => $this->boolQuery->toDSL(), 'track_total_hits' => $totals, '_source' => $this->sourceToDSL($source), 'fields' => $source && $this->fields ? $this->fields : null, ]; - if ($this->pinnedIds) { - $dsl['query']['pinned']['ids'] = $this->pinnedIds; - $dsl['query']['pinned']['organic'] = $this->boolQuery->toDSL(); - } else { - $dsl['query'] = $this->boolQuery->toDSL(); - } - $sorts ??= $this->sorts; if (!$sorts->isEmpty()) { $dsl['sort'] = $sorts->toDSL(); @@ -253,13 +245,6 @@ public function setPostFilter(BoolQueryBuilder $boolQueryBuilder): static return $this; } - public function pinned(array $ids): static - { - $this->pinnedIds = $ids; - - return $this; - } - public function addAggregations(Aggregation $aggregation): static { $this->aggregations ??= new AggregationCollection();