0

假设我有一个电影数据库,您可以在其中按标题搜索。

我有一个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()只是用于查询

如果我正在搜索电影《阿甘正传》,我希望它出现在以下搜索词中(以及更多,这些只是示例)

  1. 'fo' - '森林' 以 'fo' 开头
  2. 'gu' - 'gump' 以 'gu' 开头
  3. '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('&', '&amp;').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('&', '&amp;').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

虽然仍在寻找更好的东西

谢谢

4

0 回答 0