Skip to content

Commit 8234f8b

Browse files
Merge branch 'mr/kanya/update' into 'master'
Add query_itemsv2 method with multiple value support See merge request it/e3-aws!94
2 parents 3db3398 + 058fc08 commit 8234f8b

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

src/e3/aws/dynamodb/__init__.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import time
66
from botocore.exceptions import ClientError
7+
from boto3.dynamodb.conditions import Key
78

89
if TYPE_CHECKING:
910
from typing import Any, Literal, Optional
@@ -244,13 +245,22 @@ def query_items(
244245
"""Query items from a table.
245246
246247
:param table_name: name of the table
247-
:param query: a dict containing the key-values to query for
248-
e.g. {"name": ["John", "Robert"]}
248+
:param query: a dict containing the key-value to query for
249+
e.g. {"name": ["John"]}
249250
:param index_name: the name of an index to query, defaults to None
250251
:param sort_key: sort key-value of the table or the index to use as
251252
filter expression on the query
252253
:return: result of the query
253254
"""
255+
if len(query) > 1:
256+
logger.warning(
257+
"DynamoDB.query_items only supports one key in the query dict"
258+
)
259+
if len(list(query.values())[0]) > 1:
260+
logger.warning(
261+
"DynamoDB.query_items only supports one value per key in the "
262+
"query dict, use query_itemsv2 instead"
263+
)
254264
table = self.client.Table(table_name)
255265
logger.info(f"Quering table {table_name} with {query}")
256266
attr_name = list(query.keys())[0]
@@ -279,6 +289,8 @@ def query_items(
279289
if index_name:
280290
attr["IndexName"] = index_name
281291

292+
logger.debug(f"query attributes: {attr}")
293+
282294
paging = True
283295
items = []
284296
while paging:
@@ -292,6 +304,54 @@ def query_items(
292304

293305
return items
294306

307+
def query_itemsv2(
308+
self,
309+
table_name: str,
310+
query_key: str,
311+
query_values: list[str],
312+
sort_key: tuple[str, str] | None = None,
313+
index_name: str | None = None,
314+
) -> list[dict[str, Any]]:
315+
"""Query items from a table with support for multiple values.
316+
317+
:param table_name: name of the table to query
318+
:param query_key: the key to query
319+
:param query_values: list of values to query for the given key
320+
:param sort_key: optional sort key-value tuple (key_name, value) to
321+
filter results
322+
:param index_name: optional name of a secondary index to query
323+
:return: list of items matching the query criteria
324+
"""
325+
table = self.client.Table(table_name)
326+
logger.info(f"Querying table {table_name} with ({query_key}: {query_values})")
327+
items: list[dict[str, Any]] = []
328+
329+
for attr_value in query_values:
330+
key_condition = Key(query_key).eq(attr_value)
331+
if sort_key:
332+
key_condition = key_condition & Key(sort_key[0]).eq(sort_key[1])
333+
334+
attr = {
335+
"KeyConditionExpression": key_condition,
336+
}
337+
338+
if index_name:
339+
attr["IndexName"] = index_name
340+
341+
logger.debug(f"query attributes: {attr}")
342+
343+
paging = True
344+
while paging:
345+
query_result = table.query(**attr)
346+
items += query_result["Items"]
347+
if query_result.get("LastEvaluatedKey"):
348+
# we have not scanned all table
349+
attr.update({"ExclusiveStartKey": query_result["LastEvaluatedKey"]})
350+
else:
351+
paging = False
352+
353+
return items
354+
295355
def scan(
296356
self,
297357
table_name: str,

tests/tests_e3_aws/dynamodb/main_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,29 @@ def test_query_items(client: DynamoDB) -> None:
155155
assert items[0] == CUSTOMERS[0]
156156

157157

158+
def test_query_items_error(client: DynamoDB, caplog: LogCaptureFixture) -> None:
159+
"""Test querying items."""
160+
items = client.query_items(table_name=TABLE_NAME, query={"name": ["John", "Doe"]})
161+
assert (
162+
caplog.text.count(
163+
"DynamoDB.query_items only supports one value per key in the query dict, "
164+
"use query_itemsv2 instead"
165+
)
166+
== 1
167+
)
168+
assert len(items) == 1
169+
assert items[0] == CUSTOMERS[0]
170+
171+
172+
def test_query_itemsv2(client: DynamoDB) -> None:
173+
"""Test querying items."""
174+
items = client.query_itemsv2(
175+
table_name=TABLE_NAME, query_key="name", query_values=["John", "Doe"]
176+
)
177+
assert len(items) == 2
178+
assert items == CUSTOMERS
179+
180+
158181
def test_scan(client: DynamoDB) -> None:
159182
"""Test querying items."""
160183
items = client.scan(table_name=TABLE_NAME, query={"name": ["John"]})

0 commit comments

Comments
 (0)