App Engine 数据存储上的查询等效项之间?
我有一个包含 IP 地址范围的模型,类似于:
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
在 SQL 数据库上,我将能够找到包含特定范围内的 IP 的行,如下所示:
SELECT * FROM Country WHERE ipnum BETWEEN begin_ipnum AND end_ipnum
或这样:
SELECT * FROM Country WHERE begin_ipnum < ipnum AND end_ipnum > ipnum
可悲的是,GQL 只允许对一个属性进行不等式过滤,并且不支持 BETWEEN
语法。我如何解决这个问题并构建与 App Engine 上的查询等效的查询?
另外,ListProperty
可以是“实时”的还是必须在创建记录时进行计算?
问题已更新,并首次尝试解决方案:
因此,根据下面 David 的回答以及此类文章:
http://appengine-cookbook.appspot.com/recipe/custom-model-properties-are-cute/
我正在尝试添加我的模型的自定义字段如下所示:
class IpRangeProperty(db.Property):
def __init__(self, begin=None, end=None, **kwargs):
if not isinstance(begin, db.IntegerProperty) or not isinstance(end, db.IntegerProperty):
raise TypeError('Begin and End must be Integers.')
self.begin = begin
self.end = end
super(IpRangeProperty, self).__init__(self.begin, self.end, **kwargs)
def get_value_for_datastore(self, model_instance):
begin = self.begin.get_value_for_datastore(model_instance)
end = self.end.get_value_for_datastore(model_instance)
if begin is not None and end is not None:
return range(begin, end)
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
ip_range = IpRangeProperty(begin=begin_ipnum, end=end_ipnum)
想法是,在添加自定义属性后,我可以按原样导入数据集,然后基于 ListProperty 运行查询,如下所示:
q = Country.gql('WHERE ip_range = :1', my_num_ipaddress)
当我尝试插入新的 Country 对象时,这会失败,抱怨无法创建名称:
...
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py", line 619, in _attr_name
return '_' + self.name
TypeError: cannot concatenate 'str' and 'IntegerProperty' objects
我尝试为新属性定义一个 attr_name
方法,或者只是设置 self.name
但这似乎没有帮助。陷入绝望还是正朝着正确的方向前进?
I have a model containing ranges of IP addresses, similar to this:
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
On a SQL database, I would be able to find rows which contained an IP in a certain range like this:
SELECT * FROM Country WHERE ipnum BETWEEN begin_ipnum AND end_ipnum
or this:
SELECT * FROM Country WHERE begin_ipnum < ipnum AND end_ipnum > ipnum
Sadly, GQL only allows inequality filters on one property, and doesn't support the BETWEEN
syntax. How can I work around this and construct a query equivalent to these on App Engine?
Also, can a ListProperty
be 'live' or does it have to be computed when the record is created?
question updated with a first stab at a solution:
So based on David's answer below and articles such as these:
http://appengine-cookbook.appspot.com/recipe/custom-model-properties-are-cute/
I'm trying to add a custom field to my model like so:
class IpRangeProperty(db.Property):
def __init__(self, begin=None, end=None, **kwargs):
if not isinstance(begin, db.IntegerProperty) or not isinstance(end, db.IntegerProperty):
raise TypeError('Begin and End must be Integers.')
self.begin = begin
self.end = end
super(IpRangeProperty, self).__init__(self.begin, self.end, **kwargs)
def get_value_for_datastore(self, model_instance):
begin = self.begin.get_value_for_datastore(model_instance)
end = self.end.get_value_for_datastore(model_instance)
if begin is not None and end is not None:
return range(begin, end)
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
ip_range = IpRangeProperty(begin=begin_ipnum, end=end_ipnum)
The thinking is that after i add the custom property i can just import my dataset as is and then run queries on based on the ListProperty like so:
q = Country.gql('WHERE ip_range = :1', my_num_ipaddress)
When i try to insert new Country objects this fails though, complaning about not being able to create the name:
...
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py", line 619, in _attr_name
return '_' + self.name
TypeError: cannot concatenate 'str' and 'IntegerProperty' objects
I tried defining an attr_name
method for the new property or just setting self.name
but that does not seem to help. Hopelessly stuck or heading in the right direction?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
简短回答:目前并不真正支持查询之间。但是,如果您先验地知道您的范围将相对较小,那么您可以伪造它:只需在实体上存储一个列表,其中包含范围内的每个数字。然后,您可以使用简单的相等过滤器来获取范围包含特定值的实体。显然,如果你的范围很大,这将不起作用。但它的工作原理如下:
更好的解决方案(最终 - 由于错误,目前以下仅适用于开发服务器(请参阅问题 798)。理论上,您可以解决您提到的限制并利用
db.ListProperty 被查询。这个想法是将范围的开始和结束存储在列表中(在您的例子中,是代表 IP 地址的整数),然后获取范围包含某个值
v
的实体。即,在列表中的两个值之间),您只需使用列表上的两个个不等式过滤器执行查询 - 一个用于确保v
至少与列表中的最小元素,以及确保v
至少与列表中的最大元素一样小的以下是如何实现此技术的简单示例:
Short answer: Between queries aren't really supported at the moment. However, if you know a priori that your range is going to be relatively small, then you can fake it: just store a list on the entity with every number in the range. Then you can use a simple equality filter to get entities whose ranges contain a particular value. Obviously this won't work if your range is large. But here's how it would work:
The better solution (eventually - for now the following only works on the development server due to a bug (see issue 798). In theory, you can work around the limitations you mentioned and perform a range query by taking advantage of how
db.ListProperty
is queried. The idea is to store both the start and end of your range in a list (in your case, integers representing IP addresses). Then to get entities whose ranges contain some valuev
(i.e., between the two values in your list), you simply perform a query with two inequality filters on the list - one to ensure thatv
is at least as big as the smallest element in the list, and one to ensure thatv
is at least as small as the biggest element in the list.Here's a simple example of how to implement this technique:
我的解决方案不遵循您所要求的模式,但我认为它在应用程序引擎上运行良好。我使用 CIDR 范围字符串列表来定义 IP 块,而不是特定的开始和结束编号。
My solution doesn't follow the pattern you have requested, but I think it would work well on app engine. I'm using a list of strings of CIDR ranges to define the IP blocks instead of specific begin and end numbers.