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

Google App Engine Datastore - query with StructuredProperty in projection and filter

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

并且我想根据 he/she 拥有的产品的 'manufacturer' 和 'category' 进行查询。所以这个查询按预期工作。

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

results= query.fetch()

但是,我无法让 "projection" 与此查询一起使用。以下查询只是 return 什么也没做。

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

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

但是如果我使用没有过滤器的投影,投影部分工作正常。以下查询将 return 所有实体,但仅 'price' 属性

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

有什么想法吗?谢谢。

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

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 的最佳方式操作。


UPDATE 一些代码:

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)