2727 */
2828final class Store implements InitializableStoreInterface, VectorStoreInterface
2929{
30+ private string $ authenticationToken = '' ;
31+
3032 public function __construct (
3133 private readonly HttpClientInterface $ httpClient ,
3234 private readonly string $ endpointUrl ,
33- #[\SensitiveParameter]
34- private readonly string $ user ,
35- #[\SensitiveParameter]
36- private readonly string $ password ,
37- #[\SensitiveParameter]
38- private readonly string $ namespace ,
39- #[\SensitiveParameter]
40- private readonly string $ database ,
35+ #[\SensitiveParameter] private readonly string $ user ,
36+ #[\SensitiveParameter] private readonly string $ password ,
37+ #[\SensitiveParameter] private readonly string $ namespace ,
38+ #[\SensitiveParameter] private readonly string $ database ,
4139 private readonly string $ table = 'vectors ' ,
4240 private readonly string $ vectorFieldName = '_vectors ' ,
4341 private readonly string $ strategy = 'cosine ' ,
4442 private readonly int $ embeddingsDimension = 1536 ,
43+ private readonly bool $ isNamespacedUser = false ,
4544 ) {
4645 }
4746
4847 public function add (VectorDocument ...$ documents ): void
4948 {
50- $ authenticationToken = $ this ->authenticate ([]);
51-
5249 foreach ($ documents as $ document ) {
53- $ this ->request ('POST ' , \sprintf ('key/%s ' , $ this ->table ), $ this ->convertToIndexableArray ($ document ), [
54- 'Surreal-NS ' => $ this ->namespace ,
55- 'Surreal-DB ' => $ this ->database ,
56- 'Authorization ' => \sprintf ('Bearer %s ' , $ authenticationToken ),
57- ]);
50+ $ this ->request ('POST ' , \sprintf ('key/%s ' , $ this ->table ), $ this ->convertToIndexableArray ($ document ));
5851 }
5952 }
6053
6154 public function query (Vector $ vector , array $ options = [], ?float $ minScore = null ): array
6255 {
63- $ authenticationToken = $ this ->authenticate ($ options );
64-
6556 $ vectors = json_encode ($ vector ->getData ());
6657
6758 $ results = $ this ->request ('POST ' , 'sql ' , \sprintf (
6859 'SELECT id, %s, _metadata, vector::similarity::%s(%s, %s) AS distance FROM %s WHERE %s <|2|> %s; ' ,
6960 $ this ->vectorFieldName , $ this ->strategy , $ this ->vectorFieldName , $ vectors , $ this ->table , $ this ->vectorFieldName , $ vectors ,
70- ), [
71- 'Surreal-NS ' => $ this ->namespace ,
72- 'Surreal-DB ' => $ this ->database ,
73- 'Authorization ' => \sprintf ('Bearer %s ' , $ authenticationToken ),
74- ]);
61+ ));
7562
7663 return array_map ($ this ->convertToVectorDocument (...), $ results [0 ]['result ' ]);
7764 }
7865
7966 public function initialize (array $ options = []): void
8067 {
81- $ authenticationToken = $ this ->authenticate ($ options );
68+ $ this ->authenticate ();
8269
8370 $ this ->request ('POST ' , 'sql ' , \sprintf (
8471 'DEFINE INDEX %s_vectors ON %s FIELDS %s MTREE DIMENSION %d DIST %s TYPE F32 ' ,
8572 $ this ->table , $ this ->table , $ this ->vectorFieldName , $ this ->embeddingsDimension , $ this ->strategy
86- ), [
87- 'Surreal-NS ' => $ this ->namespace ,
88- 'Surreal-DB ' => $ this ->database ,
89- 'Authorization ' => \sprintf ('Bearer %s ' , $ authenticationToken ),
90- ]);
73+ ));
9174 }
9275
9376 /**
9477 * @param array<string, mixed>|string $payload
95- * @param array<string, mixed> $extraHeaders
9678 *
9779 * @return array<string|int, mixed>
9880 */
99- private function request (string $ method , string $ endpoint , array |string $ payload, array $ extraHeaders = [] ): array
81+ private function request (string $ method , string $ endpoint , array |string $ payload ): array
10082 {
10183 $ url = \sprintf ('%s/%s ' , $ this ->endpointUrl , $ endpoint );
10284
@@ -111,10 +93,13 @@ private function request(string $method, string $endpoint, array|string $payload
11193 }
11294
11395 $ response = $ this ->httpClient ->request ($ method , $ url , array_merge ($ finalPayload , [
114- 'headers ' => array_merge ( $ extraHeaders , [
96+ 'headers ' => [
11597 'Accept ' => 'application/json ' ,
11698 'Content-Type ' => 'application/json ' ,
117- ]),
99+ 'Surreal-NS ' => $ this ->namespace ,
100+ 'Surreal-DB ' => $ this ->database ,
101+ 'Authorization ' => \sprintf ('Bearer %s ' , $ this ->authenticationToken ),
102+ ],
118103 ]));
119104
120105 return $ response ->toArray ();
@@ -154,29 +139,35 @@ private function convertToVectorDocument(array $data): VectorDocument
154139 );
155140 }
156141
157- /**
158- * @param array{
159- * namespacedUser?: bool
160- * } $options The namespacedUser option is used to determine if the user is root or not, if not, both the namespace and database must be specified
161- */
162- private function authenticate (array $ options ): string
142+ private function authenticate (): void
163143 {
144+ if ('' !== $ this ->authenticationToken ) {
145+ return ;
146+ }
147+
164148 $ authenticationPayload = [
165149 'user ' => $ this ->user ,
166150 'pass ' => $ this ->password ,
167151 ];
168152
169- if (\array_key_exists ( ' namespacedUser ' , $ options ) && ! $ options [ ' namespacedUser ' ] ) {
153+ if ($ this -> isNamespacedUser ) {
170154 $ authenticationPayload ['ns ' ] = $ this ->namespace ;
171155 $ authenticationPayload ['db ' ] = $ this ->database ;
172156 }
173157
174- $ authenticationResponse = $ this ->request ('POST ' , 'signin ' , $ authenticationPayload );
158+ $ authenticationResponse = $ this ->httpClient ->request ('POST ' , \sprintf ('%s/signin ' , $ this ->endpointUrl ), [
159+ 'headers ' => [
160+ 'Accept ' => 'application/json ' ,
161+ ],
162+ 'json ' => $ authenticationPayload ,
163+ ]);
164+
165+ $ payload = $ authenticationResponse ->toArray ();
175166
176- if (!\array_key_exists ('token ' , $ authenticationResponse )) {
167+ if (!\array_key_exists ('token ' , $ payload )) {
177168 throw new RuntimeException ('The SurrealDB authentication response does not contain a token. ' );
178169 }
179170
180- return $ authenticationResponse ['token ' ];
171+ $ this -> authenticationToken = $ payload ['token ' ];
181172 }
182173}
0 commit comments