Skip to content

Commit fa78fb1

Browse files
committed
Replace SpatialOperator with simpler Operator class
1 parent a37977d commit fa78fb1

File tree

3 files changed

+158
-122
lines changed

3 files changed

+158
-122
lines changed

django_mongodb_backend/gis/operations.py

Lines changed: 24 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,101 +2,20 @@
22
from django.contrib.gis.db import models
33
from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations
44
from django.contrib.gis.measure import Distance
5-
from django.db import NotSupportedError
65
from django.db.backends.base.operations import BaseDatabaseOperations
76

87
from .adapter import Adapter
9-
from .utils import SpatialOperator
10-
11-
12-
def _gis_within_operator(field, value, op=None, params=None): # noqa: ARG001
13-
return {
14-
field: {
15-
"$geoWithin": {
16-
"$geometry": {
17-
"type": value["type"],
18-
"coordinates": value["coordinates"],
19-
}
20-
}
21-
}
22-
}
23-
24-
25-
def _gis_intersects_operator(field, value, op=None, params=None): # noqa: ARG001
26-
return {
27-
field: {
28-
"$geoIntersects": {
29-
"$geometry": {
30-
"type": value["type"],
31-
"coordinates": value["coordinates"],
32-
}
33-
}
34-
}
35-
}
36-
37-
38-
def _gis_disjoint_operator(field, value, op=None, params=None): # noqa: ARG001
39-
return {
40-
field: {
41-
"$not": {
42-
"$geoIntersects": {
43-
"$geometry": {
44-
"type": value["type"],
45-
"coordinates": value["coordinates"],
46-
}
47-
}
48-
}
49-
}
50-
}
51-
52-
53-
def _gis_contains_operator(field, value, op=None, params=None): # noqa: ARG001
54-
value_type = value["type"]
55-
if value_type != "Point":
56-
raise NotSupportedError("MongoDB does not support contains on non-Point query geometries.")
57-
return {
58-
field: {
59-
"$geoIntersects": {
60-
"$geometry": {
61-
"type": value_type,
62-
"coordinates": value["coordinates"],
63-
}
64-
}
65-
}
66-
}
67-
68-
69-
def _gis_distance_operator(field, value, op=None, params=None):
70-
distance = params[0].m if hasattr(params[0], "m") else params[0]
71-
if op == "distance_gt" or op == "distance_gte":
72-
cmd = {
73-
field: {
74-
"$not": {
75-
"$geoWithin": {
76-
"$centerSphere": [
77-
value["coordinates"],
78-
distance / 6378100, # radius of earth in meters
79-
],
80-
}
81-
}
82-
}
83-
}
84-
else:
85-
cmd = {
86-
field: {
87-
"$geoWithin": {
88-
"$centerSphere": [
89-
value["coordinates"],
90-
distance / 6378100, # radius of earth in meters
91-
],
92-
}
93-
}
94-
}
95-
return cmd
96-
97-
98-
def _gis_dwithin_operator(field, value, op=None, params=None): # noqa: ARG001
99-
return {field: {"$geoWithin": {"$centerSphere": [value["coordinates"], params[0]]}}}
8+
from .operators import (
9+
Contains,
10+
Disjoint,
11+
DistanceGT,
12+
DistanceGTE,
13+
DistanceLT,
14+
DistanceLTE,
15+
DWithin,
16+
Intersects,
17+
Within,
18+
)
10019

10120

10221
class GISOperations(BaseSpatialOperations, BaseDatabaseOperations):
@@ -110,19 +29,21 @@ class GISOperations(BaseSpatialOperations, BaseDatabaseOperations):
11029
models.Union,
11130
)
11231

32+
operators = {
33+
"contains": Contains(),
34+
"intersects": Intersects(),
35+
"disjoint": Disjoint(),
36+
"within": Within(),
37+
"distance_gt": DistanceGT(),
38+
"distance_gte": DistanceGTE(),
39+
"distance_lt": DistanceLT(),
40+
"distance_lte": DistanceLTE(),
41+
"dwithin": DWithin(),
42+
}
43+
11344
@property
11445
def gis_operators(self):
115-
return {
116-
"contains": SpatialOperator("contains", _gis_contains_operator),
117-
"intersects": SpatialOperator("intersects", _gis_intersects_operator),
118-
"disjoint": SpatialOperator("disjoint", _gis_disjoint_operator),
119-
"within": SpatialOperator("within", _gis_within_operator),
120-
"distance_gt": SpatialOperator("distance_gt", _gis_distance_operator),
121-
"distance_gte": SpatialOperator("distance_gte", _gis_distance_operator),
122-
"distance_lt": SpatialOperator("distance_lt", _gis_distance_operator),
123-
"distance_lte": SpatialOperator("distance_lte", _gis_distance_operator),
124-
"dwithin": SpatialOperator("dwithin", _gis_dwithin_operator),
125-
}
46+
return self.operators
12647

12748
unsupported_functions = {
12849
"Area",
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from django.db import NotSupportedError
2+
3+
4+
class Operator:
5+
def as_sql(self, connection, lookup, template_params, sql_params):
6+
# Return some dummy value to prevent str(queryset.query) from crashing.
7+
# The output of as_sql() is meaningless for this no-SQL backend.
8+
return self.name, []
9+
10+
11+
class Within(Operator):
12+
name = "within"
13+
14+
def as_mql(self, field, value, params=None):
15+
return {
16+
field: {
17+
"$geoWithin": {
18+
"$geometry": {
19+
"type": value["type"],
20+
"coordinates": value["coordinates"],
21+
}
22+
}
23+
}
24+
}
25+
26+
27+
class Intersects(Operator):
28+
name = "intersects"
29+
30+
def as_mql(self, field, value, params=None):
31+
return {
32+
field: {
33+
"$geoIntersects": {
34+
"$geometry": {
35+
"type": value["type"],
36+
"coordinates": value["coordinates"],
37+
}
38+
}
39+
}
40+
}
41+
42+
43+
class Disjoint(Operator):
44+
name = "disjoint"
45+
46+
def as_mql(self, field, value, params=None):
47+
return {
48+
field: {
49+
"$not": {
50+
"$geoIntersects": {
51+
"$geometry": {
52+
"type": value["type"],
53+
"coordinates": value["coordinates"],
54+
}
55+
}
56+
}
57+
}
58+
}
59+
60+
61+
class Contains(Operator):
62+
name = "contains"
63+
64+
def as_mql(self, field, value, params=None):
65+
value_type = value["type"]
66+
if value_type != "Point":
67+
raise NotSupportedError(
68+
"MongoDB does not support contains on non-Point query geometries."
69+
)
70+
return {
71+
field: {
72+
"$geoIntersects": {
73+
"$geometry": {
74+
"type": value_type,
75+
"coordinates": value["coordinates"],
76+
}
77+
}
78+
}
79+
}
80+
81+
82+
class DistanceBase(Operator):
83+
name = "distance_base"
84+
85+
def as_mql(self, field, value, params=None):
86+
distance = params[0].m if hasattr(params[0], "m") else params[0]
87+
if self.name == "distance_gt" or self.name == "distance_gte":
88+
cmd = {
89+
field: {
90+
"$not": {
91+
"$geoWithin": {
92+
"$centerSphere": [
93+
value["coordinates"],
94+
distance / 6378100, # radius of earth in meters
95+
],
96+
}
97+
}
98+
}
99+
}
100+
else:
101+
cmd = {
102+
field: {
103+
"$geoWithin": {
104+
"$centerSphere": [
105+
value["coordinates"],
106+
distance / 6378100, # radius of earth in meters
107+
],
108+
}
109+
}
110+
}
111+
return cmd
112+
113+
114+
class DistanceGT(DistanceBase):
115+
name = "distance_gt"
116+
117+
118+
class DistanceGTE(DistanceBase):
119+
name = "distance_gte"
120+
121+
122+
class DistanceLT(DistanceBase):
123+
name = "distance_lt"
124+
125+
126+
class DistanceLTE(DistanceBase):
127+
name = "distance_lte"
128+
129+
130+
class DWithin(Operator):
131+
name = "dwithin"
132+
133+
def as_mql(self, field, value, params=None):
134+
return {field: {"$geoWithin": {"$centerSphere": [value["coordinates"], params[0]]}}}

django_mongodb_backend/gis/utils.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)