【问题标题】:How to build a query dynamically, based on conditions?如何根据条件动态构建查询?
【发布时间】:2017-01-09 18:37:44
【问题描述】:

我想根据用户请求在可能的条件范围 (0-4) 上查询数据存储。 NDB 中的查询构建如下:

query = Account.query(Account.userid >= 40, Account.userid < 50)

我有什么办法可以做这样的事情:

myfilter = []
myfilter.push('Account.userid >= 40')
myfilter.push('Account.userid < 50')
myfilter.push('Account.name == "John"')
query = Account.query(*myfilter)

可能有 0 到 4 个过滤器参数,具体取决于条件。我的假设(这可能是错误的)是,如果不需要它,省略过滤器比使用包罗万象(例如 Account.userid == *)更优化。

我知道您可以链接过滤器,但由于查询对象是不可变的,不确定这对我有帮助。

【问题讨论】:

    标签: python google-app-engine app-engine-ndb


    【解决方案1】:

    有一种更优雅的方法可以做到这一点。顺便说一句,更具活力:

    def build_query_by(ndb_class, filters, sorts):
        """
        ndb_class: the ndb model class to query
        filters: a list of tuples of properties, operations and values
        sorts: a list of tuples of properties and order symbol
        """
        q = ndb_class.query()
        for prop, operation, value in filters:
            if operation == '==':
                q = q.filter(getattr(ndb_class, prop) == value)
            elif operation == '>=':
                q = q.filter(getattr(ndb_class, prop) >= value)
            elif operation == '<=':
                q = q.filter(getattr(ndb_class, prop) <= value)
            # more operations...
    
        for prop, symbol in sorts:
            if symbol == '-':
                q = q.order(-getattr(ndb_class, prop))
            else:
                q = q.order(getattr(ndb_class, prop))
        return q
    

    【讨论】:

    • 请注意:不能对超过 1 个属性使用不等式过滤器,并且不能对一个属性使用不等式过滤器并在另一个属性上进行排序。请参阅此答案中引用的其他限制:stackoverflow.com/a/41583096/4495081 包含对它们的保护后,代码可能看起来不那么整洁。
    【解决方案2】:

    是的,这是可能的。来自Filtering by Property Values

    而不是在单个表达式中指定整个查询过滤器, 您可能会发现分步构建它更方便:例如:

    query1 = Account.query()  # Retrieve all Account entitites
    query2 = query1.filter(Account.userid >= 40)  # Filter on userid >= 40
    query3 = query2.filter(Account.userid < 50)  # Filter on userid < 50 too
    

    query3 等价于前面的 query 变量 例子。请注意,查询对象是不可变的,因此构造 query2不影响query1query3的构建 不影响query1query2

    您可以使用这种增量查询构建技术根据需要有条件地添加过滤器。例如(这假设可选条件之间的整体与):

    query = Account.query()  # Retrieve all Account entitites
    loggin.error(query)
    
    if filter_by_userid:
        query = query.filter(Account.userid >= 40, Account.userid < 50)
        loggin.error(query)
    
    if filter_by_username:
        query = query.filter(Account.name == "John")
        loggin.error(query)
    
    loggin.error('Final: %s' % query)
    

    上面的sn-p专门利用了查询对象的不变性,对query变量的每个赋值实际上存储了应用相应过滤器获得的new查询对象。由相关的日志消息确认。

    【讨论】:

    • 我最初认为因为查询对象是不可变的,所以这是行不通的。但我找到了一种让它发挥作用的方法。它不是超级优雅,因为即使您不使用它们,您也必须为每个过滤器定义一个查询。 (例如,即使您不关心该特定过滤器,也必须定义 query2)。无论如何,这对我的目的来说已经足够了。
    • “你不关心那个特定的过滤器”是什么意思?
    • 如果参数是动态的并且您正在使用不等式过滤器,您可能会缺少索引对@DanCornilescu?除非您可以枚举所有您认为可能的查询并为每个查询提供一个索引,否则我认为这种任意查询只是不适合数据存储的东西
    • @jcjones1515 当然,所有可能的最终查询(即条件和相应过滤器的所有可能组合)的索引都需要准备好/服务。如果没有索引,查询将无法工作,无论它们是如何编写的(对我来说,问题主要是关于如何更好地编写查询)。
    • @DanCornilescu 谢谢。这与我最终做的类似。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 2021-01-12
    • 2015-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多