44import re
55import time
66from botocore .exceptions import ClientError
7+ from boto3 .dynamodb .conditions import Key
78
89if 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 ,
0 commit comments