10.7 有关更多的索引维护工作

    很明显,在shelf索引维护方面可能会需要做更多的工作。当使用简单的数据模型时,可以简单的为一篇文章的标签,日期和标题添加索引。这里为博客的另外一个访问层定义了两个索引。其中一个索引简单的列出了博客记录的所有键值。另外一个索引则基于博客标题提供相应键值。这里假设标题不是唯一的。我们会从3个方面来演示这个访问层的操作。以下是CRUD处理过程中的Create部分。

    c1ass Access4( Access2 ):
       def new( self, args, **kw ):
         super().new(
    args, **kw )
         self.database['_DB:Blog']= list()
         self.database['_DB:Blog_Title']= defaultdict(list)

       def add_blog( self, blog ):
         self.max['Blog'] += 1
         key= "Blog:(id)".format(id=self.max['Blog'])
         blog._id= key
         blog._post_list= []
         self.database[bloq._id]= blog
         self.darabase['_DB:Blog'].append( blog._id )
         
    blog_title= self.database['_DB:Blog_Title']
         blog_title[blog.title].append( blog._id )
         self.database['_DB:Blog_Title']= blog_title
         return blog

    我们添加了两个索引:一个简单的Blog键列表和另一个保存了与某个给定标题相关的键的defaultdict。如果每个标题都是唯一的,那么所有的列表都是单例的。如果标题不唯一,那么每个标题都会有一个Blog键的列表。

    当添加Blog实例时,也需要更新这两个索引。Blog键的简单列表通过追加一个新键然后保存到 shelf 上的方式更新。标题索引需要从 shelf 上获取现存的defaultdict,然后将与Blog标题匹配的键列表追加到它的尾部,最后将defaultdict保存在shelf上。下面的代码展示了CRUD中U(更新)的部分。

    def update_blog( self, blog ):
      """Replace this Blog; update index."""
      self.database[blog._id]= blog
      blog_title= self.database['_DB:Blog_Title']
      # Remove key from index in old spot.
      empties= []
      for k in blog_title:
        if blog._id in blog_title[k]:
          blog_title[k].remove( blog._id )
          if len(blog_title[k]) == 0: empties.append( k )
      # Cleanup zero-length lists from defaultdict.
      for k in empties:
        del blog_title[k]
      # Put key into index in new spot.
      
    blog_title[blog.title].append( blog._id )
      
    self.database['_DB:Blog_Title']= blog_title

    当我们更新Blog对象时,我们可能会更改Blog属性的标题。如果实体中包含越来越多的属性和索引,我们可能希望可以比较修改后的值和shelf上的值,这样就能知道哪些属性被更改了。对于这个只有一个属性的简单实体,不需要任何比较就可以知道哪个属性被更改了。

    这个操作的第1个部分是将Blog的键从索引中移除。由于还没有得到Blog.title属性之前的值,因此我们不能简单地基于旧的值移除键。取而代之的是,我们必须搜索所有Blog键的索引,并且将与标题相关的键删除。

    标题唯一的Blog会让标题的键列表为空。我们也应该清理无用的标题。

    一旦将与旧标题相关的键从索引中移除,就可以用新标题把键添加到索引中。最后的两行代码和一开始创建Blog时所用的相同。下面是一些查询处理的例子。

    def blog_iter( self ):
      return ( self.database[k] for k in self.database['_DB:Blog'] )

    def blog_title_iter( self, title ):
      blog_title= self.database['_DB:Blog_Title']
      return ( self.database[k] for k in blog_title[title] )

    blog_iter()方法函数通过从 shelf 上获取索引对象来遍历所有的Blog对象。blog_title_iter()方法函数用索引获取某个给定标题相关的Blog对象。当有许多标题不同的Blog对象时,这个方法可以快速地根据标题定位到一个Blog对象。