-
Notifications
You must be signed in to change notification settings - Fork 31
INTPYTHON-835 Add support for GIS lookups #448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| "GIS Union not supported.": { | ||
| "gis_tests.geoapp.tests.GeoLookupTest.test_gis_lookups_with_complex_expressions", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self that we should add @skipUnlessDBFeature("supports_Union_function") and send the patch upstream. I'll hold off in case any more similar changes are identified by this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple other places where tests not marked as requiring a feature use it. Maybe the authors didn't expect a backend to implement only the slice of operations that we do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. The skipping is generally just best effort... what's needed for the built-in backends.
|
All tests (including some simple ones I added due to many of the Django ones requiring features we don't support) passing, marking as ready for an initial review pass. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
tests/gis_tests_/tests.py
Outdated
| qs = City.objects.filter(point__dwithin=(houston.point, 0.2)) | ||
| self.assertEqual(qs.count(), 5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about asserting the results and not using count applies to other tests. In this case, you could use City.objects.exclude() to produce fewer results.
tests/gis_tests_/tests.py
Outdated
| def test_within(self): | ||
| zipcode = Zipcode.objects.get(code="77002") | ||
| qs = City.objects.filter(point__within=zipcode.poly).values_list("name", flat=True) | ||
| self.assertEqual(list(qs), ["Houston"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.assertCountEqual(qs, ["Houston"]) also works if you want be consistent with the other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add tests that check:
- the MQL structure
- the query in tandem with other queries
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if there's a need to verify the MQL of every query. These assertions tend to add a lot of overhead in the event of query refactors. The Django test suite generally doesn't verify queries since the syntax varies among databases. Do you have a specific concern?
Please clarify what "in tandem with other queries" means. (Subqueries aren't supported.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine on no MQL for the query.
For "in tandem queries" I meant having a query where there are multiple queries:
City.objects.filter(name="text", point__distance_lte=(houston.point, 362826))There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not very familiar with the django test suite, but is it safe to assume that such a query is covered already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly yes, so good point e.g.
src/django/tests/gis_tests/geoapp/test_functions.py: self.assertSequenceEqual(City.objects.filter(point__isempty=True), [empty])
src/django/tests/gis_tests/geoapp/test_functions.py: qs = City.objects.filter(point__isnull=False).annotate(
src/django/tests/gis_tests/geoapp/test_functions.py: .filter(geom__geom_type=geom_type)
src/django/tests/gis_tests/geoapp/tests.py: query = City.objects.filter(point__within=Polygon.from_bbox((0, 0, 2, 2)))
src/django/tests/gis_tests/geoapp/tests.py: qs1 = City.objects.filter(point__disjoint=ptown.point)
src/django/tests/gis_tests/geoapp/tests.py: qs2 = State.objects.filter(poly__disjoint=ptown.point)
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(point__contained=texas.mpoly)
src/django/tests/gis_tests/geoapp/tests.py: len(Country.objects.filter(mpoly__contains=pueblo.point)), 0
src/django/tests/gis_tests/geoapp/tests.py: len(Country.objects.filter(mpoly__contains=okcity.point.wkt)), 0
src/django/tests/gis_tests/geoapp/tests.py: qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
src/django/tests/gis_tests/geoapp/tests.py: State.objects.filter(name="Kansas", poly__isvalid=False).count(), 1
src/django/tests/gis_tests/geoapp/tests.py: self.assertEqual(qs.filter(poly__isvalid=False).count(), 1)
src/django/tests/gis_tests/geoapp/tests.py: self.assertEqual(qs.filter(poly__isvalid=True).count(), qs.count() - 1)
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(point__right=co_border)
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(point__right=ks_border)
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(point__left=ks_border)
src/django/tests/gis_tests/geoapp/tests.py: City.objects.filter(point__strictly_above=dallas.point).order_by("name"),
src/django/tests/gis_tests/geoapp/tests.py: City.objects.filter(point__strictly_below=dallas.point).order_by("name"),
src/django/tests/gis_tests/geoapp/tests.py: nullqs = State.objects.filter(poly__isnull=True)
src/django/tests/gis_tests/geoapp/tests.py: validqs = State.objects.filter(poly__isnull=False)
src/django/tests/gis_tests/geoapp/tests.py: null, State.objects.filter(**{"poly__%s" % lookup: geom})
src/django/tests/gis_tests/geoapp/tests.py: State.objects.filter(poly__intersects="LINESTRING(0 0, 1 1, 5 5)")
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__coveredby=small_poly)
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__coveredby=large_poly)
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__coveredby=poly)
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__covers=small_poly)
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__covers=large_poly)
src/django/tests/gis_tests/geoapp/tests.py: qs = State.objects.filter(poly__covers=poly)
src/django/tests/gis_tests/geoapp/tests.py: Country.objects.filter(mpoly__relate=(23, "foo"))
src/django/tests/gis_tests/geoapp/tests.py: qs = Country.objects.filter(mpoly__relate=bad_args)
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(name__in=("Houston", "Dallas"))
src/django/tests/gis_tests/geoapp/tests.py: qs = City.objects.filter(point__within=tx)
src/django/tests/gis_tests/geoapp/tests.py: City.objects.filter(point__within=tx.mpoly)
src/django/tests/gis_tests/geoapp/tests.py: City.objects.filter(point__within=tx).aggregate(
src/django/tests/gis_tests/geoapp/tests.py: City.objects.filter(point__within=tx).aggregate(
src/django/tests/gis_tests/geoapp/tests.py: point__within=Country.objects.filter(name="Texas").values("mpoly")
src/django/tests/gis_tests/geoapp/test_regress.py: state = State.objects.filter(poly__contains=pueblo.point)
src/django/tests/gis_tests/geoapp/test_regress.py: cities_within_state = City.objects.filter(id__in=state)
src/django/tests/gis_tests/layermap/tests.py: self.assertEqual(HasNulls.objects.filter(num__isnull=True).count(), 1)
src/django/tests/gis_tests/layermap/tests.py: self.assertEqual(HasNulls.objects.filter(name__isnull=True).count(), 1)
src/django/tests/gis_tests/layermap/tests.py: self.assertEqual(HasNulls.objects.filter(boolean__isnull=True).count(), 1)
src/django/tests/gis_tests/layermap/tests.py: HasNulls.objects.filter(datetime__lt=datetime.date(1994, 8, 15)).count(), 1
src/django/tests/gis_tests/layermap/tests.py: self.assertEqual(HasNulls.objects.filter(datetime__isnull=True).count(), 1)
src/django/tests/gis_tests/geogapp/tests.py: City.objects.filter(point__distance_lte=(z.poly, D(mi=500)))
src/django/tests/gis_tests/geogapp/tests.py: City.objects.filter(point__dwithin=(z.poly, D(mi=500)))
src/django/tests/gis_tests/geogapp/tests.py: qs = City.objects.filter(point__within=z.poly)
src/django/tests/gis_tests/geogapp/tests.py: qs = City.objects.filter(point__contained=z.poly)
src/django/tests/gis_tests/geogapp/tests.py: qs = City.objects.filter(point__exact=htown.point)
src/django/tests/gis_tests/geogapp/tests.py: res = City.objects.filter(name__in=("Houston", "Dallas")).aggregate(
src/django/tests/gis_tests/relatedapp/tests.py: qs = Parcel.objects.filter(center1__within=F("border1"))
src/django/tests/gis_tests/relatedapp/tests.py: qs = Parcel.objects.filter(center2__within=F("border1"))
src/django/tests/gis_tests/relatedapp/tests.py: qs = Parcel.objects.filter(center1=F("city__location__point"))
src/django/tests/gis_tests/relatedapp/tests.py: qs = Parcel.objects.filter(border2__contains=F("city__location__point"))
src/django/tests/gis_tests/relatedapp/tests.py: qs1 = City.objects.filter(location__point__within=buf1)
src/django/tests/gis_tests/relatedapp/tests.py: qs2 = City.objects.filter(location__point__within=buf2)
src/django/tests/gis_tests/relatedapp/tests.py: qs = Author.objects.annotate(num_books=Count("books")).filter(num_books__gt=1)
src/django/tests/gis_tests/relatedapp/tests.py: .filter(num_books__gt=1)
src/django/tests/gis_tests/relatedapp/tests.py: coll = City.objects.filter(state="TX").aggregate(Collect("location__point"))[
src/django/tests/gis_tests/relatedapp/tests.py: filter=~Q(parcel__name__icontains="ignore"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__icontains="nonexistent"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__contains="Alpha"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=~Q(parcel__name__icontains="ignore"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=~Q(parcel__name__icontains="ignore"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__icontains="nonexistent"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=~Q(parcel__name__icontains="ignore"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__icontains="nonexistent"),
src/django/tests/gis_tests/relatedapp/tests.py: parcel_border_no_filter=Extent("parcel__border1"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=~Q(parcel__name__icontains="ignore"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__icontains="nonexistent"),
src/django/tests/gis_tests/relatedapp/tests.py: filter=Q(parcel__name__contains="Alpha"),
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rastprojected__dwithin=(rast, D(km=1)))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rastprojected__dwithin=(JSON_RASTER, D(km=1)))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__dwithin=(rast, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__1__dwithin=(rast, 1, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__1__dwithin=(rast, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__dwithin=(rast, 1, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__dwithin=(stx_pnt, 500))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rastprojected__dwithin=(stx_pnt, D(km=10000)))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__dwithin=(stx_pnt, 5))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rastprojected__dwithin=(stx_pnt, D(km=100)))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(geom__dwithin=(rast, 500))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterRelatedModel.objects.filter(rastermodel__rast__dwithin=(rast, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterRelatedModel.objects.filter(rastermodel__rast__1__dwithin=(rast, 40))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rastprojected__bbcontains=rast)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: RasterModel.objects.filter(rast__bbcontains=(rast, 1, 2))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__bbcontains=(rast, 1))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__isvalid=True)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__left=GEOSGeometry("POINT (1 0)", 4326))
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__strictly_below=rast)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(rast__strictly_below=rast)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(geom__intersects=rast)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: qs = RasterModel.objects.filter(geom__intersects=rast)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: RasterModel.objects.filter(geom__intersects=obj)
src/django/tests/gis_tests/rasterapp/test_rasterfield.py: RasterModel.objects.filter(geom__intersects=obj)
src/django/tests/gis_tests/geo3d/tests.py: ll_cities=Extent3D("point", filter=Q(name__contains="ll"))
src/django/tests/gis_tests/distapp/tests.py: qs1 = SouthTexasCity.objects.filter(point__dwithin=(self.stx_pnt, dist1))
src/django/tests/gis_tests/distapp/tests.py: qs2 = SouthTexasCityFt.objects.filter(point__dwithin=(self.stx_pnt, dist2))
src/django/tests/gis_tests/distapp/tests.py: self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))),
src/django/tests/gis_tests/distapp/tests.py: qs = model.objects.filter(point__distance_gte=(stx_pnt, D(km=7))).filter(
src/django/tests/gis_tests/distapp/tests.py: dist_qs = AustraliaCity.objects.filter(point__distance_lte=(line, D(km=100)))
src/django/tests/gis_tests/distapp/tests.py: len(AustraliaCity.objects.filter(point__distance_lte=("POINT(5 23)",)))
src/django/tests/gis_tests/distapp/tests.py: .filter(name__in=("San Antonio", "Pearland"))
src/django/tests/gis_tests/distapp/tests.py: .filter(length__gt=4000)
That said, off hand I'm not sure how much of the GIS test suite we are running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The terminology for this type of QuerySet is that it has "multiple lookups." This sort of test could be useful if there's any doubt that any of the GIS lookups generate MQL that isn't generally composable with other MQL operators like $and, $or, etc.
A quick manual check confirms no issue with a case like this:
City.objects.filter(name="Houston", point__disjoint=Point(100, 50))
{'$match': {'point': {'$not': {'$geoIntersects': {'$geometry': {'type': 'Point', 'coordinates': (100.0, 50.0)}}}}}}, {'$project': {'name': 1}}])
City.objects.filter(point__disjoint=Point(100, 50))
{'$match': {'$and': [{'name': 'Houston'}, {'point': {'$not': {'$geoIntersects': {'$geometry': {'type': 'Point', 'coordinates': (100.0, 50.0)}}}}}]}}.
Is there any reason to doubt other cases?
| if self.name == "distance_gt" or self.name == "distance_gte": | ||
| cmd = { | ||
| field: { | ||
| "$not": { | ||
| "$geoWithin": { | ||
| "$centerSphere": [ | ||
| value["coordinates"], | ||
| distance / 6378100, # radius of earth in meters | ||
| ], | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this just be moved to DistanceGT and DistanceGTE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you be more specific? Having it here lets us reduce the code duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could see a refactor like this: [Incidentally, is gt vs. gte accounted for elsewhere?)
diff --git a/django_mongodb_backend/gis/operators.py b/django_mongodb_backend/gis/operators.py
index cc24473..3824de5 100644
--- a/django_mongodb_backend/gis/operators.py
+++ b/django_mongodb_backend/gis/operators.py
@@ -52,22 +52,28 @@ class DistanceBase(Operator):
def as_mql(self, field, value, params=None):
distance = params[0].m if hasattr(params[0], "m") else params[0]
- if self.name == "distance_gt" or self.name == "distance_gte":
- cmd = {
- field: {
- "$not": {
- "$geoWithin": {
- "$centerSphere": [
- value["coordinates"],
- distance / 6378100, # radius of earth in meters
- ],
- }
- }
+ return self.get_mql(field, value, distance):
+
+ def get_mql(self, field, value, distance):
+ return {
+ field: {
+ "$geoWithin": {
+ "$centerSphere": [
+ value["coordinates"],
+ distance / 6378100, # radius of earth in meters
+ ],
}
}
+ return self.get_mql(field, value, distance):
+
+ def get_mql(self, field, value, distance):
+ return {
+ field: {
+ "$geoWithin": {
+ "$centerSphere": [
+ value["coordinates"],
+ distance / 6378100, # radius of earth in meters
+ ],
}
}
- else:
- cmd = {
- field: {
+ }
+
+
+class DistanceGT(DistanceBase):
+ name = "distance_gt"
+
+ def get_mql(self, field, value, distance):
+ return {
+ field: {
+ "$not": {
"$geoWithin": {
"$centerSphere": [
value["coordinates"],
@@ -76,14 +82,9 @@ class DistanceBase(Operator):
}
}
}
- return cmd
-
-
-class DistanceGT(DistanceBase):
- name = "distance_gt"
-
+ }
-class DistanceGTE(DistanceBase):
+class DistanceGTE(DistanceGT):
name = "distance_gte"
| "gis_tests.distapp.tests.DistanceTest.test_dwithin", | ||
| }, | ||
| "Cannot use a non-Point geometry with distance lookups.": { | ||
| "gis_tests.distapp.tests.DistanceTest.test_dwithin_with_expression_rhs" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't fail gracefully:
======================================================================
ERROR: test_dwithin_with_expression_rhs (gis_tests.distapp.tests.DistanceTest.test_dwithin_with_expression_rhs)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/tim/code/django/tests/gis_tests/utils.py", line 24, in skip_wrapper
return test_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/django/test/testcases.py", line 1588, in skip_wrapper
return test_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/tests/gis_tests/distapp/tests.py", line 347, in test_dwithin_with_expression_rhs
self.get_names(qs),
^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/tests/gis_tests/distapp/tests.py", line 52, in get_names
cities = [c.name for c in qs]
^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/django/db/models/query.py", line 390, in __iter__
self._fetch_all()
File "/home/tim/code/django/django/db/models/query.py", line 2000, in _fetch_all
self._result_cache = list(self._iterable_class(self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/django/db/models/query.py", line 95, in __iter__
results = compiler.execute_sql(
^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/compiler.py", line 355, in execute_sql
cursor = query.get_cursor()
^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/query.py", line 20, in wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/query.py", line 79, in get_cursor
return self.compiler.collection.aggregate(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/collection.py", line 2979, in aggregate
return self._aggregate(
^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/_csot.py", line 125, in csot_wrapper
return func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/collection.py", line 2886, in _aggregate
return self._database.client._retryable_read(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/mongo_client.py", line 2026, in _retryable_read
return self._retry_internal(
^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/_csot.py", line 125, in csot_wrapper
return func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/mongo_client.py", line 1993, in _retry_internal
).run()
^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/mongo_client.py", line 2730, in run
return self._read() if self._is_read else self._write()
^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/mongo_client.py", line 2891, in _read
return self._func(self._session, self._server, conn, read_pref) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/aggregation.py", line 164, in get_cursor
result = conn.command(
^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/helpers.py", line 47, in inner
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/pool.py", line 442, in command
self._raise_connection_failure(error)
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/pool.py", line 414, in command
return command(
^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/synchronous/network.py", line 148, in command
request_id, msg, size, max_doc_size = message._op_msg(
^^^^^^^^^^^^^^^^
File "/home/tim/.virtualenvs/django312/lib/python3.12/site-packages/pymongo/message.py", line 419, in _op_msg
return _op_msg_uncompressed(flags, command, identifier, docs, opts)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
bson.errors.InvalidDocument: Invalid document {'aggregate': 'distapp_australiacity', 'pipeline': [{'$match': {'point': {'$geoWithin': {'$centerSphere': [((150.902, -34.4245), (138.6, -34.9258)), Col(distapp_australiacity, distapp.AustraliaCity.allowed_distance)]}}}}, {'$addFields': {'name': '$name'}}, {'$sort': SON([('name', 1)])}], 'cursor': {}, 'lsid': {'id': Binary(b'\xea\x82\xe9\x12\x19GO\x0e\x87\xfb\xeb\xd0\xe9\xfd\x9bJ', 4)}, '$clusterTime': {'clusterTime': Timestamp(1765932487, 8), 'signature': {'hash': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'keyId': 0}}, '$db': 'test_django', '$readPreference': {'mode': 'primaryPreferred'}} | cannot encode object: Col(distapp_australiacity, distapp.AustraliaCity.allowed_distance), of type: <class 'django.db.models.expressions.Col'>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed adding the supports_dwithin_distance_expr=False flag here that gives a better error message.
| "gis_tests.distapp.tests.DistanceTest.test_dwithin_with_expression_rhs" | ||
| }, | ||
| "Subqueries not supported.": { | ||
| "gis_tests.geoapp.tests.GeoLookupTest.test_subquery_annotation", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't fail gracefully:
======================================================================
ERROR: test_subquery_annotation (gis_tests.geoapp.tests.GeoLookupTest.test_subquery_annotation)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/tim/code/django/tests/gis_tests/utils.py", line 24, in skip_wrapper
return test_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/tests/gis_tests/geoapp/tests.py", line 695, in test_subquery_annotation
self.assertEqual(qs.get(), multifields)
^^^^^^^^
File "/home/tim/code/django/django/db/models/query.py", line 635, in get
num = len(clone)
^^^^^^^^^^
File "/home/tim/code/django/django/db/models/query.py", line 372, in __len__
self._fetch_all()
File "/home/tim/code/django/django/db/models/query.py", line 2000, in _fetch_all
self._result_cache = list(self._iterable_class(self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django/django/db/models/query.py", line 95, in __iter__
results = compiler.execute_sql(
^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/compiler.py", line 346, in execute_sql
query = self.build_query(
^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/compiler.py", line 477, in build_query
match_mql = where.as_mql(self, self.connection) if where else {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/query.py", line 354, in where_node
mql = child.as_mql(compiler, connection, as_expr=as_expr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/gis/lookups.py", line 16, in gis_lookup
return rhs_op.as_mql(lhs_mql, rhs_mql, self.rhs_params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/tim/code/django-mongodb/django_mongodb_backend/gis/operators.py", line 129, in as_mql
"type": value["type"],
~~~~~^^^^^^^^
TypeError: string indices must be integers, not 'str'
| "gis_tests.distapp.tests.DistanceTest.test_distance_lookups", | ||
| "gis_tests.distapp.tests.DistanceTest.test_distance_lookups_with_expression_rhs", | ||
| "gis_tests.distapp.tests.DistanceTest.test_distance_annotation_group_by", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed these since they are already skipped by: @skipUnlessDBFeature("supports_distances_lookups") which is an alias for DatabaseFeatures.has_Distance_function (probably the confusion was from when you were figuring things out).
dc2f504 to
924523e
Compare
PR docs: https://django-mongodb-backend--448.org.readthedocs.build/en/448/