Skip to content

Commit

Permalink
Improve performance of /nearest.
Browse files Browse the repository at this point in the history
The nearest postcode lookup performance reduced dramatically when tried
with Postgres 9.6 and PostGIS 2.3. This commit updates the query to use
the ‘<->’ centroid distance operator instead, which makes better use of
the geoindex and is much faster to execute.

Handily, the work @dracos did to introduce the TrigramDistance function
into Django 1.10 was easily adaptable for this purpose, as the operator
used is the same.
  • Loading branch information
davea authored and dracos committed Oct 24, 2017
1 parent db2322f commit a1656db
Showing 1 changed file with 17 additions and 3 deletions.
20 changes: 17 additions & 3 deletions mapit/views/postcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

from django.shortcuts import redirect, render
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.contrib.gis.db.models import Collect
from django.views.decorators.csrf import csrf_exempt
from django.db.models import FloatField
from django.db.models.expressions import Func, Value
from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter

from mapit.models import Postcode, Area, Generation
from mapit.utils import is_valid_postcode, is_valid_partial_postcode
Expand Down Expand Up @@ -36,6 +38,17 @@
}


class GeometryCentroidDistance(Func):
function = ''
arg_joiner = ' <-> '

def __init__(self, expression, string, **extra):
print(expression, string, extra)
if not hasattr(string, 'resolve_expression'):
string = Value(string)
super(GeometryCentroidDistance, self).__init__(expression, string, output_field=FloatField(), **extra)


@ratelimit(minutes=3, requests=100)
def postcode(request, postcode, format=None):
if hasattr(countries, 'canonical_postcode'):
Expand Down Expand Up @@ -156,8 +169,9 @@ def nearest(request, srid, x, y, format='json'):
location = Point(float(x), float(y), srid=int(srid))
set_timeout(format)
try:
postcode = Postcode.objects.filter(
location__distance_gte=(location, D(mi=0))).distance(location).order_by('distance')[0]
postcode = Postcode.objects.annotate(
centroid_distance=GeometryCentroidDistance('location', PostGISAdapter(location))
).filter(centroid_distance__gte=0).distance(location).order_by('centroid_distance')[0]
except DatabaseError as e:
if 'Cannot find SRID' in e.args[0]:
raise ViewException(format, e.args[0], 400)
Expand Down

0 comments on commit a1656db

Please sign in to comment.