@@ -137,16 +137,25 @@ defmodule Ecto.Vector.GeospatialTest do
137137
138138 # Query using cosine distance
139139 result =
140- Ecto.Adapters.SQL . query! ( TestRepo , """
141- SELECT name, city, country, vector_distance_cos(embedding, vector(?)) as distance
142- FROM locations
143- ORDER BY distance
144- LIMIT 3
145- """ , [ sydney_embedding ] )
140+ Ecto.Adapters.SQL . query! (
141+ TestRepo ,
142+ """
143+ SELECT name, city, country, vector_distance_cos(embedding, vector(?)) as distance
144+ FROM locations
145+ ORDER BY distance
146+ LIMIT 3
147+ """ ,
148+ [ sydney_embedding ]
149+ )
146150
147151 # Should return Sydney first (distance 0 to itself), followed by other cities
148152 assert result . num_rows == 3
149- [ [ sydney_name , _ , _ , sydney_dist ] , [ second_name , _ , _ , second_dist ] , [ third_name , _ , _ , third_dist ] ] =
153+
154+ [
155+ [ sydney_name , _ , _ , sydney_dist ] ,
156+ [ second_name , _ , _ , second_dist ] ,
157+ [ third_name , _ , _ , third_dist ]
158+ ] =
150159 result . rows
151160
152161 assert sydney_name == "Sydney"
@@ -165,36 +174,48 @@ defmodule Ecto.Vector.GeospatialTest do
165174
166175 test "filters nearest locations by region" do
167176 # Insert cities from different regions
168- Ecto.Adapters.SQL . query! ( TestRepo , """
169- INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
170- VALUES
171- ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
172- ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
173- ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
174- ('Osaka', 34.67, 135.50, vector(?), 'Osaka', 'Japan', datetime('now'), datetime('now')),
175- ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
176- """ , [
177- EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
178- EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
179- EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
180- EctoLibSql.Native . vector ( [ 34.67 / 90 , 135.50 / 180 ] ) ,
181- EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
182- ] )
177+ Ecto.Adapters.SQL . query! (
178+ TestRepo ,
179+ """
180+ INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
181+ VALUES
182+ ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
183+ ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
184+ ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
185+ ('Osaka', 34.67, 135.50, vector(?), 'Osaka', 'Japan', datetime('now'), datetime('now')),
186+ ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
187+ """ ,
188+ [
189+ EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
190+ EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
191+ EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
192+ EctoLibSql.Native . vector ( [ 34.67 / 90 , 135.50 / 180 ] ) ,
193+ EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
194+ ]
195+ )
183196
184197 # Find nearest location to Tokyo, but only in Asia
185198 tokyo_embedding = EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] )
186199
187200 result =
188- Ecto.Adapters.SQL . query! ( TestRepo , """
189- SELECT name, city, country, vector_distance_cos(embedding, vector(?)) as distance
190- FROM locations
191- WHERE country IN ('Japan', 'Australia')
192- ORDER BY distance
193- LIMIT 2
194- """ , [ tokyo_embedding ] )
201+ Ecto.Adapters.SQL . query! (
202+ TestRepo ,
203+ """
204+ SELECT name, city, country, vector_distance_cos(embedding, vector(?)) as distance
205+ FROM locations
206+ WHERE country IN ('Japan', 'Australia')
207+ ORDER BY distance
208+ LIMIT 2
209+ """ ,
210+ [ tokyo_embedding ]
211+ )
195212
196213 assert result . num_rows == 2
197- [ [ first_name , _ , first_country , first_dist ] , [ second_name , _ , _second_country , _second_dist ] ] =
214+
215+ [
216+ [ first_name , _ , first_country , first_dist ] ,
217+ [ second_name , _ , _second_country , _second_dist ]
218+ ] =
198219 result . rows
199220
200221 # Tokyo should be first (distance 0)
@@ -208,33 +229,41 @@ defmodule Ecto.Vector.GeospatialTest do
208229
209230 test "searches within distance threshold" do
210231 # Insert cities
211- Ecto.Adapters.SQL . query! ( TestRepo , """
212- INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
213- VALUES
214- ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
215- ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
216- ('Brisbane', -27.47, 153.03, vector(?), 'Brisbane', 'Australia', datetime('now'), datetime('now')),
217- ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
218- ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
219- """ , [
220- EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
221- EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
222- EctoLibSql.Native . vector ( [ - 27.47 / 90 , 153.03 / 180 ] ) ,
223- EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
224- EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
225- ] )
232+ Ecto.Adapters.SQL . query! (
233+ TestRepo ,
234+ """
235+ INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
236+ VALUES
237+ ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
238+ ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
239+ ('Brisbane', -27.47, 153.03, vector(?), 'Brisbane', 'Australia', datetime('now'), datetime('now')),
240+ ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
241+ ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
242+ """ ,
243+ [
244+ EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
245+ EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
246+ EctoLibSql.Native . vector ( [ - 27.47 / 90 , 153.03 / 180 ] ) ,
247+ EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
248+ EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
249+ ]
250+ )
226251
227252 # Search for locations within a certain distance of Sydney
228253 # Using a threshold of 0.15 (roughly 15% of max distance in normalized space)
229254 sydney_embedding = EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] )
230255
231256 result =
232- Ecto.Adapters.SQL . query! ( TestRepo , """
233- SELECT name, country, vector_distance_cos(embedding, vector(?)) as distance
234- FROM locations
235- WHERE vector_distance_cos(embedding, vector(?)) < 0.15
236- ORDER BY distance
237- """ , [ sydney_embedding , sydney_embedding ] )
257+ Ecto.Adapters.SQL . query! (
258+ TestRepo ,
259+ """
260+ SELECT name, country, vector_distance_cos(embedding, vector(?)) as distance
261+ FROM locations
262+ WHERE vector_distance_cos(embedding, vector(?)) < 0.15
263+ ORDER BY distance
264+ """ ,
265+ [ sydney_embedding , sydney_embedding ]
266+ )
238267
239268 # Should find Sydney and nearby Australian cities
240269 names = Enum . map ( result . rows , fn [ name , _ , _ ] -> name end )
@@ -249,39 +278,47 @@ defmodule Ecto.Vector.GeospatialTest do
249278
250279 test "aggregates nearest neighbors by country" do
251280 # Insert multiple cities per country
252- Ecto.Adapters.SQL . query! ( TestRepo , """
253- INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
254- VALUES
255- ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
256- ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
257- ('Brisbane', -27.47, 153.03, vector(?), 'Brisbane', 'Australia', datetime('now'), datetime('now')),
258- ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
259- ('Osaka', 34.67, 135.50, vector(?), 'Osaka', 'Japan', datetime('now'), datetime('now')),
260- ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now')),
261- ('Los Angeles', 34.05, -118.24, vector(?), 'Los Angeles', 'USA', datetime('now'), datetime('now'))
262- """ , [
263- EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
264- EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
265- EctoLibSql.Native . vector ( [ - 27.47 / 90 , 153.03 / 180 ] ) ,
266- EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
267- EctoLibSql.Native . vector ( [ 34.67 / 90 , 135.50 / 180 ] ) ,
268- EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] ) ,
269- EctoLibSql.Native . vector ( [ 34.05 / 90 , - 118.24 / 180 ] )
270- ] )
281+ Ecto.Adapters.SQL . query! (
282+ TestRepo ,
283+ """
284+ INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
285+ VALUES
286+ ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
287+ ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
288+ ('Brisbane', -27.47, 153.03, vector(?), 'Brisbane', 'Australia', datetime('now'), datetime('now')),
289+ ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
290+ ('Osaka', 34.67, 135.50, vector(?), 'Osaka', 'Japan', datetime('now'), datetime('now')),
291+ ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now')),
292+ ('Los Angeles', 34.05, -118.24, vector(?), 'Los Angeles', 'USA', datetime('now'), datetime('now'))
293+ """ ,
294+ [
295+ EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
296+ EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
297+ EctoLibSql.Native . vector ( [ - 27.47 / 90 , 153.03 / 180 ] ) ,
298+ EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
299+ EctoLibSql.Native . vector ( [ 34.67 / 90 , 135.50 / 180 ] ) ,
300+ EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] ) ,
301+ EctoLibSql.Native . vector ( [ 34.05 / 90 , - 118.24 / 180 ] )
302+ ]
303+ )
271304
272305 # Find the closest location to Sydney in each country
273306 sydney_embedding = EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] )
274307
275308 result =
276- Ecto.Adapters.SQL . query! ( TestRepo , """
277- SELECT
278- country,
279- name,
280- vector_distance_cos(embedding, vector(?)) as distance
281- FROM locations
282- WHERE country != 'Australia'
283- ORDER BY country, distance
284- """ , [ sydney_embedding ] )
309+ Ecto.Adapters.SQL . query! (
310+ TestRepo ,
311+ """
312+ SELECT
313+ country,
314+ name,
315+ vector_distance_cos(embedding, vector(?)) as distance
316+ FROM locations
317+ WHERE country != 'Australia'
318+ ORDER BY country, distance
319+ """ ,
320+ [ sydney_embedding ]
321+ )
285322
286323 assert result . num_rows == 4
287324 rows = result . rows
@@ -304,23 +341,27 @@ defmodule Ecto.Vector.GeospatialTest do
304341
305342 test "finds approximate locations using vector ranges" do
306343 # Insert locations in specific regions
307- Ecto.Adapters.SQL . query! ( TestRepo , """
308- INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
309- VALUES
310- ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
311- ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
312- ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
313- ('Bangkok', 13.73, 100.50, vector(?), 'Bangkok', 'Thailand', datetime('now'), datetime('now')),
314- ('Singapore', 1.35, 103.82, vector(?), 'Singapore', 'Singapore', datetime('now'), datetime('now')),
315- ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
316- """ , [
317- EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
318- EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
319- EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
320- EctoLibSql.Native . vector ( [ 13.73 / 90 , 100.50 / 180 ] ) ,
321- EctoLibSql.Native . vector ( [ 1.35 / 90 , 103.82 / 180 ] ) ,
322- EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
323- ] )
344+ Ecto.Adapters.SQL . query! (
345+ TestRepo ,
346+ """
347+ INSERT INTO locations (name, latitude, longitude, embedding, city, country, inserted_at, updated_at)
348+ VALUES
349+ ('Sydney', -33.87, 151.21, vector(?), 'Sydney', 'Australia', datetime('now'), datetime('now')),
350+ ('Melbourne', -37.81, 144.96, vector(?), 'Melbourne', 'Australia', datetime('now'), datetime('now')),
351+ ('Tokyo', 35.68, 139.69, vector(?), 'Tokyo', 'Japan', datetime('now'), datetime('now')),
352+ ('Bangkok', 13.73, 100.50, vector(?), 'Bangkok', 'Thailand', datetime('now'), datetime('now')),
353+ ('Singapore', 1.35, 103.82, vector(?), 'Singapore', 'Singapore', datetime('now'), datetime('now')),
354+ ('New York', 40.71, -74.01, vector(?), 'New York', 'USA', datetime('now'), datetime('now'))
355+ """ ,
356+ [
357+ EctoLibSql.Native . vector ( [ - 33.87 / 90 , 151.21 / 180 ] ) ,
358+ EctoLibSql.Native . vector ( [ - 37.81 / 90 , 144.96 / 180 ] ) ,
359+ EctoLibSql.Native . vector ( [ 35.68 / 90 , 139.69 / 180 ] ) ,
360+ EctoLibSql.Native . vector ( [ 13.73 / 90 , 100.50 / 180 ] ) ,
361+ EctoLibSql.Native . vector ( [ 1.35 / 90 , 103.82 / 180 ] ) ,
362+ EctoLibSql.Native . vector ( [ 40.71 / 90 , - 74.01 / 180 ] )
363+ ]
364+ )
324365
325366 # Find locations in Southeast Asia (roughly 0-30° N, 95-140° E)
326367 # Normalized: latitude [0/90, 30/90] = [0, 0.33], longitude [95/180, 140/180] = [0.53, 0.78]
0 commit comments