Skip to content

Commit 62469cd

Browse files
committed
minShould added into filter
recommendation endpoint request improvement and filter min_should implementation. GeoPolygon request model implementation improved recommendation endpoint
1 parent 70f0a5a commit 62469cd

File tree

11 files changed

+529
-28
lines changed

11 files changed

+529
-28
lines changed

src/Endpoints/Collections/Points.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212

1313
use Qdrant\Endpoints\AbstractEndpoint;
1414
use Qdrant\Endpoints\Collections\Points\Payload;
15+
use Qdrant\Endpoints\Collections\Points\Recommend;
1516
use Qdrant\Exception\InvalidArgumentException;
1617
use Qdrant\Models\Filter\Filter;
1718
use Qdrant\Models\PointsStruct;
1819
use Qdrant\Models\Request\PointsBatch;
19-
use Qdrant\Models\Request\RecommendRequest;
2020
use Qdrant\Models\Request\ScrollRequest;
2121
use Qdrant\Models\Request\SearchRequest;
2222
use Qdrant\Response;
@@ -28,6 +28,11 @@ public function payload(): Payload
2828
return (new Payload($this->client))->setCollectionName($this->collectionName);
2929
}
3030

31+
public function recommend(): Recommend
32+
{
33+
return (new Recommend($this->client))->setCollectionName($this->collectionName);
34+
}
35+
3136
/**
3237
* @throws InvalidArgumentException
3338
*/
@@ -180,18 +185,4 @@ public function batch(PointsBatch $points, array $queryParams = []): Response
180185
)
181186
);
182187
}
183-
184-
/**
185-
* @throws InvalidArgumentException
186-
*/
187-
public function recommend(RecommendRequest $recommendParams): Response
188-
{
189-
return $this->client->execute(
190-
$this->createRequest(
191-
'POST',
192-
'collections/' . $this->collectionName . '/points/recommend',
193-
$recommendParams->toArray()
194-
)
195-
);
196-
}
197188
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Payload
4+
*
5+
* @since Mar 2023
6+
* @author Haydar KULEKCI <haydarkulekci@gmail.com>
7+
*/
8+
9+
namespace Qdrant\Endpoints\Collections\Points;
10+
11+
use Qdrant\Endpoints\AbstractEndpoint;
12+
use Qdrant\Exception\InvalidArgumentException;
13+
use Qdrant\Models\Request\Points\BatchRecommendRequest;
14+
use Qdrant\Models\Request\Points\GroupRecommendRequest;
15+
use Qdrant\Models\Request\Points\RecommendRequest;
16+
use Qdrant\Response;
17+
18+
class Recommend extends AbstractEndpoint
19+
{
20+
/**
21+
* Retrieves points that are closer to stored positive examples and further from negative examples.
22+
*
23+
* @throws InvalidArgumentException
24+
*/
25+
public function recommend(RecommendRequest $request, array $queryParams = []): Response
26+
{
27+
return $this->client->execute(
28+
$this->createRequest(
29+
'POST',
30+
'/collections/' . $this->getCollectionName() . '/points/recommend' . $this->queryBuild($queryParams),
31+
$request->toArray()
32+
)
33+
);
34+
}
35+
36+
/**
37+
* Retrieves points in batches that are closer to stored positive examples and further from negative examples.
38+
*
39+
* @param BatchRecommendRequest $request
40+
* @param array $queryParams
41+
* @return Response
42+
*/
43+
public function batch(BatchRecommendRequest $request, array $queryParams = []): Response
44+
{
45+
46+
return $this->client->execute(
47+
$this->createRequest(
48+
'POST',
49+
'/collections/' . $this->getCollectionName() . '/points/recommend/batch' . $this->queryBuild($queryParams),
50+
$request->toArray()
51+
)
52+
);
53+
}
54+
55+
/**
56+
* @throws InvalidArgumentException
57+
*/
58+
public function groups($request, array $queryParams = []): Response
59+
{
60+
throw new \RuntimeException('Not implemented on client!');
61+
}
62+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* @since May 2023
4+
* @author Haydar KULEKCI <haydarkulekci@gmail.com>
5+
*/
6+
7+
namespace Qdrant\Models\Filter\Condition;
8+
9+
use Qdrant\Exception\InvalidArgumentException;
10+
use Qdrant\Domain\Assert;
11+
12+
class GeoPolygon extends AbstractCondition implements ConditionInterface
13+
{
14+
public function __construct(string $key, protected array $exterior, protected ?array $interiors = null)
15+
{
16+
parent::__construct($key);
17+
18+
if (empty($this->exterior)) {
19+
throw new InvalidArgumentException('Exteriors required!');
20+
}
21+
22+
foreach ($this->exterior as $point) {
23+
Assert::keysExists($point, ['lat', 'lon'], 'Each point of polygon needs lat and lon parameters');
24+
}
25+
if ($interiors) {
26+
foreach ($this->interiors as $point) {
27+
Assert::keysExists($point, ['lat', 'lon'], 'Each point of polygon needs lat and lon parameters');
28+
}
29+
}
30+
}
31+
32+
public function toArray(): array
33+
{
34+
return [
35+
'key' => $this->key,
36+
'geo_polygon' => [
37+
'exterior' => $this->exterior,
38+
'interiors' => $this->interiors ?? []
39+
]
40+
];
41+
}
42+
}

src/Models/Filter/Filter.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class Filter implements ConditionInterface
1313
protected array $must = [];
1414
protected array $must_not = [];
1515
protected array $should = [];
16+
protected array $minShould = [];
17+
protected ?int $minShouldCount;
1618

1719
public function addMust(ConditionInterface $condition): Filter
1820
{
@@ -35,6 +37,20 @@ public function addShould(ConditionInterface $condition): Filter
3537
return $this;
3638
}
3739

40+
public function addMinShould(ConditionInterface $condition): Filter
41+
{
42+
$this->minShould[] = $condition;
43+
44+
return $this;
45+
}
46+
47+
public function setMinShouldCount(int $count): Filter
48+
{
49+
$this->minShouldCount = $count;
50+
51+
return $this;
52+
}
53+
3854
public function toArray(): array
3955
{
4056
$filter = [];
@@ -59,6 +75,16 @@ public function toArray(): array
5975
$filter['should'][] = $should->toArray();
6076
}
6177
}
78+
if ($this->minShould && $this->minShouldCount) {
79+
$filter['min_should'] = [
80+
'conditions' => [],
81+
'min_count' => $this->minShouldCount
82+
];
83+
foreach ($this->minShould as $should) {
84+
/** ConditionInterface $must */
85+
$filter['min_should']['conditions'][] = $should->toArray();
86+
}
87+
}
6288

6389
return $filter;
6490
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* RecommendRequest
4+
*
5+
* @since Jun 2023
6+
* @author Greg Priday <greg@siteorigin.com>
7+
*/
8+
namespace Qdrant\Models\Request\Points;
9+
10+
use Qdrant\Exception\InvalidArgumentException;
11+
use Qdrant\Models\Filter\Filter;
12+
use Qdrant\Models\Traits\ProtectedPropertyAccessor;
13+
14+
class BatchRecommendRequest
15+
{
16+
use ProtectedPropertyAccessor;
17+
18+
/** @var RecommendRequest[] $searches */
19+
protected array $searches = [];
20+
21+
/**
22+
* @param RecommendRequest[] $searches
23+
*/
24+
public function __construct(array $searches)
25+
{
26+
foreach ($searches as $search) {
27+
$this->addSearch($search);
28+
}
29+
}
30+
31+
public function addSearch(RecommendRequest $request): static
32+
{
33+
$this->searches[] = $request;
34+
35+
return $this;
36+
}
37+
38+
public function toArray(): array
39+
{
40+
$searches = [];
41+
42+
foreach ($this->searches as $search) {
43+
$searches[] = $search->toArray();
44+
}
45+
46+
return [
47+
'searches' => $searches
48+
];
49+
}
50+
}

src/Models/Request/RecommendRequest.php renamed to src/Models/Request/Points/RecommendRequest.php

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,31 @@
55
* @since Jun 2023
66
* @author Greg Priday <greg@siteorigin.com>
77
*/
8-
namespace Qdrant\Models\Request;
8+
namespace Qdrant\Models\Request\Points;
99

10+
use Qdrant\Exception\InvalidArgumentException;
1011
use Qdrant\Models\Filter\Filter;
1112
use Qdrant\Models\Traits\ProtectedPropertyAccessor;
1213

1314
class RecommendRequest
1415
{
1516
use ProtectedPropertyAccessor;
1617

18+
/**
19+
* average_vector - Average positive and negative vectors and create a single query with the formula
20+
* query = avg_pos + avg_pos - avg_neg. Then performs normal search.
21+
*/
22+
const STRATEGY_AVERAGE_VECTOR = 'average_vector';
23+
24+
/**
25+
* best_score - Uses custom search objective. Each candidate is compared against all examples, its
26+
* score is then chosen from the max(max_pos_score, max_neg_score). If the max_neg_score is chosen
27+
* then it is squared and negated, otherwise it is just the max_pos_score.
28+
*/
29+
const STRATEGY_BEST_SCORE = 'best_score';
30+
31+
protected ?string $shardKey = null;
32+
protected ?string $strategy = null;
1733
protected ?Filter $filter = null;
1834
protected ?string $using = null;
1935
protected ?int $limit = null;
@@ -31,6 +47,27 @@ public function setFilter(Filter $filter): static
3147
return $this;
3248
}
3349

50+
public function setShardKey(string $shardKey): static
51+
{
52+
$this->shardKey = $shardKey;
53+
54+
return $this;
55+
}
56+
57+
public function setStrategy(string $strategy): static
58+
{
59+
$strategies = [
60+
self::STRATEGY_AVERAGE_VECTOR,
61+
self::STRATEGY_BEST_SCORE,
62+
];
63+
if (!in_array($strategy, $strategies)) {
64+
throw new InvalidArgumentException('Invalid strategy for recommendation.');
65+
}
66+
$this->strategy = $strategy;
67+
68+
return $this;
69+
}
70+
3471
public function setScoreThreshold(float $scoreThreshold): static
3572
{
3673
$this->scoreThreshold = $scoreThreshold;
@@ -66,19 +103,25 @@ public function toArray(): array
66103
'negative' => $this->negative,
67104
];
68105

106+
if ($this->shardKey !== null) {
107+
$body['shard_key'] = $this->shardKey;
108+
}
69109
if ($this->filter !== null && $this->filter->toArray()) {
70110
$body['filter'] = $this->filter->toArray();
71111
}
72-
if($this->scoreThreshold) {
112+
if($this->scoreThreshold !== null) {
73113
$body['score_threshold'] = $this->scoreThreshold;
74114
}
75-
if ($this->using) {
115+
if ($this->using !== null) {
76116
$body['using'] = $this->using;
77117
}
78-
if ($this->limit) {
118+
if ($this->limit !== null) {
79119
$body['limit'] = $this->limit;
80120
}
81-
if ($this->offset) {
121+
if ($this->strategy !== null) {
122+
$body['strategy'] = $this->strategy;
123+
}
124+
if ($this->offset !== null) {
82125
$body['offset'] = $this->offset;
83126
}
84127

0 commit comments

Comments
 (0)