GAE: как построить запрос, в котором строка начинается со значения

В движке Google у меня есть следующий запрос, чтобы найти всех пользователей с заданным именем.

Когда я набираю «Ми», он перечисляет мне все «Майкл» и «Майк» в базе данных.

class User(UserMixin, ndb.Model):
    firstname = ndb.StringProperty()


 data = User.query(ndb.AND(User.firstname >= name_startsWith, User.firstname <= name_startsWith + u'\ufffd')).fetch(5)

Я хотел бы сделать его инвариантным, чтобы я мог набрать «ми», и он по-прежнему выводит те же имена.

Я попробовал lower() в Python, но это не работает с StringProperty() движка приложения.

 data = User.query(ndb.AND(User.firstname.lower() >= name_startsWith.lower(), User.firstname.lower() <= name_startsWith.lower() + u'\ufffd')).fetch(5)

Выдает ошибку:

AttributeError: объект «StringProperty» не имеет атрибута «нижний»


person Houman    schedule 13.08.2013    source источник


Ответы (3)


Вы не можете выполнять такие поиски с помощью API хранилища данных. Вы можете либо сохранить дополнительную версию поля в нижнем регистре, либо использовать API полнотекстового поиска, который предназначен для такого рода вещей.

person Daniel Roseman    schedule 13.08.2013

Вы можете использовать вычисляемое свойство:

class User(UserMixin, ndb.Model):
    firstname = ndb.StringProperty()
    name_lower = ndb.ComputedProperty(lambda self: self.firstname.lower())

https://developers.google.com/appengine/docs/python/ndb/properties#computed

А затем используйте имя, которое возвращается из вашего запроса (которое будет иметь правильный регистр).

person Rob Curtis    schedule 13.08.2013

Сохраните нормализованную версию любых строк, которые вы хотите найти таким образом. Запросы в App Engine могут выполнять только простые фильтры того, что на самом деле находится в индексах.

Попробуйте что-нибудь вроде этого

class User(ndb.Model):
    firstname = ndb.StringProperty()
    firstname_norm = ndb.StringProperty()

username = "Some Name"
User(firstname = username, firstname_norm = normalizer(username)).put()

data = User.query(ndb.AND(User.firstname_norm >= normalizer(name_startsWith), User.firstname_norm <= normalizer(name_startsWith) + u'\ufffd')).fetch(5)

где "normalizer()" - это functStore нормализованная версия любых строк, которые вы хотите иметь возможность искать таким образом. Запросы в App Engine могут выполнять только простые фильтры того, что на самом деле находится в файле indexes.ion, который преобразует строки со смешанным регистром в форму со всеми строчными буквами, а затем использовать ту же функцию при построении запросов. Это может быть простым вызовом метода строки .lower() или сложным, например удалением всех знаков препинания, преобразованием всех пробелов в одиночные пробелы, выполнением нормализации юникода и т. д., в зависимости от ваших потребностей.

def normalizer(x):
    return x.lower()
person Nijin Narayanan    schedule 13.08.2013
comment
Спасибо, Ниджин, теперь мне нужно написать кусок кода, который проходит через все пользовательские объекты и создает поле firstname_norm для всех них, верно? иначе я не смогу запросить его таким образом. - person Houman; 13.08.2013
comment
@Каве Да. Вам нужно создать firstname_norm. - person Nijin Narayanan; 13.08.2013