假设我有一个电影数据库,您可以在其中按标题搜索。
我有一个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()
只是用于查询
如果我正在搜索电影《阿甘正传》,我希望它出现在以下搜索词中(以及更多,这些只是示例)
- 'fo' - '森林' 以 'fo' 开头
- 'gu' - 'gump' 以 'gu' 开头
- 'gu fo' - 'forest' 以 'fo' 开头,'gump' 以 'gu' 开头
我可以通过类似于以下的查询轻松获得前两个
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'开头的项目和一个以'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
虽然仍在寻找更好的东西
谢谢