【问题标题】:Dynamodb scan() using FilterExpressionDynamodb scan() 使用 FilterExpression
【发布时间】:2017-11-26 00:44:27
【问题描述】:

第一次在 Stack 上发帖,对使用 Python 编程和使用 DynamoDB 还很陌生,但我只是想对我的表运行扫描,它会根据两个预定义的属性返回结果。

---这是我的 Python 代码 sn-p---

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

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

我的 DynamoDB 有 4 个字段。

  1. 身份证
  2. 日期
  3. 换档
  4. 安全

现在解决这个问题,在运行时,我得到了两个表条目,而我应该只得到第一个条目……根据我的扫描标准,一个“没有安全问题”的条目。

---这是我的 DynamoDB 返回结果---

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

退回的物品:2

我相信,通过应用带有逻辑“and”的 FilterExpression 指定扫描操作正在寻找满足两个条件的条目,因为我使用了“and”。

这可能是因为在两个条目中都找到了“shift”属性“3rd”吗?如何确保它返回基于满足两个条件的条目,而不仅仅是给我一种属性类型的结果?

我觉得这很简单,但我查看了可用的文档:http://boto3.readthedocs.io/en/latest/reference/services/dynamodb.html#DynamoDB.Table.scan,但仍然遇到问题。任何帮助将不胜感激!

附:我尽量让帖子简单易懂(不包括我的所有程序代码),但是如果需要更多信息,我可以提供!

【问题讨论】:

    标签: python python-2.7 amazon-dynamodb


    【解决方案1】:

    这是因为您在表达式中使用了 Python 的 and 关键字,而不是 & 运算符。

    如果ab都被认为是Truea and b返回后者,b

    >>> 2 and 3
    3
    

    如果其中任何一个是False,或者两者都是,则返回第一个False对象:

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

    一般规则是,and 返回允许它决定整个表达式的真实性的第一个对象。

    Python 对象在布尔上下文中始终被视为True。所以,你的表情:

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

    将评估为最后一个 True 对象,即:

    Attr("Shift").eq(shift)
    

    这解释了为什么您只在轮班时进行过滤。

    您需要使用& 运算符。它通常表示 Python 中整数之间的“按位与”,它为 Attr 对象重新定义为您想要的:“两个条件”。

    所以你必须使用“按位与”:

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

    根据the documentation

    您还可以使用逻辑将条件链接在一起 运算符:&(和),| (或)和~(非)。

    【讨论】:

    • 感谢您的解释!我现在明白了!
    • 非常感谢。你是个传奇。
    【解决方案2】:

    Dynamodb scan() 使用 FilterExpression

    对于多个过滤器,您可以使用这种方法:

    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()]))
    

    【讨论】:

    • 如果你想过滤“2017-06-21”之前的任何日期,代码会是什么样子?
    • 不幸的是,在这种特殊情况下,这个函数只支持相等。一种快速的方法是:query = [(Key(key).eq(value)) for key, value in filter.items()][0]query = query & Attr("Date").lt('2017-06-21')result = table.scan(FilterExpression=query)
    【解决方案3】:

    扩展 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)
    

    【讨论】:

    • 变量不匹配,expressions.items()应该是filters.items()
    • @AshtonHonnecke 谢谢!固定。
    【解决方案4】:

    使用上述每个答案的部分,这是我能够使其工作的一种紧凑方式:

    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()])))
    
    

    允许过滤filters 中的多个条件作为dict。例如:

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

    【讨论】:

      猜你喜欢
      • 2018-04-02
      • 2015-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多