Google App Engine数据存储区 - 在投影和过滤器中使用StructuredProperty进行查询

时间:2018-03-06 05:59:01

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

我有几个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])

但如果我使用没有滤镜的投影,投影部分工作正常。以下查询将返回所有实体,但只返回'price'属性

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

有什么想法?感谢。

BTW,我的查询是基于这篇文章开发的。 https://cloud.google.com/appengine/docs/standard/python/ndb/queries#filtering_structured_properties

1 个答案:

答案 0 :(得分:0)

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

通过下面的查询,您在过滤器中执行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)