-
Notifications
You must be signed in to change notification settings - Fork 2
Enable properties / filters in geospatial search #114
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||
| from typing import Dict, Any | ||||||
| import json | ||||||
|
|
||||||
| from fastapi.testclient import TestClient | ||||||
| from pymongo.database import Database | ||||||
|
|
@@ -292,6 +293,163 @@ def test_geo_bounding_box_invalid_coordinates( | |||||
| error_data = response.json() | ||||||
| assert "latitude" in error_data["detail"].lower() | ||||||
|
|
||||||
| def test_geo_nearby_search_with_filter(self, test_client: TestClient, seeded_db: Database): | ||||||
| """Test geographic nearby search with additional filter.""" | ||||||
|
|
||||||
| # Search near the EMSL coordinates with a filter for EMSL data source | ||||||
| params = { | ||||||
| "latitude": 34.0, | ||||||
| "longitude": 118.0, | ||||||
| "radius_meters": 100000, # 100km radius | ||||||
| "filter_json": json.dumps({"ber_data_source": "EMSL"}) | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/nearby", params=params) | ||||||
|
|
||||||
| assert response.status_code == status.HTTP_200_OK | ||||||
| entities_data = response.json() | ||||||
|
|
||||||
| assert "documents" in entities_data | ||||||
| assert "count" in entities_data | ||||||
|
|
||||||
| # All returned entities should be from EMSL | ||||||
| for entity in entities_data["documents"]: | ||||||
| assert entity["ber_data_source"] == "EMSL" | ||||||
| self._verify_entity_structure(entity) | ||||||
|
|
||||||
| # Should find at least one entity | ||||||
| assert entities_data["count"] > 0 | ||||||
|
|
||||||
| def test_geo_nearby_search_with_invalid_filter_json(self, test_client: TestClient, seeded_db: Database): | ||||||
| """Test geographic nearby search with invalid JSON filter.""" | ||||||
| params = { | ||||||
| "latitude": 34.0, | ||||||
| "longitude": 118.0, | ||||||
| "radius_meters": 100000, | ||||||
| "filter_json": "invalid json {" # Invalid JSON | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/nearby", params=params) | ||||||
| assert response.status_code == status.HTTP_400_BAD_REQUEST | ||||||
|
|
||||||
| def test_geo_bbox_search_with_filter(self, test_client: TestClient, seeded_db: Database): | ||||||
| """Test geographic bounding box search with additional filter.""" | ||||||
|
|
||||||
| # Bounding box around Alaska with filter for ESS-DIVE data source | ||||||
| params = { | ||||||
| "southwest_lat": 64.0, | ||||||
| "southwest_lng": -166.0, | ||||||
| "northeast_lat": 66.0, | ||||||
| "northeast_lng": -163.0, | ||||||
| "filter_json": json.dumps({"ber_data_source": "ESS-DIVE"}) | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/bbox", params=params) | ||||||
|
|
||||||
| assert response.status_code == status.HTTP_200_OK | ||||||
| entities_data = response.json() | ||||||
|
|
||||||
| assert "documents" in entities_data | ||||||
| assert "count" in entities_data | ||||||
|
|
||||||
| # All returned entities should be from ESS-DIVE and within bounding box | ||||||
| for entity in entities_data["documents"]: | ||||||
| assert entity["ber_data_source"] == "ESS-DIVE" | ||||||
| # Verify coordinates are within bounding box | ||||||
| lat = entity["coordinates"]["latitude"] | ||||||
| lng = entity["coordinates"]["longitude"] | ||||||
| assert 64.0 <= lat <= 66.0 | ||||||
| assert -166.0 <= lng <= -163.0 | ||||||
| self._verify_entity_structure(entity) | ||||||
|
|
||||||
| def test_geo_bbox_search_with_empty_filter(self, test_client: TestClient, seeded_db: Database): | ||||||
| """Test geographic bounding box search with empty filter (should work like no filter).""" | ||||||
|
|
||||||
| # Bounding box around Alaska with empty filter | ||||||
| params = { | ||||||
| "southwest_lat": 64.0, | ||||||
| "southwest_lng": -166.0, | ||||||
| "northeast_lat": 66.0, | ||||||
| "northeast_lng": -163.0, | ||||||
| "filter_json": json.dumps({}) # Empty filter | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/bbox", params=params) | ||||||
|
|
||||||
| assert response.status_code == status.HTTP_200_OK | ||||||
| entities_data = response.json() | ||||||
|
|
||||||
| assert "documents" in entities_data | ||||||
| assert "count" in entities_data | ||||||
|
|
||||||
| # Should find entities regardless of data source (empty filter = no additional restrictions) | ||||||
| for entity in entities_data["documents"]: | ||||||
| self._verify_entity_structure(entity) | ||||||
|
Comment on lines
+385
to
+387
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the comment mentions the data source, I suggest adding an assertion that multiple data sources are represented in the response. The test can get a list of data sources directly from the database beforehand, for comparison. |
||||||
|
|
||||||
| def test_geo_search_filter_with_complex_query(self, test_client: TestClient, seeded_db: Database): | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this illustrative test. |
||||||
| """Test geospatial search with more complex filter query.""" | ||||||
|
|
||||||
| # Test with a more complex filter using MongoDB operators | ||||||
| params = { | ||||||
| "latitude": 34.0, | ||||||
| "longitude": 118.0, | ||||||
| "radius_meters": 500000, # Larger radius to catch more entities | ||||||
| "filter_json": json.dumps({ | ||||||
| "entity_type": {"$in": ["sample", "study"]}, | ||||||
| "ber_data_source": {"$ne": "JGI"} # Exclude JGI data | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/nearby", params=params) | ||||||
|
|
||||||
| assert response.status_code == status.HTTP_200_OK | ||||||
| entities_data = response.json() | ||||||
|
|
||||||
| assert "documents" in entities_data | ||||||
| assert "count" in entities_data | ||||||
|
|
||||||
| # Verify filter conditions are met | ||||||
| for entity in entities_data["documents"]: | ||||||
| # Should have entity_type containing "sample" or "study" | ||||||
| entity_types = entity.get("entity_type", []) | ||||||
| assert any(et in ["sample", "study"] for et in entity_types) | ||||||
| # Should not be from JGI | ||||||
| assert entity["ber_data_source"] != "JGI" | ||||||
| self._verify_entity_structure(entity) | ||||||
|
|
||||||
| def test_geosearch_with_properties(self, test_client: TestClient, seeded_db: Database): | ||||||
| """Test searching entities by properties.""" | ||||||
|
|
||||||
| # Search for entities with a specific property label | ||||||
| params = { | ||||||
| "latitude": 28.125842, | ||||||
| "longitude": -81.434174, | ||||||
| "radius_meters": 1000000, # 1000km radius to include NMDC entity | ||||||
| "filter_json": json.dumps({ | ||||||
| "properties.attribute.label": "depth", | ||||||
| "properties.numeric_value": 24 | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| response = test_client.get("/bertron/geo/nearby", params=params) | ||||||
|
|
||||||
| assert response.status_code == status.HTTP_200_OK | ||||||
| entities_data = response.json() | ||||||
|
|
||||||
| assert "documents" in entities_data | ||||||
| assert "count" in entities_data | ||||||
|
|
||||||
| # Should find at least the NMDC entity with depth property | ||||||
| found_nmdc = False | ||||||
| for entity in entities_data["documents"]: | ||||||
| properties = [ prop["attribute"]["label"] for prop in entity.get("properties", []) ] | ||||||
|
||||||
| properties = [ prop["attribute"]["label"] for prop in entity.get("properties", []) ] | |
| properties = [prop["attribute"]["label"] for prop in entity.get("properties", [])] |
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.
Add an assertion that the value of the retrieved property is 24 (like in the specified filter).
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.
Nitpick (optional): Swap the order of these blocks (assert there are items, then check the items).