Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/small-masks-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@meilisearch/instant-meilisearch": patch
---

Use the `_geoBoundingBox` filter to adapt the `insideBoundingBox`parameter
14 changes: 7 additions & 7 deletions packages/instant-meilisearch/__tests__/geosearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('Instant Meilisearch Browser test', () => {
])

const hits = response.results[0].hits
expect(hits.length).toEqual(7)
expect(hits.length).toEqual(5)
expect(hits[0].city).toEqual('Lille')
})

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

const hits = response.results[0].hits
expect(hits.length).toEqual(4)
expect(hits[0].city).toEqual('Ghent')
expect(hits.length).toEqual(2)
expect(hits[0].city).toEqual('Brussels')
})

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

const hits = response.results[0].hits
expect(hits.length).toEqual(4)
expect(hits[0].city).toEqual('Ghent')
expect(hits.length).toEqual(2)
expect(hits[0].city).toEqual('Brussels')
})

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

const hits = response.results[0].hits
expect(hits.length).toEqual(4)
expect(hits[0].city).toEqual('Ghent')
expect(hits.length).toEqual(2)
expect(hits[0].city).toEqual('Brussels')
})
})
Original file line number Diff line number Diff line change
@@ -1,82 +1,82 @@
import { adaptGeoPointsRules } from '../geo-rules-adapter'
import { adaptGeoSearch } from '../geo-rules-adapter'

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

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

expect(rules?.filter).toBe('_geoRadius(0.00000, 0.00000, 0)')
expect(filter).toBe('_geoBoundingBox([0, 0], [0, 0])')
})

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

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

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

expect(rules).toBeUndefined()
expect(filter).toBeUndefined()
})

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

expect(rules?.filter).toBeUndefined()
expect(filter).toBeUndefined()
})

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

expect(rules?.filter).toBe('_geoRadius(51.12420, 9.66250, 1)')
expect(filter).toBe('_geoRadius(51.12420, 9.66250, 1)')
})

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

expect(rules?.filter).toBe('_geoRadius(51.12420, 9.66250, 0)')
expect(filter).toBe('_geoRadius(51.12420, 9.66250, 0)')
})

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

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

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

expect(rules?.filter).toBe('_geoRadius(3.17650, 3.19394, 157201.47551181243)')
expect(filter).toBe('_geoBoundingBox([1, 2], [3, 4])')
})
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ describe('Parameters adapter', () => {
})
})

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

expect(searchParams.filter).toStrictEqual([
'_geoRadius(0.00000, 0.00000, 0)',
'_geoBoundingBox([0, 0], [0, 0])',
['genres="Drama"', 'genres="Thriller"'],
['title="Ariel"'],
])
Expand All @@ -63,42 +63,42 @@ describe('Geo rules adapter', () => {
expect(searchParams.attributesToHighlight?.length).toBe(1)
})

test('adapting a searchContext with only facetFilters and geo rules ', () => {
test('adapting a searchContext with only facetFilters and geo filters ', () => {
const searchParams = adaptSearchParams({
...DEFAULT_CONTEXT,
facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']],
insideBoundingBox: '0,0,0,0',
})

expect(searchParams.filter).toEqual([
'_geoRadius(0.00000, 0.00000, 0)',
'_geoBoundingBox([0, 0], [0, 0])',
['genres="Drama"', 'genres="Thriller"'],
['title="Ariel"'],
])
expect(searchParams.attributesToHighlight).toContain('*')
expect(searchParams.attributesToHighlight?.length).toBe(1)
})

test('adapting a searchContext with only sort and geo rules ', () => {
test('adapting a searchContext with only sort and geo filters ', () => {
const searchParams = adaptSearchParams({
...DEFAULT_CONTEXT,
insideBoundingBox: '0,0,0,0',
sort: 'id < 1',
})

expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])'])
expect(searchParams.sort).toStrictEqual(['id < 1'])
expect(searchParams.attributesToHighlight).toContain('*')
expect(searchParams.attributesToHighlight?.length).toBe(1)
})

test('adapting a searchContext with no sort and no filters and geo rules ', () => {
test('adapting a searchContext with no sort and no filters and geo filters ', () => {
const searchParams = adaptSearchParams({
...DEFAULT_CONTEXT,
insideBoundingBox: '0,0,0,0',
})

expect(searchParams.filter).toEqual(['_geoRadius(0.00000, 0.00000, 0)'])
expect(searchParams.filter).toEqual(['_geoBoundingBox([0, 0], [0, 0])'])
expect(searchParams.attributesToHighlight).toContain('*')
expect(searchParams.attributesToHighlight?.length).toBe(1)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,94 +1,46 @@
import { SearchContext, GeoSearchContext } from '../../types'
import { getDistanceInMeter, middleGeoPoints } from '../../utils/geographic'

export function adaptGeoPointsRules(
geoSearchContext?: GeoSearchContext
): { filter?: string; sort?: string } | undefined {
if (!geoSearchContext) {
return undefined
}
const { insideBoundingBox, aroundLatLng, aroundRadius, minimumAroundRadius } =
geoSearchContext

let middlePoint
let radius
import { InstantSearchGeoParams } from '../../types'

export function adaptGeoSearch({
insideBoundingBox,
aroundLatLng,
aroundRadius,
minimumAroundRadius,
}: InstantSearchGeoParams): string | undefined {
let middlePoint: string[] | undefined
let radius: number | undefined
let filter: string | undefined

if (aroundLatLng) {
middlePoint = aroundLatLng
}
if (aroundRadius != null || minimumAroundRadius != null) {
if (aroundRadius != null) radius = aroundRadius
else radius = minimumAroundRadius
}

// If insideBoundingBox is provided it takes precedent over all other options
if (insideBoundingBox && typeof insideBoundingBox === 'string') {
const [lat1Raw, lng1Raw, lat2Raw, lng2Raw] = insideBoundingBox.split(',')

const [lat1, lng1, lat2, lng2] = [
parseFloat(lat1Raw),
parseFloat(lng1Raw),
parseFloat(lat2Raw),
parseFloat(lng2Raw),
]
radius = getDistanceInMeter(lat1, lng1, lat2, lng2) / 2
middlePoint = middleGeoPoints(lat1, lng1, lat2, lng2)
}

if (middlePoint != null && radius != null) {
let [lat3, lng3] = middlePoint.split(',')
lat3 = Number.parseFloat(lat3).toFixed(5)
lng3 = Number.parseFloat(lng3).toFixed(5)
const filter = `_geoRadius(${lat3}, ${lng3}, ${radius})`
const [lat, lng] = aroundLatLng
.split(',')
.map((pt) => Number.parseFloat(pt).toFixed(5))

return { filter }
}
return undefined
}

export function createGeoSearchContext(
searchContext: SearchContext
): GeoSearchContext {
const geoContext: Record<string, any> = {}
const {
aroundLatLng,
aroundLatLngViaIP,
aroundRadius,
aroundPrecision,
minimumAroundRadius,
insideBoundingBox,
insidePolygon,
} = searchContext

if (aroundLatLng) {
geoContext.aroundLatLng = aroundLatLng
middlePoint = [lat, lng]
}

if (aroundLatLngViaIP) {
console.warn('instant-meilisearch: `aroundLatLngViaIP` is not supported.')
if (aroundRadius != null || minimumAroundRadius != null) {
if (aroundRadius === 'all') {
console.warn(
'instant-meilisearch is not compatible with the `all` value on the aroundRadius parameter'
)
} else if (aroundRadius != null) {
radius = aroundRadius
} else {
radius = minimumAroundRadius
}
}

if (aroundRadius) {
geoContext.aroundRadius = aroundRadius
}
if (insideBoundingBox && typeof insideBoundingBox === 'string') {
const [lat1, lng1, lat2, lng2] = insideBoundingBox
.split(',')
.map((pt) => parseFloat(pt))

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

if (minimumAroundRadius) {
geoContext.minimumAroundRadius = minimumAroundRadius
filter = `_geoRadius(${lat}, ${lng}, ${radius})`
}

if (insideBoundingBox) {
geoContext.insideBoundingBox = insideBoundingBox
}
// See related issue: https://github.com/meilisearch/instant-meilisearch/issues/555
if (insidePolygon) {
console.warn(
`instant-meilisearch: \`insidePolygon\` is not implented in instant-meilisearch.`
)
}
return geoContext
return filter
}
Loading