Skip to content

Commit c6b3c09

Browse files
committed
Use _geoBoundingBox when creating the geo search filter (#1048)
* Use _geoBoundingBox to create geosearch filters * Add the _geoBoundingBox changeset
1 parent 7f20d0b commit c6b3c09

File tree

8 files changed

+115
-255
lines changed

8 files changed

+115
-255
lines changed

.changeset/small-masks-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@meilisearch/instant-meilisearch": patch
3+
---
4+
5+
Use the `_geoBoundingBox` filter to adapt the `insideBoundingBox`parameter

packages/instant-meilisearch/__tests__/geosearch.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('Instant Meilisearch Browser test', () => {
5151
])
5252

5353
const hits = response.results[0].hits
54-
expect(hits.length).toEqual(7)
54+
expect(hits.length).toEqual(5)
5555
expect(hits[0].city).toEqual('Lille')
5656
})
5757

@@ -68,8 +68,8 @@ describe('Instant Meilisearch Browser test', () => {
6868
])
6969

7070
const hits = response.results[0].hits
71-
expect(hits.length).toEqual(4)
72-
expect(hits[0].city).toEqual('Ghent')
71+
expect(hits.length).toEqual(2)
72+
expect(hits[0].city).toEqual('Brussels')
7373
})
7474

7575
test('insideBoundingBox and aroundRadius in geo search', async () => {
@@ -86,8 +86,8 @@ describe('Instant Meilisearch Browser test', () => {
8686
])
8787

8888
const hits = response.results[0].hits
89-
expect(hits.length).toEqual(4)
90-
expect(hits[0].city).toEqual('Ghent')
89+
expect(hits.length).toEqual(2)
90+
expect(hits[0].city).toEqual('Brussels')
9191
})
9292

9393
test('insideBoundingBox and aroundLatLng in geo search', async () => {
@@ -104,7 +104,7 @@ describe('Instant Meilisearch Browser test', () => {
104104
])
105105

106106
const hits = response.results[0].hits
107-
expect(hits.length).toEqual(4)
108-
expect(hits[0].city).toEqual('Ghent')
107+
expect(hits.length).toEqual(2)
108+
expect(hits[0].city).toEqual('Brussels')
109109
})
110110
})
Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,82 @@
1-
import { adaptGeoPointsRules } from '../geo-rules-adapter'
1+
import { adaptGeoSearch } from '../geo-rules-adapter'
22

3-
test('Adapt geoPoints rules without a boundingBox', () => {
4-
const rules = adaptGeoPointsRules()
5-
expect(rules).toBeUndefined()
3+
test('Adapt instantsearch geo parameters to meilisearch filters without a boundingBox', () => {
4+
const filter = adaptGeoSearch({})
5+
expect(filter).toBeUndefined()
66
})
77

8-
test('Adapt geoPoints rules with same 0 lat and 0 lng geo points', () => {
9-
const rules = adaptGeoPointsRules({
8+
test('Adapt instantsearch geo parameters to meilisearch filters with same 0 lat and 0 lng geo points', () => {
9+
const filter = adaptGeoSearch({
1010
insideBoundingBox: '0,0,0,0',
1111
})
1212

13-
expect(rules?.filter).toBe('_geoRadius(0.00000, 0.00000, 0)')
13+
expect(filter).toBe('_geoBoundingBox([0, 0], [0, 0])')
1414
})
1515

16-
test('Adapt geoPoints rules with integer geo points', () => {
17-
const rules = adaptGeoPointsRules({
16+
test('Adapt instantsearch geo parameters to meilisearch filters with integer geo points', () => {
17+
const filter = adaptGeoSearch({
1818
insideBoundingBox: '1,2,3,4',
1919
})
2020

21-
expect(rules?.filter).toBe('_geoRadius(3.17650, 3.19394, 157201.47551181243)')
21+
expect(filter).toBe('_geoBoundingBox([1, 2], [3, 4])')
2222
})
2323

24-
test('Try geoContext with only a radius', () => {
25-
const rules = adaptGeoPointsRules({
24+
test('Adapt instantsearch geo parameters to meilisearch filters with only a radius', () => {
25+
const filter = adaptGeoSearch({
2626
aroundRadius: 1,
2727
})
2828

29-
expect(rules).toBeUndefined()
29+
expect(filter).toBeUndefined()
3030
})
3131

32-
test('Try geoContext with an aroundLatLng', () => {
33-
const rules = adaptGeoPointsRules({
32+
test('Adapt instantsearch geo parameters to meilisearch filters with only an aroundLatLng', () => {
33+
const filter = adaptGeoSearch({
3434
aroundLatLng: '51.1241999, 9.662499900000057',
3535
})
3636

37-
expect(rules?.filter).toBeUndefined()
37+
expect(filter).toBeUndefined()
3838
})
3939

40-
test('Try geoContext with an aroundLatLng and a radius', () => {
41-
const rules = adaptGeoPointsRules({
40+
test('Adapt instantsearch geo parameters to meilisearch filters with an aroundLatLng and a radius', () => {
41+
const filter = adaptGeoSearch({
4242
aroundLatLng: '51.1241999, 9.662499900000057',
4343
aroundRadius: 1,
4444
})
4545

46-
expect(rules?.filter).toBe('_geoRadius(51.12420, 9.66250, 1)')
46+
expect(filter).toBe('_geoRadius(51.12420, 9.66250, 1)')
4747
})
4848

49-
test('Try geoContext with an aroundLatLng and a 0 radius', () => {
50-
const rules = adaptGeoPointsRules({
49+
test('Adapt instantsearch geo parameters to meilisearch filters with an aroundLatLng and a 0 radius', () => {
50+
const filter = adaptGeoSearch({
5151
aroundLatLng: '51.1241999, 9.662499900000057',
5252
aroundRadius: 0,
5353
})
5454

55-
expect(rules?.filter).toBe('_geoRadius(51.12420, 9.66250, 0)')
55+
expect(filter).toBe('_geoRadius(51.12420, 9.66250, 0)')
5656
})
5757

58-
test('Try geoContext with aroundLatLng, radius and insideBoundingBox', () => {
59-
const rules = adaptGeoPointsRules({
58+
test('Adapt instantsearch geo parameters to meilisearch filters with aroundLatLng, radius and insideBoundingBox', () => {
59+
const filter = adaptGeoSearch({
6060
aroundLatLng: '51.1241999, 9.662499900000057',
6161
aroundRadius: 1,
6262
insideBoundingBox: '1,2,3,4',
6363
})
6464

65-
expect(rules?.filter).toBe('_geoRadius(3.17650, 3.19394, 157201.47551181243)')
65+
expect(filter).toBe('_geoBoundingBox([1, 2], [3, 4])')
6666
})
67-
test('Try geoContext with a radius and insideBoundingBox', () => {
68-
const rules = adaptGeoPointsRules({
67+
test('Adapt instantsearch geo parameters to meilisearch filters with a radius and insideBoundingBox', () => {
68+
const filter = adaptGeoSearch({
6969
aroundRadius: 1,
7070
insideBoundingBox: '1,2,3,4',
7171
})
7272

73-
expect(rules?.filter).toBe('_geoRadius(3.17650, 3.19394, 157201.47551181243)')
73+
expect(filter).toBe('_geoBoundingBox([1, 2], [3, 4])')
7474
})
75-
test('Try geoContext with aroundLatLng and insideBoundingBox', () => {
76-
const rules = adaptGeoPointsRules({
75+
test('Adapt instantsearch geo parameters to meilisearch filters with aroundLatLng and insideBoundingBox', () => {
76+
const filter = adaptGeoSearch({
7777
aroundLatLng: '51.1241999, 9.662499900000057',
7878
insideBoundingBox: '1,2,3,4',
7979
})
8080

81-
expect(rules?.filter).toBe('_geoRadius(3.17650, 3.19394, 157201.47551181243)')
81+
expect(filter).toBe('_geoBoundingBox([1, 2], [3, 4])')
8282
})

packages/instant-meilisearch/src/adapter/search-request-adapter/__tests__/search-params.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ describe('Parameters adapter', () => {
4444
})
4545
})
4646

47-
describe('Geo rules adapter', () => {
48-
test('adapting a searchContext with filters, sort and geo rules ', () => {
47+
describe('Geo filter adapter', () => {
48+
test('adapting a searchContext with filters, sort and geo filters ', () => {
4949
const searchParams = adaptSearchParams({
5050
...DEFAULT_CONTEXT,
5151
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
@@ -54,7 +54,7 @@ describe('Geo rules adapter', () => {
5454
})
5555

5656
expect(searchParams.filter).toStrictEqual([
57-
'_geoRadius(0.00000, 0.00000, 0)',
57+
'_geoBoundingBox([0, 0], [0, 0])',
5858
['genres="Drama"', 'genres="Thriller"'],
5959
['title="Ariel"'],
6060
])
@@ -63,42 +63,42 @@ describe('Geo rules adapter', () => {
6363
expect(searchParams.attributesToHighlight?.length).toBe(1)
6464
})
6565

66-
test('adapting a searchContext with only facetFilters and geo rules ', () => {
66+
test('adapting a searchContext with only facetFilters and geo filters ', () => {
6767
const searchParams = adaptSearchParams({
6868
...DEFAULT_CONTEXT,
6969
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
7070
insideBoundingBox: '0,0,0,0',
7171
})
7272

7373
expect(searchParams.filter).toEqual([
74-
'_geoRadius(0.00000, 0.00000, 0)',
74+
'_geoBoundingBox([0, 0], [0, 0])',
7575
['genres="Drama"', 'genres="Thriller"'],
7676
['title="Ariel"'],
7777
])
7878
expect(searchParams.attributesToHighlight).toContain('*')
7979
expect(searchParams.attributesToHighlight?.length).toBe(1)
8080
})
8181

82-
test('adapting a searchContext with only sort and geo rules ', () => {
82+
test('adapting a searchContext with only sort and geo filters ', () => {
8383
const searchParams = adaptSearchParams({
8484
...DEFAULT_CONTEXT,
8585
insideBoundingBox: '0,0,0,0',
8686
sort: 'id < 1',
8787
})
8888

89-
expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
89+
expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])'])
9090
expect(searchParams.sort).toStrictEqual(['id < 1'])
9191
expect(searchParams.attributesToHighlight).toContain('*')
9292
expect(searchParams.attributesToHighlight?.length).toBe(1)
9393
})
9494

95-
test('adapting a searchContext with no sort and no filters and geo rules ', () => {
95+
test('adapting a searchContext with no sort and no filters and geo filters ', () => {
9696
const searchParams = adaptSearchParams({
9797
...DEFAULT_CONTEXT,
9898
insideBoundingBox: '0,0,0,0',
9999
})
100100

101-
expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
101+
expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])'])
102102
expect(searchParams.attributesToHighlight).toContain('*')
103103
expect(searchParams.attributesToHighlight?.length).toBe(1)
104104
})
Lines changed: 34 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,46 @@
1-
import { SearchContext, GeoSearchContext } from '../../types'
2-
import { getDistanceInMeter, middleGeoPoints } from '../../utils/geographic'
3-
4-
export function adaptGeoPointsRules(
5-
geoSearchContext?: GeoSearchContext
6-
): { filter?: string; sort?: string } | undefined {
7-
if (!geoSearchContext) {
8-
return undefined
9-
}
10-
const { insideBoundingBox, aroundLatLng, aroundRadius, minimumAroundRadius } =
11-
geoSearchContext
12-
13-
let middlePoint
14-
let radius
1+
import { InstantSearchGeoParams } from '../../types'
2+
3+
export function adaptGeoSearch({
4+
insideBoundingBox,
5+
aroundLatLng,
6+
aroundRadius,
7+
minimumAroundRadius,
8+
}: InstantSearchGeoParams): string | undefined {
9+
let middlePoint: string[] | undefined
10+
let radius: number | undefined
11+
let filter: string | undefined
1512

1613
if (aroundLatLng) {
17-
middlePoint = aroundLatLng
18-
}
19-
if (aroundRadius != null || minimumAroundRadius != null) {
20-
if (aroundRadius != null) radius = aroundRadius
21-
else radius = minimumAroundRadius
22-
}
23-
24-
// If insideBoundingBox is provided it takes precedent over all other options
25-
if (insideBoundingBox && typeof insideBoundingBox === 'string') {
26-
const [lat1Raw, lng1Raw, lat2Raw, lng2Raw] = insideBoundingBox.split(',')
27-
28-
const [lat1, lng1, lat2, lng2] = [
29-
parseFloat(lat1Raw),
30-
parseFloat(lng1Raw),
31-
parseFloat(lat2Raw),
32-
parseFloat(lng2Raw),
33-
]
34-
radius = getDistanceInMeter(lat1, lng1, lat2, lng2) / 2
35-
middlePoint = middleGeoPoints(lat1, lng1, lat2, lng2)
36-
}
37-
38-
if (middlePoint != null && radius != null) {
39-
let [lat3, lng3] = middlePoint.split(',')
40-
lat3 = Number.parseFloat(lat3).toFixed(5)
41-
lng3 = Number.parseFloat(lng3).toFixed(5)
42-
const filter = `_geoRadius(${lat3}, ${lng3}, ${radius})`
14+
const [lat, lng] = aroundLatLng
15+
.split(',')
16+
.map((pt) => Number.parseFloat(pt).toFixed(5))
4317

44-
return { filter }
45-
}
46-
return undefined
47-
}
48-
49-
export function createGeoSearchContext(
50-
searchContext: SearchContext
51-
): GeoSearchContext {
52-
const geoContext: Record<string, any> = {}
53-
const {
54-
aroundLatLng,
55-
aroundLatLngViaIP,
56-
aroundRadius,
57-
aroundPrecision,
58-
minimumAroundRadius,
59-
insideBoundingBox,
60-
insidePolygon,
61-
} = searchContext
62-
63-
if (aroundLatLng) {
64-
geoContext.aroundLatLng = aroundLatLng
18+
middlePoint = [lat, lng]
6519
}
6620

67-
if (aroundLatLngViaIP) {
68-
console.warn('instant-meilisearch: `aroundLatLngViaIP` is not supported.')
21+
if (aroundRadius != null || minimumAroundRadius != null) {
22+
if (aroundRadius === 'all') {
23+
console.warn(
24+
'instant-meilisearch is not compatible with the `all` value on the aroundRadius parameter'
25+
)
26+
} else if (aroundRadius != null) {
27+
radius = aroundRadius
28+
} else {
29+
radius = minimumAroundRadius
30+
}
6931
}
7032

71-
if (aroundRadius) {
72-
geoContext.aroundRadius = aroundRadius
73-
}
33+
if (insideBoundingBox && typeof insideBoundingBox === 'string') {
34+
const [lat1, lng1, lat2, lng2] = insideBoundingBox
35+
.split(',')
36+
.map((pt) => parseFloat(pt))
7437

75-
if (aroundPrecision) {
76-
console.warn(`instant-meilisearch: \`aroundPrecision\` is not supported.
77-
See this discussion to track its implementation https://github.com/meilisearch/product/discussions/264`)
78-
}
38+
filter = `_geoBoundingBox([${lat1}, ${lng1}], [${lat2}, ${lng2}])`
39+
} else if (middlePoint != null && radius != null) {
40+
const [lat, lng] = middlePoint
7941

80-
if (minimumAroundRadius) {
81-
geoContext.minimumAroundRadius = minimumAroundRadius
42+
filter = `_geoRadius(${lat}, ${lng}, ${radius})`
8243
}
8344

84-
if (insideBoundingBox) {
85-
geoContext.insideBoundingBox = insideBoundingBox
86-
}
87-
// See related issue: https://github.com/meilisearch/instant-meilisearch/issues/555
88-
if (insidePolygon) {
89-
console.warn(
90-
`instant-meilisearch: \`insidePolygon\` is not implented in instant-meilisearch.`
91-
)
92-
}
93-
return geoContext
45+
return filter
9446
}

0 commit comments

Comments
 (0)