【问题标题】:Google App Engine Datastore - query with StructuredProperty in projection and filterGoogle App Engine Datastore - 在投影和过滤器中使用 StructuredProperty 进行查询
【发布时间】:2018-03-06 05:59:00
【问题描述】:

我有几个 ndb 模型如下所示:

class Product(ndb.Model):
    manufacturer = ndb.StringProperty()
    category = ndb.StringProperty()
    price = ndb.FloatProperty()
class Customer(ndb.Model):
    customerId = ndb.StringProperty()
    name = ndb.StringProperty()    
    products = ndb.StructuredProperty(Product, repeated=True)

我想根据他/她拥有的产品的“制造商”和“类别”进行查询。所以这个查询按预期工作。

query = Customer.query(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"],
                                                    category=data_json["product"]["category"]))

results= query.fetch()

但是,我无法让“投影”与此查询一起使用。以下查询根本没有返回任何内容。

query = Customer.query(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"],
                                                    category=data_json["product"]["category"]))

results= query.fetch(projection=[Customer.products.price])

但是如果我使用没有滤镜的投影,投影部分可以正常工作。以下查询将返回所有实体,但仅返回“价格”属性

results= Customer.query().fetch(projection=[Customer.products.price])

有什么想法吗?谢谢。

顺便说一句,我的查询是根据这篇文章开发的。 https://cloud.google.com/appengine/docs/standard/python/ndb/queries#filtering_structured_properties

【问题讨论】:

  • 您有此类查询的复合索引吗?它可能并不总是需要,因为它是一个仅相等的过滤器,请参阅stackoverflow.com/questions/48896357/…。但从我在 cmets 中提到的文章中读到的内容,我怀疑有时它可能没有它就行不通。要强制投诉索引并可能获得非常接近的索引定义,请临时添加结果排序。然后修改定义以删除其中的direction:(以及结果排序)。
  • 嗨,丹,感谢您的反馈。通过检查 index.yaml,我确实有复合索引。也许我错过了一些东西,但我很好奇为什么索引在这种情况下很重要?没有投影的查询能够使用相等过滤器返回预期结果,但不能使用项目。谢谢。
  • 没关系 - 如果你有索引我的理论不适用。

标签: google-app-engine google-cloud-datastore app-engine-ndb


【解决方案1】:

ndb 库中组合 ANDOR 操作的正确方法记录在 NDB Client Library's documentation 中。

通过下面的查询,您正在过滤器中执行 AND 操作,因此您应该使用我在下面建议的操作,而不是这个,使用 ndb.AND()

# Your query
query = Customer.query(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"], category=data_json["product"]["category"]))

# Query using ndb.AND
query = Customer.query(ndb.AND(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"]), Customer.products == Product(category=data_json["product"]["category"])))

另外,如果你执行filtering in multiple steps,查询也有效:

# Your request
query = Customer.query(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"], category=data_json["product"]["category"]))
results = query.fetch(projection=[Customer.products.price])

# Request performing filter in multiple steps
query = Customer.query(Customer.products == Product(category=data_json["product"]["category"]))
query1 = query.filter(Customer.products == Product(manufacturer=data_json["product"]["manufacturer"]))
results = query1.fetch(projection=[Customer.products.price])

尽管我建议使用 ndb.AND(),但您可以使用任何一种建议的替代方法,因为它可以最大限度地减少代码,也是组合 AND 操作的最佳方式。


更新带有一些代码:

app.yaml

runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /.*
  script: main.app

main.py

import webapp2
from google.appengine.ext import ndb

# Datastore Models
class Product(ndb.Model):
    manufacturer = ndb.StringProperty()
    category = ndb.StringProperty()
    price = ndb.FloatProperty()

class Customer(ndb.Model):
    customerId = ndb.StringProperty()
    name = ndb.StringProperty()
    products = ndb.StructuredProperty(Product, repeated=True)

# Create entities for testing purposes
class CreateEntities(webapp2.RequestHandler):
    def get(self):
        prod1 = Product(manufacturer="Google", category="GCP", price=105.55)
        prod2 = Product(manufacturer="Google", category="GCP", price=123.45)
        prod3 = Product(manufacturer="Google", category="Drive", price=10.38)
        prod1.put()
        prod2.put()
        prod3.put()

        cust1 = Customer(customerId="Customer1", name="Someone", products=[prod1,prod2,prod3])
        cust2 = Customer(customerId="Customer2", name="Someone else", products=[prod1])
        cust3 = Customer(customerId="Customer3", name="Noone", products=[prod3])
        cust1.put()
        cust2.put()
        cust3.put()

        # Response text
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('Done creating entities')

class GetEntities(webapp2.RequestHandler):
    def get(self):
        # This will not work
        #query = Customer.query(Customer.products == Product(category="GCP", manufacturer="Google"))
        #results = query.fetch(projection=[Customer.products.price])

        # Alternative 1 - WORKS
        #query = Customer.query(Customer.products == Product(category="GCP"))
        #query1 = query.filter(Customer.products == Product(manufacturer="Google"))
        #results = query1.fetch(projection=[Customer.products.price])

        # Alternative 2 - WORKS
        query = Customer.query(ndb.AND(Customer.products == Product(manufacturer="Google"), Customer.products == Product(category="GCP")))
        results = query.fetch(projection=[Customer.products.price])

        self.response.out.write('<html><body>')
        for result in results:
            self.response.out.write("%s<br><br>" % result)
        self.response.out.write('</body></html>')

app = webapp2.WSGIApplication([
    ('/createEntities', CreateEntities),
    ('/getEntities', GetEntities),
], debug=True)

【讨论】:

  • 您好,desto,感谢您的帖子。我同意 ndb.AND() 会使查询的过滤条件更加清晰。这 3 个不同的查询生成相同的结果。但是,问题是一旦使用投影就没有发现任何东西。我用所有三个不同的查询尝试了投影,但没有一个有效。
  • 我很惊讶这对你不起作用。我重现了您的相同问题并通过更改过滤器的制作方式解决了它。您是在开发服务器中还是在生产 GAE 中运行代码?开发服务器会自动为您创建索引,因此它可能是更好的测试选择。另外,我用我正在使用的代码的 sn-p 更新了我的答案,以便您进行比较。在我看来,您的代码和我的代码之间的唯一区别是您正在从 JSON 读取数据,而我的代码是硬编码的,但这不应该影响,因为如果没有投影,您的过滤器就可以工作。
  • 嗨 dsesto,你是对的。我进行了另一项测试并验证了该建议按预期工作。我可以通过将每个条件分解为一个单独的过滤器来使用投影(如代码中提到的备选方案 1)。上次它对我不起作用的原因是我在查询过滤器中的 Product 中有多个属性。 -> query.filter(Customer.products == Product(category='A',manufacturer='Nike')) 通过将它们分成两个过滤器来工作。感谢您的帮助。
  • 没问题,@James,我很高兴能帮上忙!
猜你喜欢
  • 2018-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
相关资源
最近更新 更多