假设我有一个电影数据库,您可以按标题搜索。
我有一个Movie
模型,如下所示(简化)
class Movie(ndb.Model):
name = ndb.StringProperty(required=True)
queryName = ndb.ComputedProperty(lambda self: [w.lower() for w in self.name.split()], repeated=True)
@staticmethod
def parent_key():
return ndb.Key(Movie, 'parent')
queryName
只是Movie.name
中单词的小写列表。 parent_key()
仅适用于查询
如果我正在搜索电影Forest Gump,我希望它能够显示以下搜索字词(以及更多,这些只是示例)
我可以使用类似于以下内容的查询轻松获得前两个
movies = Movie\
.query(ancestor=Movie.parent_key())\
.filter(Movie.queryName >= x)\
.filter(Movie.queryName < x + u'\ufffd')\
.feth(10)
其中x是'fo'或'gu'。同样,这只是一个不是我的实际代码的查询。那是后来的事。如果我在上面的查询上展开一点来寻找两个单词,我认为我可以做类似下面的事情,但它不起作用。
movies = Movie\
.query(ancestor=Movie.parent_key())\
.filter(Movie.queryName >= 'fo')\
.filter(Movie.queryName < 'fo' + u'\ufffd')\
.filter(Movie.queryName >= 'gu')\
.filter(Movie.queryName < 'gu' + u'\ufffd')\
.feth(10)
现在,这不起作用,因为它正在queryName
查看它是否有任何以'fo'开头并以'gu'开头的项目。由于列表中的单个项目永远不会成立,因此查询不会返回任何内容。
问题是,如何查询Movies
哪个queryName
的项目以'fo'开头? AND 以'gu'开头的项目?
实际代码:
class MovieSearchHandler(BaseHandler):
def get(self):
q = self.request.get('q')
if q:
q = q.replace('&', '&').lower()
filters = self.create_filter(*q.split())
if filters:
movies = Movie\
.query(ancestor=Movie.parent_key())\
.filter(*filters)\
.fetch(10)
return self.write_json([{'id': m.movieId, 'name': m.name} for m in movies])
return self.write_json([])
def create_filter(self, *args):
filters = []
if args:
for prefix in args:
filters.append(Movie.queryName >= prefix)
filters.append(Movie.queryName < prefix + u'\ufffd')
return filters
更新
我目前的解决方案是
class MovieSearchHandler(BaseHandler):
def get(self):
q = self.request.get('q')
if q:
q = q.replace('&', '&').lower().split()
movieFilter, reducable = self.create_filter(*q)
if movieFilter:
movies = Movie\
.query(ancestor=Movie.parent_key())\
.filter(movieFilter)\
.fetch(None if reducable else 10)
if reducable:
movies = self.reduce(movies, q)
return self.write_json([{'id': m.movieId, 'name': m.name} for m in movies])
return self.write_json([])
def create_filter(self, *args):
if args:
if len(args) == 1:
prefix = args[0]
return ndb.AND(Movie.queryName >= prefix, Movie.queryName < prefix + u'\ufffd'), False
ands = [ndb.AND(Movie.queryName >= prefix, Movie.queryName < prefix + u'\ufffd')
for prefix in args]
return ndb.OR(*ands), True
return None, False
def reduce(self, movies, terms):
reducedMovies = []
for m in movies:
if len(reducedMovies) >= 10:
return reducedMovies
if all(any(n.startswith(t) for n in m.queryName) for t in terms):
reducedMovies.append(m)
return reducedMovies
仍在寻找更好的东西
由于