Между запросом в хранилище данных App Engine? - PullRequest
5 голосов
/ 26 июля 2010

У меня есть модель, содержащая диапазоны 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 быть «живым» или его нужно вычислять при создании записи?

вопрос дополнен первым ударом по решению:

Итак, основываясь на ответе Дэвида ниже и таких статьях:

http://appengine -cookbook.appspot.com / рецепт / заказ модели-свойства-это мило /

Я пытаюсь добавить настраиваемое поле в мою модель, например:

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, но это, похоже, не помогает. Безнадежно застрял или движется в правильном направлении?

Ответы [ 3 ]

2 голосов
/ 27 июля 2010

Мое решение не соответствует запрошенному вами шаблону, но я думаю, что оно будет хорошо работать на движке приложения.Я использую список строк диапазонов 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)
2 голосов
/ 27 июля 2010

Краткий ответ: Между запросами в настоящий момент не поддерживается. Однако, если вы априори знаете, что ваш диапазон будет относительно небольшим, вы можете подделать его: просто сохраните список на объекте с каждым числом в диапазоне. Затем вы можете использовать простой фильтр равенства, чтобы получить сущности, диапазоны которых содержат определенное значение. Очевидно, это не сработает, если ваш диапазон большой. Но вот как это будет работать:

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)

Лучшее решение (в конечном итоге - пока следующее работает только на сервере разработки из-за ошибки (см. выпуск 798 ). Теоретически, вы можете обойти упомянутые ограничения и выполнить диапазон запрос, используя преимущества запроса 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)
0 голосов
/ 03 августа 2010

Это странно, потому что я пытаюсь выяснить ту же самую базовую предпосылку и сталкиваюсь со всеми видами проблем (и исходя из номера записи 130 КБ, предположим, что вы используете файл MaxMind Geo Country). Хотя ответ Дэвида сработал бы достаточно просто, на практике это не сработало. Это из Google:

Во-первых, если у запроса есть несколько фильтров неравенства для данного свойства, объект будет соответствовать запросу, только если у него есть отдельное значение для этого свойства, которое соответствует всем фильтрам неравенства. Например, если сущность имеет значения [1, 2] для свойства x, она не будет соответствовать запросу WHERE x> 1 AND x <2. Каждый фильтр соответствует одному из значений x, но нет единственного значения, которое соответствует обоим фильтры. </em>

Я загрузил весь набор данных, создав поле listproperty, чтобы попытаться безрезультатно.

Так что любая дополнительная помощь будет принята с благодарностью!

...