简体   繁体   English

Dynamodb scan() 使用 FilterExpression

[英]Dynamodb scan() using FilterExpression

First post here on Stack and fairly new to programming with Python and using DynamoDB, but I'm simply trying to run a scan on my table that returns results based on two pre-defined attributes.第一次在 Stack 上发帖,对使用 Python 和使用 DynamoDB 编程还很陌生,但我只是尝试在我的表上运行扫描,返回基于两个预定义属性的结果。

---Here is my Python code snippet--- ---这是我的 Python 代码片段---

shift = "3rd"
date = "2017-06-21"

if shift != "":
    response = table.scan(
        FilterExpression=Attr("Date").eq(date) and Attr("Shift").eq(shift)
    )

My DynamoDB has 4 fields.我的 DynamoDB 有 4 个字段。

  1. ID身份证
  2. Date日期
  3. Shift移位
  4. Safety安全

DynamoDB 图片

Now for the issue, upon running I'm getting two table entries returned when I should only be getting the first entry... the one with "No safety issues" based on my scan criteria.现在对于这个问题,在运行时,当我应该只获取第一个条目时,我返回了两个表条目......根据我的扫描标准,“没有安全问题”。

---Here is my DynamoDB return results--- ---这是我的 DynamoDB 返回结果---

[
  {
    "Shift": "3rd",  
    "Safety": "No safety issues",  
    "Date": "2017-06-21",
    "ID": "2"
  }, 
  {
    "Shift": "3rd", 
    "Safety": "Cut Finger", 
    "Date": "2017-06-22", 
    "ID": "4"
  }
]

Items Returned: 2退回的物品:2

I believe that by applying the FilterExpression with the logical 'and' specified that the scan operation is looking for entries that meet BOTH criteria since I used 'and'.我相信通过应用带有逻辑“and”的 FilterExpression 指定扫描操作正在寻找满足两个条件的条目,因为我使用了“and”。

Could this be because the 'shift' attribute "3rd" is found in both entries?这可能是因为在两个条目中都找到了 'shift' 属性“3rd”? How do I ensure it returns entries based on BOTH criteria being meet and not just giving me results from one attribute type?我如何确保它返回基于满足 BOTH 标准的条目,而不仅仅是给我来自一种属性类型的结果?

I have a feeling this is simple but I've looked at the available documentation at: http://boto3.readthedocs.io/en/latest/reference/services/dynamodb.html#DynamoDB.Table.scan and am still having trouble.我觉得这很简单,但我查看了以下可用文档: http : //boto3.readthedocs.io/en/latest/reference/services/dynamodb.html#DynamoDB.Table.scan并且仍然遇到问题. Any help would be greatly appreciated!任何帮助将不胜感激!

PS I tried to keep the post simple and easy to understand (not including all my program code) however, if additional information is needed I can provide it! PS 我尽量让帖子简单易懂(不包括我所有的程序代码)但是,如果需要其他信息,我可以提供!

This is because you used Python's and keyword in your expression, instead of the & operator.这是因为您在表达式中使用了 Python 的and关键字,而不是&运算符。

If a and b are both considered True , a and b returns the latter, b :如果ab都被认为是Truea and b返回后者, b

>>> 2 and 3
3

If any of them is False , or if both of them are, the first False object is returned:如果它们中的任何一个是False ,或者如果它们两个都是,则返回第一个False对象:

>>> 0 and 3
0
>>> 0 and ''
0
>>> 

The general rule is, and returns the first object that allows it to decide the truthiness of the whole expression.一般规则是, and返回第一个允许它决定整个表达式的真实性的对象。

Python objects are always considered True in boolean context. Python 对象在布尔上下文中始终被视为True So, your expression:所以,你的表达:

Attr("Date").eq(date) and Attr("Shift").eq(shift)

will evaluate as the last True object, that is:将评估为最后一个True对象,即:

Attr("Shift").eq(shift)

which explains why you only filtered on the shift.这解释了为什么您只对班次进行过滤。

You need to use the & operator.您需要使用&运算符。 It usually means "bitwise and" between integers in Python, it is redefined for Attr objects to mean what you want: "both conditions".它通常表示 Python 中整数之间的“按位和”,它被重新定义为 Attr 对象以表示您想要的内容:“两个条件”。

So you must use the "bitwise and":所以你必须使用“按位与”:

FilterExpression=Attr("Date").eq(date) & Attr("Shift").eq(shift)

According to the documentation ,根据文档

You are also able to chain conditions together using the logical operators: & (and), |您还可以使用逻辑运算符将条件链接在一起:&(和)、| (or), and ~ (not). (或)和~(不是)。

Dynamodb scan() using FilterExpression Dynamodb scan() 使用 FilterExpression

For multiple filters, you can use this approach:对于多个过滤器,您可以使用这种方法:

import boto3
from boto3.dynamodb.conditions import Key, And

filters = dict()
filters['Date'] = "2017-06-21"
filters['Shift'] = "3rd"

response = table.scan(FilterExpression=And(*[(Key(key).eq(value)) for key, value in filters.items()]))

Using parts from each of the above answers, here's a compact way I was able to get this working:使用上述每个答案中的部分,这是我能够使其工作的紧凑方式:

from functools import reduce
from boto3.dynamodb.conditions import Key, And

response = table.scan(FilterExpression=reduce(And, ([Key(k).eq(v) for k, v in filters.items()])))

Allows filtering upon multiple conditions in filters as a dict .允许根据filters中的多个条件作为dict进行filters For example:例如:

{
    'Status': 'Approved', 
    'SubmittedBy': 'JackCasey'
}

Expanding on Maxime Paille's answer, this covers the case when only one filter is present vs many.扩展 Maxime Paille 的回答,这涵盖了只有一个过滤器而不是多个过滤器的情况。

from boto3.dynamodb.conditions import And, Attr
from functools import reduce
from operator import and_

filters = dict()
filters['Date'] = "2017-06-21"
filters['Shift'] = "3rd"

table.scan("my-table", **build_query_params(filters))

def build_query_params(filters):
    query_params = {}
    if len(filters) > 0:
        query_params["FilterExpression"] = add_expressions(filters)

    return query_params

def add_expressions(self, filters: dict):
    if filters:
        conditions = []
        for key, value in filters.items():
            if isinstance(value, str):
                conditions.append(Attr(key).eq(value))
            if isinstance(value, list):
                conditions.append(Attr(key).is_in([v for v in value]))
        return reduce(and_, conditions)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM