2023-03-17 12:03:46 -07:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
#
|
|
|
|
|
# The OpenSearch Contributors require contributions made to
|
|
|
|
|
# this file be licensed under the Apache-2.0 license or a
|
|
|
|
|
# compatible open source license.
|
|
|
|
|
#
|
|
|
|
|
# Modifications Copyright OpenSearch Contributors. See
|
|
|
|
|
# GitHub history for details.
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
from typing import Any
|
|
|
|
|
|
2023-03-17 12:03:46 -07:00
|
|
|
from opensearchpy.connection.async_connections import get_connection
|
|
|
|
|
from opensearchpy.helpers.query import Bool, Q
|
|
|
|
|
from opensearchpy.helpers.response import UpdateByQueryResponse
|
|
|
|
|
from opensearchpy.helpers.search import ProxyDescriptor, QueryProxy, Request
|
|
|
|
|
from opensearchpy.helpers.utils import recursive_to_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AsyncUpdateByQuery(Request):
|
|
|
|
|
query = ProxyDescriptor("query")
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Update by query request to opensearch.
|
|
|
|
|
|
|
|
|
|
:arg using: `AsyncOpenSearch` instance to use
|
|
|
|
|
:arg index: limit the search to index
|
|
|
|
|
:arg doc_type: only query this type.
|
|
|
|
|
|
|
|
|
|
All the parameters supplied (or omitted) at creation type can be later
|
|
|
|
|
overridden by methods (`using`, `index` and `doc_type` respectively).
|
|
|
|
|
|
|
|
|
|
"""
|
2024-07-20 23:19:20 +03:00
|
|
|
super().__init__(**kwargs)
|
2023-03-17 12:03:46 -07:00
|
|
|
self._response_class = UpdateByQueryResponse
|
2023-11-06 13:08:19 -05:00
|
|
|
self._script: Any = {}
|
2023-03-17 12:03:46 -07:00
|
|
|
self._query_proxy = QueryProxy(self, "query")
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def filter(self, *args: Any, **kwargs: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
return self.query(Bool(filter=[Q(*args, **kwargs)]))
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def exclude(self, *args: Any, **kwargs: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
return self.query(Bool(filter=[~Q(*args, **kwargs)]))
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2023-11-06 13:08:19 -05:00
|
|
|
def from_dict(cls, d: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Construct a new `AsyncUpdateByQuery` instance from a raw dict containing the search
|
|
|
|
|
body. Useful when migrating from raw dictionaries.
|
|
|
|
|
|
|
|
|
|
Example::
|
|
|
|
|
|
|
|
|
|
ubq = AsyncUpdateByQuery.from_dict({
|
|
|
|
|
"query": {
|
|
|
|
|
"bool": {
|
|
|
|
|
"must": [...]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"script": {...}
|
|
|
|
|
})
|
|
|
|
|
ubq = ubq.filter('term', published=True)
|
|
|
|
|
"""
|
|
|
|
|
u = cls()
|
|
|
|
|
u.update_from_dict(d)
|
|
|
|
|
return u
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def _clone(self) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Return a clone of the current search request. Performs a shallow copy
|
|
|
|
|
of all the underlying objects. Used internally by most state modifying
|
|
|
|
|
APIs.
|
|
|
|
|
"""
|
2024-07-20 23:19:20 +03:00
|
|
|
ubq = super()._clone()
|
2023-03-17 12:03:46 -07:00
|
|
|
|
|
|
|
|
ubq._response_class = self._response_class
|
|
|
|
|
ubq._script = self._script.copy()
|
|
|
|
|
ubq.query._proxied = self.query._proxied
|
|
|
|
|
return ubq
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def response_class(self, cls: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Override the default wrapper used for the response.
|
|
|
|
|
"""
|
|
|
|
|
ubq = self._clone()
|
|
|
|
|
ubq._response_class = cls
|
|
|
|
|
return ubq
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def update_from_dict(self, d: Any) -> "AsyncUpdateByQuery":
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Apply options from a serialized body to the current instance. Modifies
|
|
|
|
|
the object in-place. Used mostly by ``from_dict``.
|
|
|
|
|
"""
|
|
|
|
|
d = d.copy()
|
|
|
|
|
if "query" in d:
|
|
|
|
|
self.query._proxied = Q(d.pop("query"))
|
|
|
|
|
if "script" in d:
|
|
|
|
|
self._script = d.pop("script")
|
|
|
|
|
self._extra.update(d)
|
|
|
|
|
return self
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def script(self, **kwargs: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Define update action to take:
|
|
|
|
|
|
|
|
|
|
Note: the API only accepts a single script, so
|
|
|
|
|
calling the script multiple times will overwrite.
|
|
|
|
|
|
|
|
|
|
Example::
|
|
|
|
|
|
|
|
|
|
ubq = AsyncSearch()
|
|
|
|
|
ubq = ubq.script(source="ctx._source.likes++"")
|
|
|
|
|
ubq = ubq.script(source="ctx._source.likes += params.f"",
|
|
|
|
|
lang="expression",
|
|
|
|
|
params={'f': 3})
|
|
|
|
|
"""
|
|
|
|
|
ubq = self._clone()
|
|
|
|
|
if ubq._script:
|
|
|
|
|
ubq._script = {}
|
|
|
|
|
ubq._script.update(kwargs)
|
|
|
|
|
return ubq
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
def to_dict(self, **kwargs: Any) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Serialize the search into the dictionary that will be sent over as the
|
|
|
|
|
request'ubq body.
|
|
|
|
|
|
|
|
|
|
All additional keyword arguments will be included into the dictionary.
|
|
|
|
|
"""
|
|
|
|
|
d = {}
|
|
|
|
|
if self.query:
|
|
|
|
|
d["query"] = self.query.to_dict()
|
|
|
|
|
|
|
|
|
|
if self._script:
|
|
|
|
|
d["script"] = self._script
|
|
|
|
|
|
|
|
|
|
d.update(recursive_to_dict(self._extra))
|
|
|
|
|
d.update(recursive_to_dict(kwargs))
|
|
|
|
|
return d
|
|
|
|
|
|
2023-11-06 13:08:19 -05:00
|
|
|
async def execute(self) -> Any:
|
2023-03-17 12:03:46 -07:00
|
|
|
"""
|
|
|
|
|
Execute the search and return an instance of ``Response`` wrapping all
|
|
|
|
|
the data.
|
|
|
|
|
"""
|
|
|
|
|
opensearch = await get_connection(self._using)
|
|
|
|
|
|
|
|
|
|
self._response = self._response_class(
|
|
|
|
|
self,
|
|
|
|
|
await opensearch.update_by_query(
|
|
|
|
|
index=self._index, body=self.to_dict(), **self._params
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
return self._response
|