1
votes

I'm working on a Phoenix project that stores lat/lng values in MongoDB, and needs to use MongoDB's built in Geo queries in order to query for records based on the distance between two points.

I've been looking around all morning, and have found this Geo repo, which is targeted at Postgres. I'd rather not have to switch to Postgres unless there's no other option.

Does anyone have any experience with Ecto/MongoDB and Geo queries and can point me in the right direction, or know if it's currently even possible with Ecto?

One possible alternative might be to set up a simple nodejs server with mongoose to handle the geo queries, and then have Phoenix hand off to that service when necessary? But I would obviously prefer an Ecto based solution if one's available.

Thanks

Paul

2

2 Answers

1
votes

It is not implemented directly in mongodb_ecto, but MichaƂ says it should be possible using fragments.

In IRC, he wrote:

something along the lines of where: fragment(foo: ["$geoWithin": [...]])

$geoWithin was explicitly excluded from the original work... https://github.com/michalmuskala/mongodb_ecto/issues/5

...and does not appear in the NormalizedQuery module https://github.com/michalmuskala/mongodb_ecto/blob/master/lib/mongo_ecto/normalized_query.ex

Also note that mongodb_ecto still requires Ecto 1.0, so you can't use it with the Ecto 2 beta.

1
votes

Yes, it's possible. You can use fragment as was said before. A full example might be helpful:

near_sphere = [
  "$nearSphere": [
    "$geometry": [
      "type": "Point",
      "coordinates": [10.0, 20.0]
    ],
    "$maxDistance": 1000
  ]
]

from(c in City, where: fragment(location: ^near_sphere)

In order to make this work, you will also need to create an index for the location field. According to the documentation, the best way is to use GeoJSON to store the location, but you can also use array:

defmodule MyApp.City do
  # ...
  schema "cities" do
    field :location, {:array, :float}
  end
end

Then you can create index with execute/1 (https://hexdocs.pm/ecto/Ecto.Migration.html#execute/1)

def up do
  execute [
    createIndexes: "cities",
    indexes: [
      [
        key: [location: "2dsphere"],
        name: "location_2dsphere",
        "2dsphereIndexVersion": 3
      ]
    ]
  ]
end

def down do
  execute [
    dropIndexes: "cities",
    index: "location_2dsphere"
  ]
end