Odoo优化层级关系查询效率的方法

《Odoo10 Development Essentials》Chapter5(p106)中介绍了Odoo中分层关系的建立。 代码如下

class Tags(models.Model):
    _name = 'todo.task.tag'
    ...
    _parent_store = True
    parent_id = fields.Many2one('todo.task.tag', 'Parent Tag',
                                                    ondelete='restrict')
    parent_left = fields.Integer('Parent Left', index=True)
    parent_right = fields.Integer('Parent Right', index=True)

在上面的代码中。

  • 使用parent_id字段来关联上层记录。
  • 使用_parent_store = True这个属性来开启分层搜索功能.
  • 使用parent_leftparent_right字段来进行记录所属层级,当时看书的时候对这些代码不是很理解,只是知道这样做能够提高层级关系数据模型查询数据记录的效率。

简单原理

查询分层结构记录时,一般的想到的方法是从根目录开始,对每个子目录进行递归查询.然后才能得出具体的分层结构。(如递归查询文件夹文件)

Odoo中为了提高层次结构(树状结构)查询效率,每一条层级数据记录添加parent_leftparent_right字段. 假设A是B的上级对象。那么存在这样的逻辑关系。

B.parent_right  > A.parent_left
B.parent_left  > A.parent_left
B.parent_right < A.parent_right
B.parent_left < A.parent_right

画个图来理解下

image.png

  • 可以看到,图中的B属于A的,清楚的表示了A,B的层级从属关系。

Odoo 应用

我们用Odoo11的product模块作为演示 在/addons/product/models/product.py文件中.看到产品目录(ProductCategory类.15行起)的代码

class ProductCategory(models.Model):
   _name = "product.category"
   _description = "Product Category"
   _parent_name = "parent_id"
   _parent_store = True
   _parent_order = 'name'
   _rec_name = 'complete_name'
   _order = 'parent_left'

   name = fields.Char('Name', index=True, required=True, translate=True)
   complete_name = fields.Char(
       'Complete Name', compute='_compute_complete_name',
       store=True)
   parent_id = fields.Many2one('product.category', 'Parent Category', index=True, ondelete='cascade')
   child_id = fields.One2many('product.category', 'parent_id', 'Child Categories')
   parent_left = fields.Integer('Left Parent', index=1)
   parent_right = fields.Integer('Right Parent', index=1)

添加parent_L/R数值

    All (0, 11)
    ----Internal (1, 2)
    ----Saleable (3,10)
         ----Service (6,7)
         ----Physical (4,5)
         ----Software (8,9)
  • 通过上面的展示,可以清楚的看到所有产品目录各自的包含结构。根目录为All,然后依次为Internal,Saleable. Saleable下面又有3个子目录…

假设要找到All产品目录下的所有产品目录

demo=# select id,name from product_category where parent_left > 0 and parent_right < 11;
 id |   name
----+----------
  3 | Internal
  6 | Physical
  5 | Software
  2 | Saleable
  4 | Service
(5 rows)
  • 只需要一条查询语句即可找到所有子目录.无须遍历. 要找Saleable下的所有子目录即可使用 parent_left > 3 and parent_right < 10作为条件

结论:

在Odoo的实际使用中,发现使用parent存储特性的模块主要涉及account, product, stock_location. 因为这个优化对查询层级结构效率有良好效果。 凡事皆有两面,这种存储特性会在数据库中添加多余的字段。其实是以空间换时间。


参考

https://stackoverflow.com/questions/11861436/parent-left-and-parent-right-in-openerp