Skip to content

Geosearch

Geosearch is a feature that allows you to filter your search results by distance from a given location, or by bounding box.

To perform geosearch queries, you first have to define a new geopoint property inside your schema definition when creating your Orama instance:

javascript
import { create } from '@orama/orama'

const db = await create({
  schema: {
    name: 'string',
    location: 'geopoint'
  }
})

What are geopoints

A geopoint is an object with two properties: lat and lon, which are both numbers. For reference, the following is a valid geopoint:

javascript
{
  lat: 45.46409,
  lon: 9.19192
}

As you may guess, lat stands for latitude, and lon for longitude. The values are expressed in degrees, and can be positive or negative.

Inserting documents with geopoints

You can insert documents with geopoints as you would with any other property:

javascript
import { create, insert } from '@orama/orama'

const db = await create({
  schema: {
    name: 'string',
    location: 'geopoint'
  }
})

await insert(db, { name: 'Duomo di Milano',    location: { lat: 45.46409, lon: 9.19192 } })
await insert(db, { name: 'Piazza Duomo',       location: { lat: 45.46416, lon: 9.18945 } })
await insert(db, { name: 'Piazzetta Reale',    location: { lat: 45.46339, lon: 9.19092 } })

Given the points above, we can picture them on a map as follows:



To avoid confusion, please note that Orama is a full-text and vector search library. We do not provide the tools to draw on maps.

Now that we have some data, let's see how we can perform geosearch queries.

Performing geosearch queries

When performing geosearch queries, we have to decide whether we want to filter our results by distance from a given location, or by bounding polygon.

When filtering by distance, we select a central coordinate and a radius of a given length, and we return all the documents that are within that radius from the central coordinate.

When filtering by bounding polygon, we select a polygon on the map, and we return all the documents that are within that polygon.

Filtering by distance (radius)

To filter by distance, we use the radius property. Let's see how it works:

javascript
import { create, insert, search } from '@orama/orama'

const db = await create({ ... })

await insert(db, { ... })
await insert(db, { ... })
await insert(db, { ... })

const searchResult = await search(db, {
  term: 'Duomo',
  where: {
    location: {           // The property we want to filter by
      radius: {           // The filter we want to apply (in that case: "radius")
        coordinates: {    // The central coordinate
          lat: 45.4648, 
          lon: 9.18998
        },
        unit: 'm',        // The unit of measurement. The default is "m" (meters)
        value: 1000,      // The radius length. In that case, 1km
        inside: true      // Whether we want to return the documents inside or outside the radius. The default is "true"
      }
    }
  }
})

The geopoint { lat: 45.4648, lon: 9.18998 } represents the entrance of the Vittorio Emanuele II Gallery in Milan, nearby the Duomo.

If we follow the configuration above, we are asking Orama to return all the documents that are within 100 meters from the Gallery, as shown in the following map:



The query above will return only one result indeed: the Piazza Duomo document.

If we change the inside property to false, we will get the opposite result: all the documents that are outside the radius.

Supported units of measurement

Orama currently supports the following units of measurement:

  • cm (centimeters)
  • m (meters)
  • km (kilometers)
  • ft (feet)
  • yd (yards)
  • mi (miles)

All these units will be converted into meters automatically. If you feel like we should support other units of measurement, please open an issue

Filtering by bounding polygon

To filter by bounding polygon, we use the polygon property. Let's see how it works:

javascript
import { create, insert, search } from '@orama/orama'

const db = await create({ ... })

await insert(db, { ... })
await insert(db, { ... })
await insert(db, { ... })

const searchResult = await search(db, {
  term: 'Duomo',
  where: {
    location: {            // The property we want to filter by
      polygon: {           // The filter we want to apply (in that case: "polygon")
        coordinates: [     // The polygon coordinate
          { lat: 45.46472, lon: 9.1886  },
          { lat: 45.46352, lon: 9.19177 },
          { lat: 45.46278, lon: 9.19176 },
          { lat: 45.4628,  lon: 9.18857 },
          { lat: 45.46472, lon: 9.1886  },
        ],
        inside: true      // Whether we want to return the documents inside or outside the polygon. The default is "true"
      }
    }
  }
})

If we try to draw the polygon above on our map, we will get the following result:



The query above will return only one result: Piazza Duomo. This is because it will filter all the documents inside the polygon whose name property contains the term "Duomo".

If you want to return all the points outside the polygon, you can set the inside property to false.

High Precision Geosearch

By default, Orama will use the Haversine Formula to calculate the distance between two points.

This formula is very fast, but since it considers the Earth as a perfect sphere, it might not be very precise for long distances.

Even though the Haversine Formula is more than enough for most use cases, if you need to perform geosearch queries with higher precision, you can force Orama to use the Vincenty Formula by setting highPrecision: true when performing your search:

javascript
import { create, insert, search } from '@orama/orama'

const db = await create({ ... })

await insert(db, { ... })
await insert(db, { ... })
await insert(db, { ... })

const searchResult = await search(db, {
  term: 'Duomo',
  where: {
    location: {
      polygon: {
        coordinates: [
          { lat: 45.46472, lon: 9.1886  },
          { lat: 45.46352, lon: 9.19177 },
          { lat: 45.46278, lon: 9.19176 },
          { lat: 45.4628,  lon: 9.18857 },
          { lat: 45.46472, lon: 9.1886  },
        ],
        highPrecision: true
      }
    }
  }
})

This will make the search a bit slower, but more precise on long distances.