Skip to content

Commit 8b6df59

Browse files
Shubhamgreenrobot-team
Shubham
authored andcommitted
VectorDistance: add new distance-type 'Geo', add vectorSearchCitiesGeo test #129
1 parent 0d98007 commit 8b6df59

File tree

8 files changed

+66
-4
lines changed

8 files changed

+66
-4
lines changed

objectbox/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## latest
22

3+
* Vector Search: You can now use the new `VectorDistanceType.GEO` distance-type to perform vector searches on
4+
geographical coordinates. This is particularly useful for location-based applications.
35
* Flutter for Linux/Windows, Dart Native: update to [objectbox-c 4.1.0](https://github.com/objectbox/objectbox-c/releases/tag/v4.1.0).
46
* Flutter for Android: update to [objectbox-android 4.1.0](https://github.com/objectbox/objectbox-java/releases/tag/V4.1.0).
57
If your project is [using Admin](https://docs.objectbox.io/data-browser#admin-for-android), make sure to

objectbox/example/dart-native/vectorsearch_cities/lib/model.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class City {
77

88
String? name;
99

10-
@HnswIndex(dimensions: 2)
10+
@HnswIndex(dimensions: 2, distanceType: VectorDistanceType.geo)
1111
@Property(type: PropertyType.floatVector)
1212
List<double>? location;
1313

objectbox/lib/src/annotations.dart

+12-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,18 @@ enum VectorDistanceType {
341341
/// The more negative the dot product, the higher the distance is (the farther the vectors are).
342342
///
343343
/// Value range: 0.0 - 2.0 (nonlinear; 0.0: nearest, 1.0: orthogonal, 2.0: farthest)
344-
dotProductNonNormalized
344+
dotProductNonNormalized,
345+
346+
/// For geospatial coordinates aka latitude/longitude pairs.
347+
///
348+
/// Note, that the vector dimension must be 2, with the latitude being the first element and longitude the second.
349+
/// If the vector has more than 2 dimensions, the first 2 dimensions are used.
350+
/// If the vector has fewer than 2 dimensions, the distance is zero.
351+
///
352+
/// Internally, this uses haversine distance.
353+
///
354+
/// Value range: 0 km - 6371 * π km (approx. 20015.09 km; half the Earth's circumference)
355+
geo
345356
}
346357

347358
/// Flags as a part of the [HnswIndex] configuration.

objectbox/lib/src/modelinfo/model_hnsw_params.dart

+2
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ extension ModelVectorDistanceType on VectorDistanceType {
189189
return OBXVectorDistanceType.DotProduct;
190190
} else if (this == VectorDistanceType.dotProductNonNormalized) {
191191
return OBXVectorDistanceType.DotProductNonNormalized;
192+
} else if (this == VectorDistanceType.geo) {
193+
return OBXVectorDistanceType.Geo;
192194
} else {
193195
throw ArgumentError.value(this, "distanceType");
194196
}

objectbox_test/test/annotations_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ void main() {
2222
OBXVectorDistanceType.DotProduct);
2323
expect(VectorDistanceType.dotProductNonNormalized.toConstant(),
2424
OBXVectorDistanceType.DotProductNonNormalized);
25+
expect(VectorDistanceType.geo.toConstant(), OBXVectorDistanceType.Geo);
2526
});
2627

2728
test("ModelHnswParams maps values", () {

objectbox_test/test/entity.dart

+4
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ class HnswObject {
465465
@HnswIndex(dimensions: 2)
466466
List<double>? floatVector;
467467

468+
@Property(type: PropertyType.floatVector)
469+
@HnswIndex(dimensions: 2, distanceType: VectorDistanceType.geo)
470+
List<double>? floatVectorGeoCoordinates;
471+
468472
final rel = ToOne<RelatedNamedEntity>();
469473
}
470474

objectbox_test/test/hnsw_test.dart

+35
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,41 @@ void main() {
7272
expect(closest2.name, "node8");
7373
});
7474

75+
test('vectorSearchCitiesGeo', () {
76+
// capital cities across Europe
77+
List<String> cities = ["Berlin", "Paris", "Rome", "Madrid", "London"];
78+
List<List<double>> coordinates = [
79+
[52.5200, 13.4050],
80+
[48.8566, 2.3522],
81+
[41.9028, 12.4964],
82+
[40.4168, -3.7038],
83+
[51.5074, -0.1278]
84+
];
85+
86+
box.putMany(List.generate(cities.length, (i) {
87+
return HnswObject()
88+
..name = cities[i]
89+
..floatVectorGeoCoordinates = coordinates[i];
90+
}));
91+
92+
// lat/lng for Munich
93+
final List<double> searchVector = [48.1371, 11.5754];
94+
95+
final query = box
96+
.query(HnswObject_.floatVectorGeoCoordinates
97+
.nearestNeighborsF32(searchVector, 5))
98+
.build();
99+
addTearDown(() => query.close());
100+
101+
final nearestCities = query.find();
102+
expect(nearestCities.length, 5);
103+
expect(nearestCities[0].name, "Berlin");
104+
expect(nearestCities[1].name, "Paris");
105+
expect(nearestCities[2].name, "Rome");
106+
expect(nearestCities[3].name, "Madrid");
107+
expect(nearestCities[4].name, "London");
108+
});
109+
75110
test('find offset limit', () {
76111
box.putMany(List.generate(15, (index) {
77112
final i = index + 1; // start at 1

objectbox_test/test/objectbox-model.json

+9-2
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@
667667
},
668668
{
669669
"id": "14:880388751413233760",
670-
"lastPropertyId": "4:2308158275756661586",
670+
"lastPropertyId": "5:1476994841170736323",
671671
"name": "HnswObject",
672672
"properties": [
673673
{
@@ -695,6 +695,13 @@
695695
"flags": 520,
696696
"indexId": "22:6527315700526716999",
697697
"relationTarget": "RelatedNamedEntity"
698+
},
699+
{
700+
"id": "5:1476994841170736323",
701+
"name": "floatVectorGeoCoordinates",
702+
"type": 28,
703+
"flags": 8,
704+
"indexId": "23:6649884639373473085"
698705
}
699706
],
700707
"relations": []
@@ -720,7 +727,7 @@
720727
}
721728
],
722729
"lastEntityId": "15:4803284427984871569",
723-
"lastIndexId": "22:6527315700526716999",
730+
"lastIndexId": "23:6649884639373473085",
724731
"lastRelationId": "1:2155747579134420981",
725732
"lastSequenceId": "0:0",
726733
"modelVersion": 5,

0 commit comments

Comments
 (0)