在Google App Engine上过滤和排序音乐信息

1 投票
3 回答
992 浏览
提问于 2025-04-15 13:18

我在谷歌应用引擎上做了一些简单的应用,感觉挺不错的,但现在我遇到了一些问题,想要在这个平台上搭建一个音乐收藏整理工具。简单来说,我不知道怎么在多个属性上进行筛选,同时又能按另一个属性排序。

假设我们有一个核心模型叫做专辑,它包含几个属性,比如:

  • 标题
  • 艺术家
  • 唱片公司
  • 出版年份
  • 音乐类型
  • 时长
  • 曲目名称列表
  • 情绪列表
  • 插入数据库的时间

假设我想用这些属性来筛选整个收藏,然后再按以下其中一个属性排序:

  • 出版年份
  • 专辑时长
  • 艺术家名字
  • 信息添加到数据库的时间

我不知道怎么做到这一点,而不遇到索引爆炸的问题。具体来说,我想做的事情是:

Albums.all().filter('publication_year <', 1980).order('artist_name')

我知道这样做是不可能的,但有没有其他解决办法呢?

这看起来是一个比较通用的应用类型。音乐专辑也可以是餐厅、葡萄酒瓶或者酒店。我有一堆带有描述性属性的物品,想要进行筛选和排序。

有没有我忽视的最佳实践数据模型设计?有什么建议吗?

3 个回答

0

正如你所说的,你不能在一个字段上使用不等式条件,而在另一个字段上进行排序(或者在两个字段上都使用不等式条件等等)。解决这个问题的方法就是先用“最合适”的不等式条件来获取数据(这里的“最合适”是指预计能得到最少数据的条件),然后再在你的应用程序中用Python代码进一步处理和排序。

Python的列表推导式(还有其他循环方式)、列表的sort方法、内置的sorted函数,以及标准库中的itertools模块等等,都能大大简化在Python中执行这些任务的过程。

1

因为存储空间便宜,你可以自己创建一个基于列表属性的索引文件,里面的关键字可以反映你想要的排序标准。

class album_pubyear_List(db.Model):
    words = db.StringListProperty()

class album_length_List(db.Model):
    words = db.StringListProperty()

class album_artist_List(db.Model):
    words = db.StringListProperty()

class Album(db.Model):
    blah...

    def save(self):
        super(Album, self).save()

        # you could do this at save time or batch it and do
        # it with a cronjob or taskqueue

        words = []

        for field in ["title", "artist", "label", "genre", ...]:
            words.append("%s:%s" %(field, getattr(self, field)))

        word_records = []
        now = repr(time.time())
        word_records.append(album_pubyear_List(parent=self, key_name="%s_%s" %(self.pubyear, now)), words=words)
        word_records.append(album_length_List(parent=self, key_name="%s_%s" %(self.album_length, now)), words=words)
        word_records.append(album_artist_List(parent=self, key_name="%s_%s" %(self.artist_name, now)), words=words)
        db.put(word_records)

当你需要搜索的时候,就可以创建一个合适的 WHERE 条件,然后调用相应的模型。

where = "WHERE words = " + "%s:%s" %(field-a, value-a) + " AND " + "%s:%s" %(field-b, value-b) etc.
aModel = "album_pubyear_List" # or anyone of the other key_name sorted wordlist models

indexes = db.GqlQuery("""SELECT __key__ from %s %s""" %(aModel, where))
keys = [k.parent() for k in indexes[offset:numresults+1]] # +1 for pagination
object_list = db.get(keys) # returns a sorted by key_name list of Albums
1

这里有几种选择:你可以尽量过滤数据,然后在内存中对结果进行排序,就像Alex建议的那样,或者你可以重新设计你的数据结构,使用相等的过滤条件,而不是不相等的。

举个例子,假设你只想按十年为单位来过滤数据,你可以添加一个字段,记录这首歌是在哪个十年录制的。要查找某个十年之前或之后的所有歌曲,可以使用一个IN查询,列出你想要的十年。这需要为每个包含的十年做一个基础查询,但如果记录数量很大,这样做可能比先获取所有结果再在内存中排序要便宜。

撰写回答