Google app engine 在应用程序引擎数据存储上的查询等价物之间?

Google app engine 在应用程序引擎数据存储上的查询等价物之间?,google-app-engine,google-cloud-datastore,gql,Google App Engine,Google Cloud Datastore,Gql,我有一个包含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

我有一个包含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
其思想是,在添加自定义属性后,我可以按原样导入数据集,然后基于ListProperty运行查询,如下所示:

q = Country.gql('WHERE ip_range = :1', my_num_ipaddress)
当我尝试插入新的国家/地区对象时,这失败了,抱怨无法创建名称:

...
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\u name
方法,或者只是设置
self.name
,但似乎没有帮助。无望地被卡住或朝着正确的方向前进?

简短回答:目前不支持中间查询。然而,如果你先验地知道你的范围将相对较小,那么你可以伪造它:只需在实体上存储一个包含范围内每个数字的列表。然后,您可以使用简单的相等过滤器来获取其范围包含特定值的实体。显然,如果你的射程太大,这是行不通的。但它的工作原理如下:

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)

更好的解决方案(最终-由于一个bug,目前以下仅适用于开发服务器(请参阅)。理论上,您可以绕过您提到的限制,通过利用
db.ListProperty
的查询方式来执行范围查询。其思想是将范围的开始和结束存储在列表中(在您的例子中,是表示IP地址的整数)。然后获取其范围包含某个值
v
(即列表中两个值之间)的实体,您只需在列表上使用两个不等式过滤器执行查询—一个确保
v
至少与列表中最小的元素一样大,另一个确保
v
至少与列表中最大的元素一样小

下面是如何实现此技术的一个简单示例:

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)
M类(db.Model):
r=db.ListProperty(int)
#创建一个M的实例,该实例的rnage从'begin'到'end'(包括在内)
M(r=[begin,end]).put()
#查询以查找包含值'v'的M的实例`

q=M.gql('其中r>=:1和r我的解决方案不符合您要求的模式,但我认为它在app engine上运行良好。我使用CIDR范围的字符串列表来定义IP块,而不是特定的开始和结束编号

from google.appengine.ext import db    
class Country(db.Model):
    subnets = db.StringListProperty()
    country_code = db.StringProperty()

c = Country()
c.subnets = ['1.2.3.0/24', '1.2.0.0/16', '1.3.4.0/24']
c.country_code = 'US'
c.put()

c = Country()
c.subnets = ['2.2.3.0/24', '2.2.0.0/16', '2.3.4.0/24']
c.country_code = 'CA'
c.put()

# Search for 1.2.4.5 starting with most specific block and then expanding until found    
result = Country.all().filter('subnets =', '1.2.4.5/32').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/31').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/30').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.0/29').fetch(1)
# ... repeat until found
# optimize by starting with the largest routing prefix actually found in your data (probably not 32)

好主意。不过,这通常需要多次往返数据存储—如果您不需要扩展,或者这种查找不太频繁,这也没关系。(每次往返至少需要60-80ms)。我的解决方案以存储换取速度,而不是你的解决方案。假设我们考虑所有地址,你的列表项大约有3B个。为了减少我的往返次数,你可以一次对所有可能的情况进行in查询。in查询肯定会有助于减少运行时间-尽管这会导致非常昂贵的查询,因为in查询将被分成[最多]30个子查询。(如果您想要这样的匹配,那么在返回的结果上有一个内存中最长的前缀匹配;与我的解决方案相同。)也就是说,尽管在大地址范围上执行很昂贵:)(+1)这很聪明,但对我来说更容易理解David的解决方案,所以我将首先尝试。完整的数据集,每个国家的ip范围)顺便说一句,我使用的起始点只有130k个条目,所以还不算太糟。@tijs我对条目的引用是所有列表中的IP地址总数。您有130k个范围,但每个范围可能包含数万个地址。David的解决方案更容易实现。祝您好运!错误就是开发人员应用程序的方式不幸的是,服务器执行的是:正确的行为是当前的生产行为。这看起来是一个很有前途的解决方案,很好而且简单。从文档中,我似乎可以创建一个自定义模型属性,根据begin_ipnum和end_ipnum的值报告列表值“live”。但据我所知,我无法查询客户m属性使用GQL。因此,我将首先在对象创建时尝试计算范围的ListProperty值。我将报告我的进展情况!是否有7.5年前回答的这个问题的更新?现在是否有可能的中间查询?
from google.appengine.ext import db    
class Country(db.Model):
    subnets = db.StringListProperty()
    country_code = db.StringProperty()

c = Country()
c.subnets = ['1.2.3.0/24', '1.2.0.0/16', '1.3.4.0/24']
c.country_code = 'US'
c.put()

c = Country()
c.subnets = ['2.2.3.0/24', '2.2.0.0/16', '2.3.4.0/24']
c.country_code = 'CA'
c.put()

# Search for 1.2.4.5 starting with most specific block and then expanding until found    
result = Country.all().filter('subnets =', '1.2.4.5/32').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/31').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/30').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.0/29').fetch(1)
# ... repeat until found
# optimize by starting with the largest routing prefix actually found in your data (probably not 32)