Skip to content

Commit 9ab8e99

Browse files
committed
create shared filter methods
1 parent 820c028 commit 9ab8e99

File tree

3 files changed

+154
-58
lines changed

3 files changed

+154
-58
lines changed

stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
)
2323
from stac_fastapi.sfeos_helpers import filter
2424
from stac_fastapi.sfeos_helpers.database_logic_helpers import (
25+
apply_free_text_filter_shared,
26+
apply_intersects_filter_shared,
2527
create_index_templates_shared,
28+
populate_sort_shared,
2629
)
2730
from stac_fastapi.sfeos_helpers.mappings import (
2831
COLLECTIONS_INDEX,
@@ -483,21 +486,8 @@ def apply_intersects_filter(
483486
Notes:
484487
A geo_shape filter is added to the search object, set to intersect with the specified geometry.
485488
"""
486-
return search.filter(
487-
Q(
488-
{
489-
"geo_shape": {
490-
"geometry": {
491-
"shape": {
492-
"type": intersects.type.lower(),
493-
"coordinates": intersects.coordinates,
494-
},
495-
"relation": "intersects",
496-
}
497-
}
498-
}
499-
)
500-
)
489+
filter = apply_intersects_filter_shared(intersects=intersects)
490+
return search.filter(Q(filter))
501491

502492
@staticmethod
503493
def apply_stacql_filter(search: Search, op: str, field: str, value: float):
@@ -523,14 +513,21 @@ def apply_stacql_filter(search: Search, op: str, field: str, value: float):
523513

524514
@staticmethod
525515
def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]):
526-
"""Database logic to perform query for search endpoint."""
527-
if free_text_queries is not None:
528-
free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries)
529-
search = search.query(
530-
"query_string", query=f'properties.\\*:"{free_text_query_string}"'
531-
)
516+
"""Create a free text query for Elasticsearch queries.
532517
533-
return search
518+
This method delegates to the shared implementation in apply_free_text_filter_shared.
519+
520+
Args:
521+
search (Search): The search object to apply the query to.
522+
free_text_queries (Optional[List[str]]): A list of text strings to search for in the properties.
523+
524+
Returns:
525+
Search: The search object with the free text query applied, or the original search
526+
object if no free_text_queries were provided.
527+
"""
528+
return apply_free_text_filter_shared(
529+
search=search, free_text_queries=free_text_queries
530+
)
534531

535532
async def apply_cql2_filter(
536533
self, search: Search, _filter: Optional[Dict[str, Any]]
@@ -561,11 +558,18 @@ async def apply_cql2_filter(
561558

562559
@staticmethod
563560
def populate_sort(sortby: List) -> Optional[Dict[str, Dict[str, str]]]:
564-
"""Database logic to sort search instance."""
565-
if sortby:
566-
return {s.field: {"order": s.direction} for s in sortby}
567-
else:
568-
return None
561+
"""Create a sort configuration for Elasticsearch queries.
562+
563+
This method delegates to the shared implementation in populate_sort_shared.
564+
565+
Args:
566+
sortby (List): A list of sort specifications, each containing a field and direction.
567+
568+
Returns:
569+
Optional[Dict[str, Dict[str, str]]]: A dictionary mapping field names to sort direction
570+
configurations, or None if no sort was specified.
571+
"""
572+
return populate_sort_shared(sortby=sortby)
569573

570574
async def execute_search(
571575
self,

stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
from stac_fastapi.opensearch.config import OpensearchSettings as SyncSearchSettings
2323
from stac_fastapi.sfeos_helpers import filter
2424
from stac_fastapi.sfeos_helpers.database_logic_helpers import (
25+
apply_free_text_filter_shared,
26+
apply_intersects_filter_shared,
2527
create_index_templates_shared,
28+
populate_sort_shared,
2629
)
2730
from stac_fastapi.sfeos_helpers.mappings import (
2831
COLLECTIONS_INDEX,
@@ -340,14 +343,21 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
340343

341344
@staticmethod
342345
def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]):
343-
"""Database logic to perform query for search endpoint."""
344-
if free_text_queries is not None:
345-
free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries)
346-
search = search.query(
347-
"query_string", query=f'properties.\\*:"{free_text_query_string}"'
348-
)
346+
"""Create a free text query for OpenSearch queries.
349347
350-
return search
348+
This method delegates to the shared implementation in apply_free_text_filter_shared.
349+
350+
Args:
351+
search (Search): The search object to apply the query to.
352+
free_text_queries (Optional[List[str]]): A list of text strings to search for in the properties.
353+
354+
Returns:
355+
Search: The search object with the free text query applied, or the original search
356+
object if no free_text_queries were provided.
357+
"""
358+
return apply_free_text_filter_shared(
359+
search=search, free_text_queries=free_text_queries
360+
)
351361

352362
@staticmethod
353363
def apply_datetime_filter(search: Search, datetime_search):
@@ -513,21 +523,8 @@ def apply_intersects_filter(
513523
Notes:
514524
A geo_shape filter is added to the search object, set to intersect with the specified geometry.
515525
"""
516-
return search.filter(
517-
Q(
518-
{
519-
"geo_shape": {
520-
"geometry": {
521-
"shape": {
522-
"type": intersects.type.lower(),
523-
"coordinates": intersects.coordinates,
524-
},
525-
"relation": "intersects",
526-
}
527-
}
528-
}
529-
)
530-
)
526+
filter = apply_intersects_filter_shared(intersects=intersects)
527+
return search.filter(Q(filter))
531528

532529
@staticmethod
533530
def apply_stacql_filter(search: Search, op: str, field: str, value: float):
@@ -580,11 +577,18 @@ async def apply_cql2_filter(
580577

581578
@staticmethod
582579
def populate_sort(sortby: List) -> Optional[Dict[str, Dict[str, str]]]:
583-
"""Database logic to sort search instance."""
584-
if sortby:
585-
return {s.field: {"order": s.direction} for s in sortby}
586-
else:
587-
return None
580+
"""Create a sort configuration for OpenSearch queries.
581+
582+
This method delegates to the shared implementation in populate_sort_shared.
583+
584+
Args:
585+
sortby (List): A list of sort specifications, each containing a field and direction.
586+
587+
Returns:
588+
Optional[Dict[str, Dict[str, str]]]: A dictionary mapping field names to sort direction
589+
configurations, or None if no sort was specified.
590+
"""
591+
return populate_sort_shared(sortby=sortby)
588592

589593
async def execute_search(
590594
self,

stac_fastapi/sfeos_helpers/stac_fastapi/sfeos_helpers/database_logic_helpers.py

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
"""Shared code for elasticsearch/ opensearch database logic."""
22

3-
from typing import Any
3+
from typing import Any, Dict, List, Optional
44

55
from stac_fastapi.sfeos_helpers.mappings import (
66
COLLECTIONS_INDEX,
77
ES_COLLECTIONS_MAPPINGS,
88
ES_ITEMS_MAPPINGS,
99
ES_ITEMS_SETTINGS,
1010
ITEMS_INDEX_PREFIX,
11+
Geometry,
1112
)
1213

1314

1415
async def create_index_templates_shared(settings: Any) -> None:
15-
"""
16-
Create index templates for the Collection and Item indices.
16+
"""Create index templates for Elasticsearch/OpenSearch Collection and Item indices.
17+
18+
Args:
19+
settings (Any): The settings object containing the client configuration.
20+
Must have a create_client attribute that returns an Elasticsearch/OpenSearch client.
1721
1822
Returns:
19-
None
23+
None: This function doesn't return any value but creates index templates in the database.
2024
25+
Notes:
26+
This function creates two index templates:
27+
1. A template for the Collections index with the appropriate mappings
28+
2. A template for the Items indices with both settings and mappings
29+
30+
These templates ensure that any new indices created with matching patterns
31+
will automatically have the correct structure.
2132
"""
2233
client = settings.create_client
2334
await client.indices.put_index_template(
@@ -35,3 +46,80 @@ async def create_index_templates_shared(settings: Any) -> None:
3546
},
3647
)
3748
await client.close()
49+
50+
51+
def apply_free_text_filter_shared(
52+
search: Any, free_text_queries: Optional[List[str]]
53+
) -> Any:
54+
"""Create a free text query for Elasticsearch/OpenSearch.
55+
56+
Args:
57+
search (Any): The search object to apply the query to.
58+
free_text_queries (Optional[List[str]]): A list of text strings to search for in the properties.
59+
60+
Returns:
61+
Any: The search object with the free text query applied, or the original search
62+
object if no free_text_queries were provided.
63+
64+
Notes:
65+
This function creates a query_string query that searches for the specified text strings
66+
in all properties of the documents. The query strings are joined with OR operators.
67+
"""
68+
if free_text_queries is not None:
69+
free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries)
70+
search = search.query(
71+
"query_string", query=f'properties.\\*:"{free_text_query_string}"'
72+
)
73+
74+
return search
75+
76+
77+
def apply_intersects_filter_shared(
78+
intersects: Geometry,
79+
) -> Dict[str, Dict]:
80+
"""Create a geo_shape filter for intersecting geometry.
81+
82+
Args:
83+
intersects (Geometry): The intersecting geometry, represented as a GeoJSON-like object.
84+
85+
Returns:
86+
Dict[str, Dict]: A dictionary containing the geo_shape filter configuration
87+
that can be used with Elasticsearch/OpenSearch Q objects.
88+
89+
Notes:
90+
This function creates a geo_shape filter configuration to find documents that intersect
91+
with the specified geometry. The returned dictionary should be wrapped in a Q object
92+
when applied to a search.
93+
"""
94+
return {
95+
"geo_shape": {
96+
"geometry": {
97+
"shape": {
98+
"type": intersects.type.lower(),
99+
"coordinates": intersects.coordinates,
100+
},
101+
"relation": "intersects",
102+
}
103+
}
104+
}
105+
106+
107+
def populate_sort_shared(sortby: List) -> Optional[Dict[str, Dict[str, str]]]:
108+
"""Create a sort configuration for Elasticsearch/OpenSearch queries.
109+
110+
Args:
111+
sortby (List): A list of sort specifications, each containing a field and direction.
112+
113+
Returns:
114+
Optional[Dict[str, Dict[str, str]]]: A dictionary mapping field names to sort direction
115+
configurations, or None if no sort was specified.
116+
117+
Notes:
118+
This function transforms a list of sort specifications into the format required by
119+
Elasticsearch/OpenSearch for sorting query results. The returned dictionary can be
120+
directly used in search requests.
121+
"""
122+
if sortby:
123+
return {s.field: {"order": s.direction} for s in sortby}
124+
else:
125+
return None

0 commit comments

Comments
 (0)