DEV Community

Cover image for Tutorial: GeoSpatial Search In MongoDB The Easy Way!
Dĵ ΝιΓΞΗΛψΚ
Dĵ ΝιΓΞΗΛψΚ

Posted on • Updated on

Tutorial: GeoSpatial Search In MongoDB The Easy Way!

let's say we have a bunch of art galleries stored in mongodb located in paris and we want to retrieve only those that are within 20 kilometers of the eiffel tower.

mongodb makes it extremely easy if you store the entities properly tagged with their longitudes and latitudes.

Getting Started

if you haven't already, please see the introductory article mentioned below in order to get a new project setup before continuing with the rest of this article.

Define The Gallery Entity

public class ArtGallery : Entity
{
    public string Name { get; set; }
    public Coordinates2D Location { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

the Coordinates2D type is a special class supplied by the library to streamline the storage & retrieval of geographical data.

Create Some Entities

await new[]
{
    new ArtGallery {
        Name = "Galleria Le-Pari",
        Location = new Coordinates2D(48.8539241, 2.2913515)
    },
    new ArtGallery {
        Name = "Versailles House Of Art",
        Location = new Coordinates2D(48.796964, 2.137456)
    },
    new ArtGallery {
        Name = "Poissy Arts & Crafts",
        Location = new Coordinates2D(48.928860, 2.046889)
    }
}.SaveAsync();
Enter fullscreen mode Exit fullscreen mode

we are setting the locations of these galleries by creating new instances of Coordinates2D class supplying the longitude and latitude to the constructor. finally the whole array is persisted to mongodb by calling the .SaveAsync() extension method which issues a bulk write command.

Create An Index

mongodb geospatial queries require an index to be created which can be easily done as follows:

await DB.Index<ArtGallery>()
        .Key(g => g.Location, KeyType.Geo2DSphere)
        .Option(o => o.Background = false)
        .CreateAsync();
Enter fullscreen mode Exit fullscreen mode

we specify the index key as the Location property of the ArtGallery class and type of key as Geo2DSphere. also we are telling mongodb to build the index in the foreground with the .Option() method.

Do The Search

var locEiffelTower = new Coordinates2D(48.857908, 2.295243);

var results = await DB.Find<ArtGallery>()
                      .Match(g => g.Location, locEiffelTower, 20000)
                      .ExecuteAsync();

foreach (var gallery in results)
{
    Console.WriteLine(gallery.Name);
}

Console.Read();
Enter fullscreen mode Exit fullscreen mode

first we define the search point, which in this case is the location/coordinates of the eiffel tower in paris.

then we issue a find command that has the following matching criteria/arguments:

  1. the property where coordinates are stored on the ArtGallery entity.
  2. the search point to base the matching on.
  3. how far away from the search point (in meters) - at maximum should matches be located in.

The End Result

after running the find command you will see the following output in the console window:

Galleria Le-Pari
Versailles House Of Art
Enter fullscreen mode Exit fullscreen mode

notice that Poissy Arts & Crafts has been excluded. that is because it is located further than 20KMs from the eiffel tower.

Getting Distances Back

if you need to also retrieve how far away each gallery is from the eiffel tower, simply do the following:

add the following import:

using MongoDB.Driver;
Enter fullscreen mode Exit fullscreen mode

add another property to the ArtGallery entity like so:

public double DistanceInMeters { get; set; }
Enter fullscreen mode Exit fullscreen mode

query mongodb with the IAggregateFluent interface like so:

var results = await DB.FluentGeoNear<ArtGallery>(
                          NearCoordinates: locEiffelTower,
                          DistanceField: g => g.DistanceInMeters,
                          MaxDistance: 20000)
                      .ToListAsync();
Enter fullscreen mode Exit fullscreen mode

write to the console like so:

foreach (var gallery in results)
{
    Console.WriteLine(
    $"{gallery.Name} - {(gallery.DistanceInMeters / 1000).ToString("00.00")} KM");
}
Enter fullscreen mode Exit fullscreen mode

Next Steps...

if the above exercise has piqued your interest enough to discover how to easily get things done with mongodb using c#, please visit the official website of MongoDB.Entities. you can also check out the source code on github:

GitHub logo dj-nitehawk / MongoDB.Entities

A data access library for MongoDB with an elegant api, LINQ support and built-in entity relationship management

license nuget nuget tests discord

MongoDB.Entities

A light-weight .net standard library with barely any overhead that aims to simplify access to mongodb by abstracting the official driver while adding useful features on top of it resulting in an elegant API surface which produces beautiful, human friendly data access code.

More Info:

please visit the official website for detailed documentation:

https://mongodb-entities.com

Top comments (3)

Collapse
 
reachej profile image
Erik • Edited

First of all, thank you for this library, I appreciate your work greatly.

I had to add the IndexKey to get things working with CosmosDB.

Code along the lines of this tutorial that works for me using CosmoDB with Driver 4.2:

public class Tower : Entity
{
    public string Name { get; set; }
    public Coordinates2D Location { get; set; }
    public double Distance_in_meters { get; set; }
}


public class ArtGallery : Entity
{
    public string Name { get; set; }
    public Coordinates2D Location { get; set; }
    public double Distance_in_meters { get; set; }
}


var EiffelTower = new Tower{ 
    Name = "Eiffel Tower",
        Location = new Coordinates2D(48.857908, 2.295243)
    };

    var results =   DB.FluentGeoNear<ArtGallery >(
                    IndexKey: "Location",
                    NearCoordinates: EiffelTower.Location, 
                    DistanceField: x=>x.Distance_in_meters,
                    DistanceMultiplier:1,
                //  Spherical: true,
                //  MinDistance: 5,
                     MaxDistance: 15000 // distance in meters
                     )
                      .ToListAsync().GetAwaiter().GetResult();


            foreach (var gallery in results)
{
    Console.WriteLine(
    $"{gallery.Name} - {(gallery.Distance_in_meters / 1000).ToString("00.00")} KM");
}


Enter fullscreen mode Exit fullscreen mode
Collapse
 
willysierra profile image
William Sierra

Hi, I implemented all the steps but the final result is Zero, I use the exact same coordinates that the example and I get zero results, do you know what can I be missing?

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

i just copy/pasted the above code into a new console project and it works fine. here's a gist if you wanna try again. the only thing i had to add was the connection initialization.