Skip to content
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

[Backport 8.x] Added the EmptySearch class #1797

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/search_dsl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -666,3 +666,10 @@ If you need to execute multiple searches at the same time you can use the
print("Results for query %r." % response.search.query)
for hit in response:
print(hit.title)


``EmptySearch``
---------------

The ``EmptySearch`` class can be used as a fully compatible version of ``Search``
that will return no results, regardless of any queries configured.
11 changes: 10 additions & 1 deletion elasticsearch_dsl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@
from .index import AsyncIndex, AsyncIndexTemplate, Index, IndexTemplate
from .mapping import AsyncMapping, Mapping
from .query import Q
from .search import AsyncMultiSearch, AsyncSearch, MultiSearch, Search
from .search import (
AsyncEmptySearch,
AsyncMultiSearch,
AsyncSearch,
EmptySearch,
MultiSearch,
Search,
)
from .update_by_query import AsyncUpdateByQuery, UpdateByQuery
from .utils import AttrDict, AttrList, DslBase
from .wrappers import Range
Expand All @@ -92,6 +99,7 @@
__all__ = [
"A",
"AsyncDocument",
"AsyncEmptySearch",
"AsyncFacetedSearch",
"AsyncIndex",
"AsyncIndexTemplate",
Expand All @@ -115,6 +123,7 @@
"DoubleRange",
"DslBase",
"ElasticsearchDslException",
"EmptySearch",
"Facet",
"FacetedResponse",
"FacetedSearch",
Expand Down
15 changes: 15 additions & 0 deletions elasticsearch_dsl/_async/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,18 @@ async def execute(self, ignore_cache=False, raise_on_error=True):
self._response = out

return self._response


class AsyncEmptySearch(AsyncSearch):
async def count(self):
return 0

async def execute(self, ignore_cache=False):
return self._response_class(self, {"hits": {"total": 0, "hits": []}})

async def scan(self):
return
yield # a bit strange, but this forces an empty generator function

async def delete(self):
return AttrDict({})
15 changes: 15 additions & 0 deletions elasticsearch_dsl/_sync/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,18 @@ def execute(self, ignore_cache=False, raise_on_error=True):
self._response = out

return self._response


class EmptySearch(Search):
def count(self):
return 0

def execute(self, ignore_cache=False):
return self._response_class(self, {"hits": {"total": 0, "hits": []}})

def scan(self):
return
yield # a bit strange, but this forces an empty generator function

def delete(self):
return AttrDict({})
12 changes: 10 additions & 2 deletions elasticsearch_dsl/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
# specific language governing permissions and limitations
# under the License.

from elasticsearch_dsl._async.search import AsyncMultiSearch, AsyncSearch # noqa: F401
from elasticsearch_dsl._sync.search import MultiSearch, Search # noqa: F401
from elasticsearch_dsl._async.search import ( # noqa: F401
AsyncEmptySearch,
AsyncMultiSearch,
AsyncSearch,
)
from elasticsearch_dsl._sync.search import ( # noqa: F401
EmptySearch,
MultiSearch,
Search,
)
from elasticsearch_dsl.search_base import Q # noqa: F401
13 changes: 12 additions & 1 deletion tests/_async/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from pytest import raises

from elasticsearch_dsl import AsyncSearch, Document, Q, query
from elasticsearch_dsl import A, AsyncEmptySearch, AsyncSearch, Document, Q, query
from elasticsearch_dsl.exceptions import IllegalOperation


Expand Down Expand Up @@ -681,3 +681,14 @@ def test_rescore_query_to_dict():
},
},
}


async def test_empty_search():
s = AsyncEmptySearch(index="index-name")
s = s.query("match", lang="java")
s.aggs.bucket("versions", A("terms", field="version"))

assert await s.count() == 0
assert [hit async for hit in s] == []
assert [hit async for hit in s.scan()] == []
await s.delete() # should not error
13 changes: 12 additions & 1 deletion tests/_sync/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from pytest import raises

from elasticsearch_dsl import Document, Q, Search, query
from elasticsearch_dsl import A, Document, EmptySearch, Q, Search, query
from elasticsearch_dsl.exceptions import IllegalOperation


Expand Down Expand Up @@ -679,3 +679,14 @@ def test_rescore_query_to_dict():
},
},
}


def test_empty_search():
s = EmptySearch(index="index-name")
s = s.query("match", lang="java")
s.aggs.bucket("versions", A("terms", field="version"))

assert s.count() == 0
assert [hit for hit in s] == []
assert [hit for hit in s.scan()] == []
s.delete() # should not error
1 change: 1 addition & 0 deletions utils/run-unasync.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def main(check=False):
"AsyncElasticsearch": "Elasticsearch",
"AsyncSearch": "Search",
"AsyncMultiSearch": "MultiSearch",
"AsyncEmptySearch": "EmptySearch",
"AsyncDocument": "Document",
"AsyncIndexMeta": "IndexMeta",
"AsyncIndexTemplate": "IndexTemplate",
Expand Down
Loading