Skip to content

Commit 6c953e5

Browse files
authored
Merge pull request #757 from utopia-php/fix/vector-queries
Fix vector methods(count, upsert, sum)
2 parents f2d01b6 + d4e3876 commit 6c953e5

File tree

3 files changed

+178
-5
lines changed

3 files changed

+178
-5
lines changed

src/Database/Adapter/SQL.php

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3188,7 +3188,18 @@ public function count(Document $collection, array $queries = [], ?int $max = nul
31883188

31893189
$queries = array_map(fn ($query) => clone $query, $queries);
31903190

3191-
$conditions = $this->getSQLConditions($queries, $binds);
3191+
// Extract vector queries (used for ORDER BY) and keep non-vector for WHERE
3192+
$vectorQueries = [];
3193+
$otherQueries = [];
3194+
foreach ($queries as $query) {
3195+
if (in_array($query->getMethod(), Query::VECTOR_TYPES)) {
3196+
$vectorQueries[] = $query;
3197+
} else {
3198+
$otherQueries[] = $query;
3199+
}
3200+
}
3201+
3202+
$conditions = $this->getSQLConditions($otherQueries, $binds);
31923203
if (!empty($conditions)) {
31933204
$where[] = $conditions;
31943205
}
@@ -3206,12 +3217,23 @@ public function count(Document $collection, array $queries = [], ?int $max = nul
32063217
? 'WHERE ' . \implode(' AND ', $where)
32073218
: '';
32083219

3220+
// Add vector distance calculations to ORDER BY (similarity-aware LIMIT)
3221+
$vectorOrders = [];
3222+
foreach ($vectorQueries as $query) {
3223+
$vectorOrder = $this->getVectorDistanceOrder($query, $binds, $alias);
3224+
if ($vectorOrder) {
3225+
$vectorOrders[] = $vectorOrder;
3226+
}
3227+
}
3228+
$sqlOrder = !empty($vectorOrders) ? 'ORDER BY ' . implode(', ', $vectorOrders) : '';
3229+
32093230
$sql = "
32103231
SELECT COUNT(1) as sum FROM (
32113232
SELECT 1
32123233
FROM {$this->getSQLTable($name)} AS {$this->quote($alias)}
3213-
{$sqlWhere}
3214-
{$limit}
3234+
{$sqlWhere}
3235+
{$sqlOrder}
3236+
{$limit}
32153237
) table_count
32163238
";
32173239

@@ -3264,7 +3286,18 @@ public function sum(Document $collection, string $attribute, array $queries = []
32643286

32653287
$queries = array_map(fn ($query) => clone $query, $queries);
32663288

3267-
$conditions = $this->getSQLConditions($queries, $binds);
3289+
// Extract vector queries (used for ORDER BY) and keep non-vector for WHERE
3290+
$vectorQueries = [];
3291+
$otherQueries = [];
3292+
foreach ($queries as $query) {
3293+
if (in_array($query->getMethod(), Query::VECTOR_TYPES)) {
3294+
$vectorQueries[] = $query;
3295+
} else {
3296+
$otherQueries[] = $query;
3297+
}
3298+
}
3299+
3300+
$conditions = $this->getSQLConditions($otherQueries, $binds);
32683301
if (!empty($conditions)) {
32693302
$where[] = $conditions;
32703303
}
@@ -3282,11 +3315,22 @@ public function sum(Document $collection, string $attribute, array $queries = []
32823315
? 'WHERE ' . \implode(' AND ', $where)
32833316
: '';
32843317

3318+
// Add vector distance calculations to ORDER BY (similarity-aware LIMIT)
3319+
$vectorOrders = [];
3320+
foreach ($vectorQueries as $query) {
3321+
$vectorOrder = $this->getVectorDistanceOrder($query, $binds, $alias);
3322+
if ($vectorOrder) {
3323+
$vectorOrders[] = $vectorOrder;
3324+
}
3325+
}
3326+
$sqlOrder = !empty($vectorOrders) ? 'ORDER BY ' . implode(', ', $vectorOrders) : '';
3327+
32853328
$sql = "
32863329
SELECT SUM({$this->quote($attribute)}) as sum FROM (
32873330
SELECT {$this->quote($attribute)}
32883331
FROM {$this->getSQLTable($name)} AS {$this->quote($alias)}
32893332
{$sqlWhere}
3333+
{$sqlOrder}
32903334
{$limit}
32913335
) table_count
32923336
";

src/Database/Database.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6240,7 +6240,6 @@ public function upsertDocumentsWithIncrease(
62406240

62416241
if (!$old->isEmpty()) {
62426242
$old = $this->adapter->castingAfter($collection, $old);
6243-
$old = $this->decode($collection, $old);
62446243
}
62456244

62466245
try {

tests/e2e/Adapter/Scopes/VectorTests.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2624,4 +2624,134 @@ public function testVectorQueryInNestedQuery(): void
26242624
// Cleanup
26252625
$database->deleteCollection('vectorNested');
26262626
}
2627+
2628+
public function testVectorQueryCount(): void
2629+
{
2630+
/** @var Database $database */
2631+
$database = static::getDatabase();
2632+
2633+
if (!$database->getAdapter()->getSupportForVectors()) {
2634+
$this->expectNotToPerformAssertions();
2635+
return;
2636+
}
2637+
2638+
$database->createCollection('vectorCount');
2639+
$database->createAttribute('vectorCount', 'embedding', Database::VAR_VECTOR, 3, true);
2640+
2641+
$database->createDocument('vectorCount', new Document([
2642+
'$permissions' => [
2643+
Permission::read(Role::any())
2644+
],
2645+
'embedding' => [1.0, 0.0, 0.0],
2646+
]));
2647+
2648+
$count = $database->count('vectorCount', [
2649+
Query::vectorCosine('embedding', [1.0, 0.0, 0.0]),
2650+
]);
2651+
2652+
$this->assertEquals(1, $count);
2653+
2654+
$database->deleteCollection('vectorCount');
2655+
}
2656+
2657+
public function testVectorQuerySum(): void
2658+
{
2659+
/** @var Database $database */
2660+
$database = static::getDatabase();
2661+
2662+
if (!$database->getAdapter()->getSupportForVectors()) {
2663+
$this->expectNotToPerformAssertions();
2664+
return;
2665+
}
2666+
2667+
$database->createCollection('vectorSum');
2668+
$database->createAttribute('vectorSum', 'embedding', Database::VAR_VECTOR, 3, true);
2669+
$database->createAttribute('vectorSum', 'value', Database::VAR_INTEGER, 0, true);
2670+
2671+
// Create documents with different values
2672+
$database->createDocument('vectorSum', new Document([
2673+
'$permissions' => [
2674+
Permission::read(Role::any())
2675+
],
2676+
'embedding' => [1.0, 0.0, 0.0],
2677+
'value' => 10
2678+
]));
2679+
2680+
$database->createDocument('vectorSum', new Document([
2681+
'$permissions' => [
2682+
Permission::read(Role::any())
2683+
],
2684+
'embedding' => [0.0, 1.0, 0.0],
2685+
'value' => 20
2686+
]));
2687+
2688+
$database->createDocument('vectorSum', new Document([
2689+
'$permissions' => [
2690+
Permission::read(Role::any())
2691+
],
2692+
'embedding' => [0.5, 0.5, 0.0],
2693+
'value' => 30
2694+
]));
2695+
2696+
// Test sum with vector query - should sum all matching documents
2697+
$sum = $database->sum('vectorSum', 'value', [
2698+
Query::vectorCosine('embedding', [1.0, 0.0, 0.0]),
2699+
]);
2700+
2701+
$this->assertEquals(60, $sum);
2702+
2703+
// Test sum with vector query and filter combined
2704+
$sum = $database->sum('vectorSum', 'value', [
2705+
Query::vectorCosine('embedding', [1.0, 0.0, 0.0]),
2706+
Query::greaterThan('value', 15),
2707+
]);
2708+
2709+
$this->assertEquals(50, $sum);
2710+
2711+
$database->deleteCollection('vectorSum');
2712+
}
2713+
2714+
public function testVetorUpsert(): void
2715+
{
2716+
/** @var Database $database */
2717+
$database = static::getDatabase();
2718+
2719+
if (!$database->getAdapter()->getSupportForVectors()) {
2720+
$this->expectNotToPerformAssertions();
2721+
return;
2722+
}
2723+
2724+
$database->createCollection('vectorUpsert');
2725+
$database->createAttribute('vectorUpsert', 'embedding', Database::VAR_VECTOR, 3, true);
2726+
2727+
$insertedDoc = $database->upsertDocument('vectorUpsert', new Document([
2728+
'$id' => 'vectorUpsert',
2729+
'$permissions' => [
2730+
Permission::read(Role::any()),
2731+
Permission::update(Role::any())
2732+
],
2733+
'embedding' => [1.0, 0.0, 0.0],
2734+
]));
2735+
2736+
$this->assertEquals([1.0, 0.0, 0.0], $insertedDoc->getAttribute('embedding'));
2737+
2738+
$insertedDoc = $database->getDocument('vectorUpsert', 'vectorUpsert');
2739+
$this->assertEquals([1.0, 0.0, 0.0], $insertedDoc->getAttribute('embedding'));
2740+
2741+
$updatedDoc = $database->upsertDocument('vectorUpsert', new Document([
2742+
'$id' => 'vectorUpsert',
2743+
'$permissions' => [
2744+
Permission::read(Role::any()),
2745+
Permission::update(Role::any())
2746+
],
2747+
'embedding' => [2.0, 0.0, 0.0],
2748+
]));
2749+
2750+
$this->assertEquals([2.0, 0.0, 0.0], $updatedDoc->getAttribute('embedding'));
2751+
2752+
$updatedDoc = $database->getDocument('vectorUpsert', 'vectorUpsert');
2753+
$this->assertEquals([2.0, 0.0, 0.0], $updatedDoc->getAttribute('embedding'));
2754+
2755+
$database->deleteCollection('vectorUpsert');
2756+
}
26272757
}

0 commit comments

Comments
 (0)