5
5
use Illuminate \Database \Eloquent \Collection as EloquentCollection ;
6
6
use Illuminate \Support \Collection as LaravelCollection ;
7
7
use Illuminate \Support \Facades \Schema ;
8
+ use MongoDB \Builder \Query ;
8
9
use MongoDB \Builder \Search ;
9
10
use MongoDB \Collection as MongoDBCollection ;
10
11
use MongoDB \Driver \Exception \ServerException ;
11
12
use MongoDB \Laravel \Schema \Builder ;
12
13
use MongoDB \Laravel \Tests \Models \Book ;
13
14
15
+ use function array_map ;
14
16
use function assert ;
17
+ use function mt_getrandmax ;
18
+ use function rand ;
19
+ use function range ;
20
+ use function srand ;
15
21
use function usleep ;
16
22
use function usort ;
17
23
18
24
class AtlasSearchTest extends TestCase
19
25
{
26
+ private array $ vectors ;
27
+
20
28
public function setUp (): void
21
29
{
22
30
parent ::setUp ();
23
31
24
- Book::insert ([
32
+ Book::insert ($ this -> addVector ( [
25
33
['title ' => 'Introduction to Algorithms ' ],
26
34
['title ' => 'Clean Code: A Handbook of Agile Software Craftsmanship ' ],
27
35
['title ' => 'Design Patterns: Elements of Reusable Object-Oriented Software ' ],
@@ -42,7 +50,7 @@ public function setUp(): void
42
50
['title ' => 'Understanding Machine Learning: From Theory to Algorithms ' ],
43
51
['title ' => 'Deep Learning ' ],
44
52
['title ' => 'Pattern Recognition and Machine Learning ' ],
45
- ]);
53
+ ])) ;
46
54
47
55
$ collection = $ this ->getConnection ('mongodb ' )->getCollection ('books ' );
48
56
assert ($ collection instanceof MongoDBCollection);
@@ -66,8 +74,9 @@ public function setUp(): void
66
74
67
75
$ collection ->createSearchIndex ([
68
76
'fields ' => [
69
- ['type ' => 'vector ' , 'numDimensions ' => 16 , 'path ' => 'vector16 ' , 'similarity ' => 'cosine ' ],
77
+ ['type ' => 'vector ' , 'numDimensions ' => 4 , 'path ' => 'vector4 ' , 'similarity ' => 'cosine ' ],
70
78
['type ' => 'vector ' , 'numDimensions ' => 32 , 'path ' => 'vector32 ' , 'similarity ' => 'euclidean ' ],
79
+ ['type ' => 'filter ' , 'path ' => 'title ' ],
71
80
],
72
81
], ['name ' => 'vector ' , 'type ' => 'vectorSearch ' ]);
73
82
} catch (ServerException $ e ) {
@@ -131,7 +140,7 @@ public function testGetIndexes()
131
140
],
132
141
[
133
142
'name ' => 'vector ' ,
134
- 'columns ' => ['vector16 ' , 'vector32 ' ],
143
+ 'columns ' => ['vector4 ' , 'vector32 ' , ' title ' ],
135
144
'type ' => 'vectorSearch ' ,
136
145
'primary ' => false ,
137
146
'unique ' => false ,
@@ -180,10 +189,10 @@ public function testEloquentBuilderAutocomplete()
180
189
self ::assertInstanceOf (LaravelCollection::class, $ results );
181
190
self ::assertCount (3 , $ results );
182
191
self ::assertSame ([
183
- 'Operating System Concepts ' ,
184
192
'Database System Concepts ' ,
185
193
'Modern Operating Systems ' ,
186
- ], $ results ->all ());
194
+ 'Operating System Concepts ' ,
195
+ ], $ results ->sort ()->values ()->all ());
187
196
}
188
197
189
198
public function testDatabaseBuilderAutocomplete ()
@@ -194,9 +203,62 @@ public function testDatabaseBuilderAutocomplete()
194
203
self ::assertInstanceOf (LaravelCollection::class, $ results );
195
204
self ::assertCount (3 , $ results );
196
205
self ::assertSame ([
197
- 'Operating System Concepts ' ,
198
206
'Database System Concepts ' ,
199
207
'Modern Operating Systems ' ,
200
- ], $ results ->all ());
208
+ 'Operating System Concepts ' ,
209
+ ], $ results ->sort ()->values ()->all ());
210
+ }
211
+
212
+ public function testDatabaseBuilderVectorSearch ()
213
+ {
214
+ $ results = $ this ->getConnection ('mongodb ' )->table ('books ' )
215
+ ->vectorSearch (
216
+ index: 'vector ' ,
217
+ path: 'vector4 ' ,
218
+ queryVector: $ this ->vectors [7 ], // This is an exact match of the vector
219
+ limit: 4 ,
220
+ exact: true ,
221
+ );
222
+
223
+ self ::assertInstanceOf (LaravelCollection::class, $ results );
224
+ self ::assertCount (4 , $ results );
225
+ self ::assertSame ('The Art of Computer Programming ' , $ results ->first ()['title ' ]);
226
+ self ::assertSame (1.0 , $ results ->first ()['vectorSearchScore ' ]);
227
+ }
228
+
229
+ public function testEloquentBuilderVectorSearch ()
230
+ {
231
+ $ results = Book::vectorSearch (
232
+ index: 'vector ' ,
233
+ path: 'vector4 ' ,
234
+ queryVector: $ this ->vectors [7 ],
235
+ limit: 5 ,
236
+ numCandidates: 15 ,
237
+ // excludes the exact match
238
+ filter: Query::query (
239
+ title: Query::ne ('The Art of Computer Programming ' ),
240
+ ),
241
+ );
242
+
243
+ self ::assertInstanceOf (EloquentCollection::class, $ results );
244
+ self ::assertCount (5 , $ results );
245
+ self ::assertInstanceOf (Book::class, $ results ->first ());
246
+ self ::assertNotSame ('The Art of Computer Programming ' , $ results ->first ()->title );
247
+ self ::assertSame ('The Mythical Man-Month: Essays on Software Engineering ' , $ results ->first ()->title );
248
+ self ::assertThat (
249
+ $ results ->first ()->vectorSearchScore ,
250
+ self ::logicalAnd (self ::isType ('float ' ), self ::greaterThan (0.9 ), self ::lessThan (1.0 )),
251
+ );
252
+ }
253
+
254
+ /** Generate random vectors using fixed seed to make tests deterministic */
255
+ private function addVector (array $ items ): array
256
+ {
257
+ srand (1 );
258
+ foreach ($ items as &$ item ) {
259
+ $ this ->vectors [] = $ item ['vector4 ' ] = array_map (fn () => rand () / mt_getrandmax (), range (0 , 3 ));
260
+ }
261
+
262
+ return $ items ;
201
263
}
202
264
}
0 commit comments