From 2137066279a28828fa39fff0e1d099605c7eeaef Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 12 Apr 2024 13:44:27 +0100 Subject: [PATCH 1/5] Adding free text search extension. --- CHANGELOG.md | 1 + stac_fastapi/core/stac_fastapi/core/core.py | 12 ++ .../stac_fastapi/elasticsearch/app.py | 6 +- .../elasticsearch/database_logic.py | 15 +++ .../stac_fastapi/opensearch/database_logic.py | 15 +++ stac_fastapi/tests/resources/test_item.py | 112 ++++++++++++++++++ 6 files changed, 160 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab946d89..d453c416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - use index templates for Collection and Item indices [#208](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/discussions/208) - Added API `title`, `version`, and `description` parameters from environment variables `STAC_FASTAPI_TITLE`, `STAC_FASTAPI_VERSION` and `STAC_FASTAPI_DESCRIPTION`, respectively. [#207](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/207) - Added a `STAC_FASTAPI_ROOT_PATH` environment variable to define the root path. Useful when working with an API gateway or load balancer. [#221](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/221) +- Added Free-text Extension ### Changed diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index c27e6859..4724ad16 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -1,4 +1,5 @@ """Item crud client.""" + import logging import re from datetime import datetime as datetime_type @@ -557,6 +558,17 @@ async def post_search( status_code=400, detail=f"Error with cql2_json filter: {e}" ) + if self.extension_is_enabled("FreeTextExtension") and hasattr( + search_request, "q" + ): + query_str = getattr(search_request, "q", None) + try: + search = self.database.apply_free_text_filter(search, query_str) + except Exception as e: + raise HTTPException( + status_code=400, detail=f"Error with cql2_json filter: {e}" + ) + sort = None if search_request.sortby: sort = self.database.populate_sort(search_request.sortby) diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py index 6d189179..26091af9 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py @@ -26,7 +26,10 @@ TokenPaginationExtension, TransactionExtension, ) -from stac_fastapi.extensions.third_party import BulkTransactionExtension +from stac_fastapi.extensions.third_party import ( + BulkTransactionExtension, + FreeTextExtension, +) settings = ElasticsearchSettings() session = Session.create_from_settings(settings) @@ -58,6 +61,7 @@ TokenPaginationExtension(), ContextExtension(), filter_extension, + FreeTextExtension(), ] post_request_model = create_post_request_model(extensions) diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py index 49c44a60..e53b08e6 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py @@ -1,4 +1,5 @@ """Database logic.""" + import asyncio import logging import os @@ -506,6 +507,20 @@ def apply_stacql_filter(search: Search, op: str, field: str, value: float): return search + @staticmethod + def apply_free_text_filter(search: Search, query_str: Optional[str]): + """Database logic to perform query for search endpoint.""" + if query_str is not None: + # the colon is a reserved character + query_str = query_str.replace(":", "\:").replace("=", ":") + search = search.query( + "query_string", + query=query_str, + fields=["collection", "properties.*"], + lenient=True, + ) + return search + @staticmethod def apply_cql2_filter(search: Search, _filter: Optional[Dict[str, Any]]): """Database logic to perform query for search endpoint.""" diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py index 95129f27..12513d18 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py @@ -1,4 +1,5 @@ """Database logic.""" + import asyncio import logging import os @@ -423,6 +424,20 @@ def apply_collections_filter(search: Search, collection_ids: List[str]): """Database logic to search a list of STAC collection ids.""" return search.filter("terms", collection=collection_ids) + @staticmethod + def apply_free_text_filter(search: Search, query_str: Optional[str]): + """Database logic to perform query for search endpoint.""" + if query_str is not None: + # the colon is a reserved character + query_str = query_str.replace(":", "\:").replace("=", ":") + search = search.query( + "query_string", + query=query_str, + fields=["collection", "properties.*"], + lenient=True, + ) + return search + @staticmethod def apply_datetime_filter(search: Search, datetime_search): """Apply a filter to search based on datetime field. diff --git a/stac_fastapi/tests/resources/test_item.py b/stac_fastapi/tests/resources/test_item.py index 20517f42..7b9ba3ed 100644 --- a/stac_fastapi/tests/resources/test_item.py +++ b/stac_fastapi/tests/resources/test_item.py @@ -536,6 +536,118 @@ async def test_item_search_properties_field(app_client): assert len(resp_json["features"]) == 0 +@pytest.mark.asyncio +async def test_item_search_free_text_extension(app_client, txn_client, ctx): + """Test POST search indexed field with q parameter (free-text)""" + first_item = ctx.item + + second_item = dict(first_item) + second_item["id"] = "second-item" + second_item["properties"]["ft_field1"] = "hello" + + await create_item(txn_client, second_item) + + params = {"q": "hello"} + resp = await app_client.post("/search", json=params) + assert resp.status_code == 200 + resp_json = resp.json() + assert len(resp_json["features"]) == 1 + + +@pytest.mark.asyncio +async def test_item_search_free_text_extension_and_query(app_client, txn_client, ctx): + """Test POST search indexed field with q parameter with AND operator (free-text)""" + first_item = ctx.item + + second_item = dict(first_item) + second_item["id"] = "second-item" + second_item["properties"]["ft_field1"] = "hello" + second_item["properties"]["ft_field2"] = "world" + + await create_item(txn_client, second_item) + + third_item = dict(first_item) + third_item["id"] = "third-item" + third_item["properties"]["ft_field1"] = "world" + await create_item(txn_client, third_item) + + params = {"q": "hello AND world"} + resp = await app_client.post("/search", json=params) + assert resp.status_code == 200 + resp_json = resp.json() + assert len(resp_json["features"]) == 1 + + +@pytest.mark.asyncio +async def test_item_search_free_text_extension_or_query(app_client, txn_client, ctx): + """Test POST search indexed field with q parameter with OR operator (free-text)""" + first_item = ctx.item + + second_item = dict(first_item) + second_item["id"] = "second-item" + second_item["properties"]["ft_field1"] = "hello" + second_item["properties"]["ft_field2"] = "world" + + await create_item(txn_client, second_item) + + third_item = dict(first_item) + third_item["id"] = "third-item" + third_item["properties"]["ft_field1"] = "world" + await create_item(txn_client, third_item) + + params = {"q": "hello OR world"} + resp = await app_client.post("/search", json=params) + assert resp.status_code == 200 + resp_json = resp.json() + assert len(resp_json["features"]) == 2 + + +@pytest.mark.asyncio +async def test_item_search_free_text_extension_exact_query(app_client, txn_client, ctx): + """Test POST search indexed field with q parameter with an exact match (free-text)""" + first_item = ctx.item + + second_item = dict(first_item) + second_item["id"] = "second-item" + second_item["properties"]["ft_field1"] = "hello" + + await create_item(txn_client, second_item) + + params = {"q": '"hello"'} + resp = await app_client.post("/search", json=params) + assert resp.status_code == 200 + resp_json = resp.json() + assert len(resp_json["features"]) == 1 + + +@pytest.mark.asyncio +async def test_item_search_free_text_extension_parentheses_query( + app_client, txn_client, ctx +): + """Test POST search indexed field with q parameter with parentheses (free-text)""" + first_item = ctx.item + + second_item = dict(first_item) + second_item["id"] = "second-item" + second_item["properties"]["ft_field1"] = "hello" + second_item["properties"]["ft_field2"] = "foo" + + await create_item(txn_client, second_item) + + third_item = dict(first_item) + third_item["id"] = "third-item" + third_item["properties"]["ft_field1"] = "world" + third_item["properties"]["ft_field2"] = "foo" + + await create_item(txn_client, third_item) + + params = {"q": "(hello OR world) AND foo"} + resp = await app_client.post("/search", json=params) + assert resp.status_code == 200 + resp_json = resp.json() + assert len(resp_json["features"]) == 2 + + @pytest.mark.asyncio async def test_item_search_get_query_extension(app_client, ctx): """Test GET search with JSONB query (query extension)""" From 16c7657799b64df1fefe7eedd4b90c41c15a592e Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Thu, 29 Aug 2024 15:55:16 +0100 Subject: [PATCH 2/5] Switching to simple free text search. --- stac_fastapi/core/stac_fastapi/core/core.py | 13 ++-- .../elasticsearch/database_logic.py | 17 ++--- .../stac_fastapi/opensearch/database_logic.py | 17 ++--- stac_fastapi/tests/resources/test_item.py | 74 +------------------ 4 files changed, 25 insertions(+), 96 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index 4d198db8..e50e8a6e 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -600,15 +600,16 @@ async def post_search( status_code=400, detail=f"Error with cql2_json filter: {e}" ) - if self.extension_is_enabled("FreeTextExtension") and hasattr( - search_request, "q" - ): - query_str = getattr(search_request, "q", None) + if self.extension_is_enabled("FreeTextExtension"): + q_param = getattr(search_request, "q", None) + free_text_queries = ( + q_param.split(",") if isinstance(q_param, str) else q_param + ) try: - search = self.database.apply_free_text_filter(search, query_str) + search = self.database.apply_free_text_filter(search, free_text_queries) except Exception as e: raise HTTPException( - status_code=400, detail=f"Error with cql2_json filter: {e}" + status_code=400, detail=f"Error with free text query: {e}" ) sort = None diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py index 4e3c2df7..a4ee53bf 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py @@ -511,17 +511,16 @@ def apply_stacql_filter(search: Search, op: str, field: str, value: float): return search @staticmethod - def apply_free_text_filter(search: Search, query_str: Optional[str]): + def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]): """Database logic to perform query for search endpoint.""" - if query_str is not None: + if free_text_queries is not None: # the colon is a reserved character - query_str = query_str.replace(":", "\:").replace("=", ":") - search = search.query( - "query_string", - query=query_str, - fields=["collection", "properties.*"], - lenient=True, - ) + for free_text_query in free_text_queries: + search = search.query( + "multi_match", + query=free_text_query, + fields=["collection", "properties.*"], + ) return search @staticmethod diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py index 1a721d98..d8591e48 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py @@ -428,17 +428,16 @@ def apply_collections_filter(search: Search, collection_ids: List[str]): return search.filter("terms", collection=collection_ids) @staticmethod - def apply_free_text_filter(search: Search, query_str: Optional[str]): + def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]): """Database logic to perform query for search endpoint.""" - if query_str is not None: + if free_text_queries is not None: # the colon is a reserved character - query_str = query_str.replace(":", "\:").replace("=", ":") - search = search.query( - "query_string", - query=query_str, - fields=["collection", "properties.*"], - lenient=True, - ) + for free_text_query in free_text_queries: + search = search.query( + "multi_match", + query=free_text_query, + fields=["collection", "properties.*"], + ) return search @staticmethod diff --git a/stac_fastapi/tests/resources/test_item.py b/stac_fastapi/tests/resources/test_item.py index 37cc52b9..79c5f26c 100644 --- a/stac_fastapi/tests/resources/test_item.py +++ b/stac_fastapi/tests/resources/test_item.py @@ -551,33 +551,9 @@ async def test_item_search_free_text_extension(app_client, txn_client, ctx): assert len(resp_json["features"]) == 1 -@pytest.mark.asyncio -async def test_item_search_free_text_extension_and_query(app_client, txn_client, ctx): - """Test POST search indexed field with q parameter with AND operator (free-text)""" - first_item = ctx.item - - second_item = dict(first_item) - second_item["id"] = "second-item" - second_item["properties"]["ft_field1"] = "hello" - second_item["properties"]["ft_field2"] = "world" - - await create_item(txn_client, second_item) - - third_item = dict(first_item) - third_item["id"] = "third-item" - third_item["properties"]["ft_field1"] = "world" - await create_item(txn_client, third_item) - - params = {"q": "hello AND world"} - resp = await app_client.post("/search", json=params) - assert resp.status_code == 200 - resp_json = resp.json() - assert len(resp_json["features"]) == 1 - - @pytest.mark.asyncio async def test_item_search_free_text_extension_or_query(app_client, txn_client, ctx): - """Test POST search indexed field with q parameter with OR operator (free-text)""" + """Test POST search indexed field with q parameter with multiple terms (free-text)""" first_item = ctx.item second_item = dict(first_item) @@ -592,59 +568,13 @@ async def test_item_search_free_text_extension_or_query(app_client, txn_client, third_item["properties"]["ft_field1"] = "world" await create_item(txn_client, third_item) - params = {"q": "hello OR world"} - resp = await app_client.post("/search", json=params) - assert resp.status_code == 200 - resp_json = resp.json() - assert len(resp_json["features"]) == 2 - - -@pytest.mark.asyncio -async def test_item_search_free_text_extension_exact_query(app_client, txn_client, ctx): - """Test POST search indexed field with q parameter with an exact match (free-text)""" - first_item = ctx.item - - second_item = dict(first_item) - second_item["id"] = "second-item" - second_item["properties"]["ft_field1"] = "hello" - - await create_item(txn_client, second_item) - - params = {"q": '"hello"'} + params = {"q": ["hello", "world"]} resp = await app_client.post("/search", json=params) assert resp.status_code == 200 resp_json = resp.json() assert len(resp_json["features"]) == 1 -@pytest.mark.asyncio -async def test_item_search_free_text_extension_parentheses_query( - app_client, txn_client, ctx -): - """Test POST search indexed field with q parameter with parentheses (free-text)""" - first_item = ctx.item - - second_item = dict(first_item) - second_item["id"] = "second-item" - second_item["properties"]["ft_field1"] = "hello" - second_item["properties"]["ft_field2"] = "foo" - - await create_item(txn_client, second_item) - - third_item = dict(first_item) - third_item["id"] = "third-item" - third_item["properties"]["ft_field1"] = "world" - third_item["properties"]["ft_field2"] = "foo" - - await create_item(txn_client, third_item) - - params = {"q": "(hello OR world) AND foo"} - resp = await app_client.post("/search", json=params) - assert resp.status_code == 200 - resp_json = resp.json() - assert len(resp_json["features"]) == 2 - - @pytest.mark.asyncio async def test_item_search_get_query_extension(app_client, ctx): """Test GET search with JSONB query (query extension)""" From 42df567932675caaccce21ad3ffb937b1844e11e Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 30 Aug 2024 08:28:31 +0100 Subject: [PATCH 3/5] Updating free-text import. --- stac_fastapi/core/stac_fastapi/core/core.py | 2 +- .../elasticsearch/stac_fastapi/elasticsearch/app.py | 6 ++---- stac_fastapi/tests/resources/test_item.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index e50e8a6e..f1b76c8f 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -600,7 +600,7 @@ async def post_search( status_code=400, detail=f"Error with cql2_json filter: {e}" ) - if self.extension_is_enabled("FreeTextExtension"): + if hasattr(search_request, "q"): q_param = getattr(search_request, "q", None) free_text_queries = ( q_param.split(",") if isinstance(q_param, str) else q_param diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py index 4b53a27a..6b26c2ac 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/app.py @@ -28,14 +28,12 @@ from stac_fastapi.extensions.core import ( AggregationExtension, FilterExtension, + FreeTextExtension, SortExtension, TokenPaginationExtension, TransactionExtension, ) -from stac_fastapi.extensions.third_party import ( - BulkTransactionExtension, - FreeTextExtension, -) +from stac_fastapi.extensions.third_party import BulkTransactionExtension settings = ElasticsearchSettings() session = Session.create_from_settings(settings) diff --git a/stac_fastapi/tests/resources/test_item.py b/stac_fastapi/tests/resources/test_item.py index 79c5f26c..d2dc964a 100644 --- a/stac_fastapi/tests/resources/test_item.py +++ b/stac_fastapi/tests/resources/test_item.py @@ -572,7 +572,7 @@ async def test_item_search_free_text_extension_or_query(app_client, txn_client, resp = await app_client.post("/search", json=params) assert resp.status_code == 200 resp_json = resp.json() - assert len(resp_json["features"]) == 1 + assert len(resp_json["features"]) == 2 @pytest.mark.asyncio From 7476f207445685e1bedf6f3da84501c7f212db35 Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 30 Aug 2024 10:35:43 +0100 Subject: [PATCH 4/5] Switching to query_string. --- stac_fastapi/core/stac_fastapi/core/core.py | 8 ++++---- .../stac_fastapi/elasticsearch/database_logic.py | 12 +++++------- .../opensearch/stac_fastapi/opensearch/app.py | 2 ++ .../stac_fastapi/opensearch/database_logic.py | 12 +++++------- stac_fastapi/tests/conftest.py | 4 ++++ stac_fastapi/tests/resources/test_item.py | 2 +- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/stac_fastapi/core/stac_fastapi/core/core.py b/stac_fastapi/core/stac_fastapi/core/core.py index f1b76c8f..56afcbc8 100644 --- a/stac_fastapi/core/stac_fastapi/core/core.py +++ b/stac_fastapi/core/stac_fastapi/core/core.py @@ -457,6 +457,7 @@ async def get_search( token: Optional[str] = None, fields: Optional[List[str]] = None, sortby: Optional[str] = None, + q: Optional[List[str]] = None, intersects: Optional[str] = None, filter: Optional[str] = None, filter_lang: Optional[str] = None, @@ -474,6 +475,7 @@ async def get_search( token (Optional[str]): Access token to use when searching the catalog. fields (Optional[List[str]]): Fields to include or exclude from the results. sortby (Optional[str]): Sorting options for the results. + q (Optional[List[str]]): Free text query to filter the results. intersects (Optional[str]): GeoJSON geometry to search in. kwargs: Additional parameters to be passed to the API. @@ -490,6 +492,7 @@ async def get_search( "limit": limit, "token": token, "query": orjson.loads(query) if query else query, + "q": q, } if datetime: @@ -601,10 +604,7 @@ async def post_search( ) if hasattr(search_request, "q"): - q_param = getattr(search_request, "q", None) - free_text_queries = ( - q_param.split(",") if isinstance(q_param, str) else q_param - ) + free_text_queries = getattr(search_request, "q", None) try: search = self.database.apply_free_text_filter(search, free_text_queries) except Exception as e: diff --git a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py index a4ee53bf..7aa887b5 100644 --- a/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py +++ b/stac_fastapi/elasticsearch/stac_fastapi/elasticsearch/database_logic.py @@ -514,13 +514,11 @@ def apply_stacql_filter(search: Search, op: str, field: str, value: float): def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]): """Database logic to perform query for search endpoint.""" if free_text_queries is not None: - # the colon is a reserved character - for free_text_query in free_text_queries: - search = search.query( - "multi_match", - query=free_text_query, - fields=["collection", "properties.*"], - ) + free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries) + search = search.query( + "query_string", query=f'properties.\\*:"{free_text_query_string}"' + ) + return search @staticmethod diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/app.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/app.py index 186a85ab..2a764518 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/app.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/app.py @@ -22,6 +22,7 @@ from stac_fastapi.extensions.core import ( AggregationExtension, FilterExtension, + FreeTextExtension, SortExtension, TokenPaginationExtension, TransactionExtension, @@ -71,6 +72,7 @@ SortExtension(), TokenPaginationExtension(), filter_extension, + FreeTextExtension(), ] extensions = [aggregation_extension] + search_extensions diff --git a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py index d8591e48..014ea57b 100644 --- a/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py +++ b/stac_fastapi/opensearch/stac_fastapi/opensearch/database_logic.py @@ -431,13 +431,11 @@ def apply_collections_filter(search: Search, collection_ids: List[str]): def apply_free_text_filter(search: Search, free_text_queries: Optional[List[str]]): """Database logic to perform query for search endpoint.""" if free_text_queries is not None: - # the colon is a reserved character - for free_text_query in free_text_queries: - search = search.query( - "multi_match", - query=free_text_query, - fields=["collection", "properties.*"], - ) + free_text_query_string = '" OR properties.\\*:"'.join(free_text_queries) + search = search.query( + "query_string", query=f'properties.\\*:"{free_text_query_string}"' + ) + return search @staticmethod diff --git a/stac_fastapi/tests/conftest.py b/stac_fastapi/tests/conftest.py index dde9375e..ca2d8436 100644 --- a/stac_fastapi/tests/conftest.py +++ b/stac_fastapi/tests/conftest.py @@ -49,6 +49,7 @@ AggregationExtension, FieldsExtension, FilterExtension, + FreeTextExtension, SortExtension, TokenPaginationExtension, TransactionExtension, @@ -215,6 +216,7 @@ async def app(): QueryExtension(), TokenPaginationExtension(), FilterExtension(), + FreeTextExtension(), ] extensions = [aggregation_extension] + search_extensions @@ -301,6 +303,7 @@ async def app_basic_auth(): QueryExtension(), TokenPaginationExtension(), FilterExtension(), + FreeTextExtension(), ] extensions = [aggregation_extension] + search_extensions @@ -380,6 +383,7 @@ async def route_dependencies_app(): QueryExtension(), TokenPaginationExtension(), FilterExtension(), + FreeTextExtension(), ] post_request_model = create_post_request_model(extensions) diff --git a/stac_fastapi/tests/resources/test_item.py b/stac_fastapi/tests/resources/test_item.py index d2dc964a..904adbbf 100644 --- a/stac_fastapi/tests/resources/test_item.py +++ b/stac_fastapi/tests/resources/test_item.py @@ -544,7 +544,7 @@ async def test_item_search_free_text_extension(app_client, txn_client, ctx): await create_item(txn_client, second_item) - params = {"q": "hello"} + params = {"q": ["hello"]} resp = await app_client.post("/search", json=params) assert resp.status_code == 200 resp_json = resp.json() From 08890b0ecaf2c425771a913e4eb3bb062f42073a Mon Sep 17 00:00:00 2001 From: rhysrevans3 Date: Fri, 30 Aug 2024 11:42:35 +0100 Subject: [PATCH 5/5] Update CHANGELOG message. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e3ed1df..1e4cbadf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - - Added free text search + - Added support for FreeTextExtension. [#227](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/227) ### Changed - Support escaped backslashes in CQL2 `LIKE` queries, and reject invalid (or incomplete) escape sequences. [#286](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pull/286)