-
Notifications
You must be signed in to change notification settings - Fork 66
Add geosearch widget compatibility #543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
a7e39b1
Add geosearch
bidoubiwa 0c20aeb
Add env variable for api key
bidoubiwa 9341194
Communicate geo points
bidoubiwa 7c0109d
Add working geopoint calculation
bidoubiwa a139b9c
Add geo utilities
bidoubiwa 2f52dc3
Add geo adapter and tests
bidoubiwa 55c62b0
Add compatibility with geo search options
bidoubiwa c045820
Round distance to avoid to much uneeded precision
bidoubiwa 5ebc082
Fix geo rules adapter tests
bidoubiwa 7c58925
Remove unecessary console.logs
bidoubiwa ac69707
Add geo search response adapter
bidoubiwa f199b28
Add geo response adapter
bidoubiwa 118a62c
Add geo integration tests
bidoubiwa c11508c
Add documentation on geoSearch
bidoubiwa a97bdc9
Rollback app.js to initial main state
bidoubiwa 0a42c2e
remove geo dataset
bidoubiwa 6bb7df9
Rollback to initial javascript playground state
bidoubiwa ddaf49c
Remove uncessary comments
bidoubiwa 8391fa8
Add geographic utilities documentation
bidoubiwa 36b6566
Remove geo sorting rules
bidoubiwa 34a8513
Add playground and documentation
bidoubiwa 74a9ec2
Add polygon issue
bidoubiwa 71440fa
Apply review updates
bidoubiwa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,3 +119,4 @@ cypress/plugins | |
| cypress/fixtures | ||
|
|
||
| .vscode | ||
| data.ms | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
src/adapter/search-request-adapter/__tests__/geo-rules.tests.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import { adaptGeoPointsRules } from '../geo-rules-adapter' | ||
|
|
||
| test('Adapt geoPoints rules without a boundingBox', () => { | ||
| const rules = adaptGeoPointsRules() | ||
| expect(rules).toBeUndefined() | ||
| }) | ||
|
|
||
| test('Adapt geoPoints rules with same 0 lat and 0 lng geo points', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| insideBoundingBox: '0,0,0,0', | ||
| }) | ||
|
|
||
| expect(rules?.filter).toBe('_geoRadius(0, 0, 0)') | ||
| expect(rules?.sort).toBe('_geoPoint(0, 0):asc') | ||
| }) | ||
|
|
||
| test('Adapt geoPoints rules with integer geo points', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| insideBoundingBox: '1,2,3,4', | ||
| }) | ||
| expect(rules?.filter).toBe( | ||
| '_geoRadius(2.0003044085023727, 2.999390393801055, 157201.5)' | ||
| ) | ||
| expect(rules?.sort).toBe( | ||
| '_geoPoint(2.0003044085023727, 2.999390393801055):asc' | ||
| ) | ||
| }) | ||
|
|
||
| test('Try geoContext with only a radius', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundRadius: 1, | ||
| }) | ||
| expect(rules).toBeUndefined() | ||
| }) | ||
|
|
||
| test('Try geoContext with an aroundLatLng', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundLatLng: '51.1241999, 9.662499900000057', | ||
| }) | ||
| expect(rules?.sort).toBe('_geoPoint(51.1241999, 9.662499900000057):asc') | ||
| expect(rules?.filter).toBeUndefined() | ||
| }) | ||
|
|
||
| test('Try geoContext with an aroundLatLng and a radius', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundLatLng: '51.1241999, 9.662499900000057', | ||
| aroundRadius: 1, | ||
| }) | ||
| expect(rules?.sort).toBe('_geoPoint(51.1241999, 9.662499900000057):asc') | ||
| expect(rules?.filter).toBe('_geoRadius(51.1241999, 9.662499900000057, 1)') | ||
| }) | ||
|
|
||
| test('Try geoContext with an aroundLatLng and a 0 radius', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundLatLng: '51.1241999, 9.662499900000057', | ||
| aroundRadius: 0, | ||
| }) | ||
| expect(rules?.sort).toBe('_geoPoint(51.1241999, 9.662499900000057):asc') | ||
| expect(rules?.filter).toBe('_geoRadius(51.1241999, 9.662499900000057, 0)') | ||
| }) | ||
|
|
||
| test('Try geoContext with aroundLatLng, radius and insideBoundingBox', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundLatLng: '51.1241999, 9.662499900000057', | ||
| aroundRadius: 1, | ||
| insideBoundingBox: '1,2,3,4', | ||
| }) | ||
| expect(rules?.filter).toBe( | ||
| '_geoRadius(2.0003044085023727, 2.999390393801055, 157201.5)' | ||
| ) | ||
| expect(rules?.sort).toBe( | ||
| '_geoPoint(2.0003044085023727, 2.999390393801055):asc' | ||
| ) | ||
| }) | ||
| test('Try geoContext with a radius and insideBoundingBox', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundRadius: 1, | ||
| insideBoundingBox: '1,2,3,4', | ||
| }) | ||
| expect(rules?.filter).toBe( | ||
| '_geoRadius(2.0003044085023727, 2.999390393801055, 157201.5)' | ||
| ) | ||
| expect(rules?.sort).toBe( | ||
| '_geoPoint(2.0003044085023727, 2.999390393801055):asc' | ||
| ) | ||
| }) | ||
| test('Try geoContext with aroundLatLng and insideBoundingBox', () => { | ||
| const rules = adaptGeoPointsRules({ | ||
| aroundLatLng: '51.1241999, 9.662499900000057', | ||
| insideBoundingBox: '1,2,3,4', | ||
| }) | ||
| expect(rules?.filter).toBe( | ||
| '_geoRadius(2.0003044085023727, 2.999390393801055, 157201.5)' | ||
| ) | ||
| expect(rules?.sort).toBe( | ||
| '_geoPoint(2.0003044085023727, 2.999390393801055):asc' | ||
| ) | ||
| }) |
90 changes: 90 additions & 0 deletions
90
src/adapter/search-request-adapter/__tests__/search-params.tests.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import { adaptSearchParams } from '../search-params-adapter' | ||
|
|
||
| test('Adapt basic SearchContext ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| }) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) | ||
|
|
||
| test('Adapt SearchContext with filters, sort and no geo rules ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], | ||
| sort: 'id < 1', | ||
| }) | ||
|
|
||
| expect(searchParams.filter).toStrictEqual([ | ||
| ['genres="Drama"', 'genres="Thriller"'], | ||
| ['title="Ariel"'], | ||
| ]) | ||
| expect(searchParams.sort).toStrictEqual(['id < 1']) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) | ||
|
|
||
| test('Adapt SearchContext with filters, sort and geo rules ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], | ||
| insideBoundingBox: '0,0,0,0', | ||
| sort: 'id < 1', | ||
| }) | ||
|
|
||
| expect(searchParams.filter).toStrictEqual([ | ||
| '_geoRadius(0, 0, 0)', | ||
| ['genres="Drama"', 'genres="Thriller"'], | ||
| ['title="Ariel"'], | ||
| ]) | ||
| expect(searchParams.sort).toStrictEqual(['_geoPoint(0, 0):asc', 'id < 1']) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) | ||
|
|
||
| test('Adapt SearchContext with only facetFilters and geo rules ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| facetFilters: [['genres:Drama', 'genres:Thriller'], ['title:Ariel']], | ||
| insideBoundingBox: '0,0,0,0', | ||
| }) | ||
|
|
||
| expect(searchParams.filter).toEqual([ | ||
| '_geoRadius(0, 0, 0)', | ||
| ['genres="Drama"', 'genres="Thriller"'], | ||
| ['title="Ariel"'], | ||
| ]) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) | ||
|
|
||
| test('Adapt SearchContext with only sort and geo rules ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| insideBoundingBox: '0,0,0,0', | ||
| sort: 'id < 1', | ||
| }) | ||
|
|
||
| expect(searchParams.filter).toEqual(['_geoRadius(0, 0, 0)']) | ||
| expect(searchParams.sort).toStrictEqual(['_geoPoint(0, 0):asc', 'id < 1']) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) | ||
|
|
||
| test('Adapt SearchContext with no sort abd no filters and geo rules ', () => { | ||
| const searchParams = adaptSearchParams({ | ||
| indexUid: 'test', | ||
| paginationTotalHits: 20, | ||
| insideBoundingBox: '0,0,0,0', | ||
| }) | ||
|
|
||
| expect(searchParams.filter).toEqual(['_geoRadius(0, 0, 0)']) | ||
| expect(searchParams.sort).toStrictEqual(['_geoPoint(0, 0):asc']) | ||
| expect(searchParams.attributesToHighlight).toContain('*') | ||
| expect(searchParams.attributesToHighlight?.length).toBe(1) | ||
| }) |
104 changes: 104 additions & 0 deletions
104
src/adapter/search-request-adapter/geo-rules-adapter.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| 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 | ||
|
|
||
| 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) { | ||
| const [lat3, lng3] = middlePoint.split(',') | ||
|
|
||
| // check if radius is big enough | ||
| const filter = `_geoRadius(${lat3}, ${lng3}, ${radius})` | ||
|
|
||
| const sort = `_geoPoint(${lat3}, ${lng3}):asc` | ||
|
|
||
| return { filter, sort } | ||
| } else if (middlePoint != null) { | ||
| const [lat3, lng3] = middlePoint.split(',') | ||
| const sort = `_geoPoint(${lat3}, ${lng3}):asc` | ||
| return { sort } | ||
| } | ||
| 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 | ||
| } | ||
|
|
||
| if (aroundLatLngViaIP) { | ||
| console.warn('instant-meilisearch: `aroundLatLngViaIP` is not supported.') | ||
| } | ||
|
|
||
| if (aroundRadius) { | ||
| geoContext.aroundRadius = aroundRadius | ||
| } | ||
|
|
||
| if (aroundPrecision) { | ||
| console.warn(`instant-meilisearch: \`aroundPrecision\` is not supported. | ||
| See this discussion to track its implementation https://github.com/meilisearch/product/discussions/264`) | ||
| } | ||
|
|
||
| if (minimumAroundRadius) { | ||
| geoContext.minimumAroundRadius = minimumAroundRadius | ||
| } | ||
|
|
||
| if (insideBoundingBox) { | ||
| geoContext.insideBoundingBox = insideBoundingBox | ||
| } | ||
| // TODO: issue | ||
bidoubiwa marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (insidePolygon) { | ||
| console.warn( | ||
| `instant-meilisearch: \`insidePolygon\` is not implented in instant-meilisearch.` | ||
| ) | ||
| } | ||
| return geoContext | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.