在App Engine数据存储上的查询等价之间? [英] Between query equivalent on App Engine datastore?

查看:105
本文介绍了在App Engine数据存储上的查询等价之间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含IP地址范围的模型,与此类似:

  class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()

在SQL数据库中,我将能够找到包含某个IP范围内的IP的行,如下所示:

  SELECT * FROM国家/地区WHERE ipnum BETWEEN begin_ipnum AND end_ipnum 

或此:

  SELECT * FROM Country WHERE begin_ipnum< ipnum AND end_ipnum> ipnum 

可悲的是,GQL只允许在一个属性上使用不等式过滤器,并且不支持 BETWEEN 语法。我怎样才能解决这个问题,并在App Engine上构建一个等价的查询?

是'生活'还是必须在创建记录时计算?

通过第一次刺穿解决方案更新了问题:

因此,根据David的回答以及以下文章:

http://appengine-cookbook.appspot.com/recipe/custom-model-properties-are-可爱/



我试图向我的模型添加一个自定义字段,如下所示:



<$
def __init __(self,begin = None,end = None,** kwargs):
如果不是isinstance(开始, db.IntegerProperty)或不是isinstance(end,db.IntegerProperty):
raise TypeError('Begin and End must be Integers。')
self.begin = begin
self.end = en d
super(IpRangeProperty,self).__ init __(self.begin,self.end,** kwargs)
$ b $ 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)
如果begin不是None并且end不是None:
返回范围(开始,结束)

class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.InteProProperty()
ip_range = IpRangeProperty(begin = begin_ipnum,end = end_ipnum)

这个想法是,我添加自定义属性后,我可以直接导入我的数据集,然后运行查询基于ListProperty,如下所示:

  q = Country.gql('WHERE ip_range =:1',my_num_ipaddress)

当我尝试插入新的Country对象时,虽然失败,但抱怨无法创建名称:

  ... 
文件/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py,第619行,位于_attr_name
return'_ '+ self.name
TypeError:无法连接'str'和'IntegerProperty'对象

我试着为新属性定义一个 attr_name 方法,或者只是设置 self.name ,但这似乎没有帮助。绝望地坚持或朝着正确的方向前进?

解决方案

简短回答:目前并不真正支持查询。但是,如果您事先知道您的范围会相对较小,那么您可以伪造它:只需在该范围内的每个号码的实体上存储一个列表。然后,您可以使用简单的相等过滤器来获取其范围包含特定值的实体。显然,如果你的范围很大,这将不起作用。但这里是它的工作方式:

$ p $ class M(db.Model):
r = db.ListProperty(int)

#创建一个M的实例,其范围从'begin'到'end'(包括)
M(r =范围(开始,结束+ 1))。put()

#查询以查找包含值v的M的实例
q = M.gql('WHERE r =:1',v)
issue 798 )。理论上,您可以解决你提到的限制问题,并通过查询 db.ListProperty 的方式来执行范围查询。这个想法是将你的范围的开始和结束存储在列表(在你的情况下,表示IP地址的整数),然后获得范围包含一些值 v 的实体(即在你的两个值之间列表),您只需在列表中使用两个不等式过滤器执行查询 - 一个确保 v 至少与最小元素一样大在列表中,并确保 v 至少与列表中最大的元素一样小。



下面是一个简单的例子,说明如何实现这个技巧:
$ b $ pre $ $ $ $ $ $>类M(db.Model):
r = db .ListProperty(int)

#创建一个从`begin`到`end`(含)
M(r = [begin,end])的M的实例。put ()

#查询来查找包含值v的M的实例
q = M.gql('WHERE r> =:1 AND r <=:1', v)


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?

解决方案

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:

class M(db.Model):
    r = db.ListProperty(int)

# create an instance of M which has a range from `begin` to `end` (inclusive)
M(r=range(begin, end+1)).put()

# query to find instances of M which contain a value `v`
q = M.gql('WHERE r = :1', v)


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 value v (i.e., between the two values in your list), you simply perform a query with two inequality filters on the list - one to ensure that v is at least as big as the smallest element in the list, and one to ensure that v is at least as small as the biggest element in the list.

Here's a simple example of how to implement this technique:

class M(db.Model):
    r = db.ListProperty(int)

# create an instance of M which has a rnage from `begin` to `end` (inclusive)
M(r=[begin, end]).put()

# query to find instances of M which contain a value `v`
q = M.gql('WHERE r >= :1 AND r <= :1', v)

这篇关于在App Engine数据存储上的查询等价之间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆