What database are you using ? In our e-file system, we have something similar with court cases, but we use db functions to do the heavy lifting for use, since in postgres they can be called with a select directly ...
*Ben Duncan* DBA / Chief Software Architect Mississippi State Supreme Court Electronic Filing Division On Sat, Nov 24, 2018 at 10:31 AM Val K <valq7...@gmail.com> wrote: > > running example: > > # fake table in which result of recursive select will be temporary stored > # id-values will be inherited from parent_child table > db.define_table('entry_collector', > Field('child', 'integer'), > Field('xpath', 'json'), # array of ids, xpath[0] == root, > xpath[-1] == child > Field('root', 'integer'), > Field('xdepth', 'integer'), > migrate_enabled = False, > fake_migrate = True > ) > > > def with_recursive(parent, child, roots_select, q, *fields, > **select_kwargs): > """ > parent, child - fields obj ( like db.parent_child.parent, > db.parent_child.child ) > roots_select - sql string (like 'select 123 as id' or > db(db.person.id.belongs([11,22,33])._select(db.person.id)) > q, fields, select_kwargs - args that will pass to dal: > db(q).select(*fields, **select_kwargs) > select_kwargs may include 'entry_collector' - name of fake table for > recursive (default is 'entry_collector') > returns a regular rows dal object (nothing new) > """ > > entry_collector = select_kwargs.pop('entry_collector', > 'entry_collector') > args = Storage( > entry = parent.table._tablename, > parent = parent.name, > child = child.name, > entry_collector = entry_collector, > roots = roots_select > ) > > rec_sql_s = \ > """ > WITH RECURSIVE > %(entry_collector)s(id, child, xpath, root, xdepth) AS > (SELECT NULL, id, "[" || id || "]", id, 0 FROM (%(roots)s) > UNION > SELECT %(entry)s.id, > %(entry)s.%(child)s, > rtrim(xpath,"]") || "," || %(entry)s.%(child)s || "]", > %(entry_collector)s.root, > %(entry_collector)s.xdepth + 1 > FROM %(entry_collector)s > JOIN %(entry)s ON > NOT instr(%(entry_collector)s.xpath, > %(entry)s.%(parent)s || "," ) > AND %(entry)s.%(parent)s = %(entry_collector)s.child > ORDER BY 5 DESC /* means BY xdepth */ > > ) > """ % args > > q = db(q) > dal_select = q._db._adapter._select_aux > def patch_select(*args, **kwargs): > if args: > is_recursive = False > for fld in args[1]: > if fld.table._tablename == 'entry_collector': > is_recursive = True > break > if is_recursive: > args = list(args) > args[0] = rec_sql_s + args[0] > print 'with rec: ', args[0] > return dal_select(*args, **kwargs) > > q._db._adapter._select_aux = patch_select > try: > ret = q.select(*(fields + (db[entry_collector].id,)), > **select_kwargs) > finally: > q._db._adapter._select_aux = dal_select > return ret > > > > > On Thursday, November 22, 2018 at 2:41:23 AM UTC+3, BigBaaadBob wrote: >> >> The use case is manufacturing. Large complicated manufacturing with >> special requirements. And SAP need not apply... :-) >> >> On Wednesday, November 21, 2018 at 1:26:56 PM UTC-8, Dave S wrote: >>> >>> >>> >>> On Wednesday, November 21, 2018 at 10:33:13 AM UTC-8, BigBaaadBob wrote: >>>> >>>> I'm just trying to find a good solid way of doing the BOM pattern using >>>> the DAL, and pretty much all of the decent articles I've found say the >>>> Closure Table method is the best trade-off, especially for large-ish and >>>> deep-ish BOM structures. >>>> >>> >>> It would be interesting to hear your use case. Are you into a >>> scheduling problem like the airport/flight example? Or an organizational >>> example where you need to quickly find the director in the hierarchy above >>> one us grunts? >>> >>> >>>> But, I'm not dogmatic. How would you code up a version using "with >>>> recursive" queries using the DAL? If you post a running example it would be >>>> great at informing the group! >>>> >>>> On Wednesday, November 21, 2018 at 9:56:48 AM UTC-8, Val K wrote: >>>>> >>>>> Why do you have to use this crutches (despite they are genius)? Now, >>>>> even Sqlite3 supports 'with recursive' queries. >>>>> And what do you mean under BOM and large tree? If we are talking >>>>> about BOM of real (physical) object like a car or even an aircraft >>>>> carrier, I think it is not large tree >>>>> only if you don't want to have BOAOM (bill of atoms of materials) >>>>> >>>>> >>> My BOM experience is more with circuit boards, and there would probably >>> a dozen part numbers for resistors and and a dozen part numbers for >>> capacitors, and more than a dozen ICs. But there could be a dozen or a >>> hundred boards using part X, and if you need to figure out which boards are >>> affected when the manufacturer stops manuffing the part, it starts getting >>> interesting. If you also make boxes the boards go into, then the hierarchy >>> gains another level (although not many entries at that level). >>> >>> >>> >>>> On Wednesday, November 21, 2018 at 7:58:48 PM UTC+3, BigBaaadBob wrote: >>>>>> >>>>>> I went ahead and coded something up, inspired by Massimo's Preorder >>>>>> Traversal example. I wouldn't be offended if people suggest how to make >>>>>> it >>>>>> better/faster, perhaps by combining stuff in the Link function into one >>>>>> query instead of many. >>>>>> >>>>>> # Demonstrate closure tables. Deletion of nodes is left as an >>>>>> exercise to the reader. >>>>>> # See: >>>>>> http://dirtsimple.org/2010/11/simplest-way-to-do-tree-based-queries.html >>>>>> >>>>>> >>>>>> from gluon import DAL, Field >>>>>> >>>>>> db=DAL('sqlite://closure.db') >>>>>> >>>>>> db.define_table( >>>>>> 'thing', >>>>>> db.Field('name') >>>>>> ) >>>>>> db.thing.truncate() >>>>>> >>>>>> db.define_table( >>>>>> 'closure', >>>>>> db.Field('parent', type='reference thing'), >>>>>> db.Field('child', type='reference thing'), >>>>>> db.Field('depth', type='integer') >>>>>> ) >>>>>> db.closure.truncate() >>>>>> >>>>>> def link(parent_id,child_id): >>>>>> """ link(1,3) """ >>>>>> p = db.closure.with_alias('p') >>>>>> c = db.closure.with_alias('c') >>>>>> rows = db((p.child==parent_id) & (c.parent==child_id)).select( >>>>>> p.parent.with_alias('parent'), >>>>>> c.child.with_alias('child'), >>>>>> (p.depth+c.depth+1).with_alias('depth')) >>>>>> for row in rows: >>>>>> db.closure.insert(parent=row.parent, child=row.child, >>>>>> depth=row.depth) >>>>>> >>>>>> def add_node(name,parent_name): >>>>>> """ add_node('Fruit','Food') """ >>>>>> child_id=db.thing.insert(name=name) >>>>>> db.closure.insert(parent=child_id, child=child_id, depth=0) >>>>>> if parent_name is not None: >>>>>> parent_id=db(db.thing.name==parent_name).select().first().id >>>>>> link(parent_id, child_id) >>>>>> >>>>>> def ancestors(name): >>>>>> """ print ancestors('Red')""" >>>>>> node=db(db.thing.name==name).select().first() >>>>>> return db((db.closure.child==node.id) & (db.closure.parent != >>>>>> node.id)).select( >>>>>> db.thing.name, left=db.thing.on(db.thing.id==db.closure.parent), >>>>>> orderby=db.closure.depth) >>>>>> >>>>>> def descendants(name): >>>>>> """ print descendants('Fruit')""" >>>>>> node=db(db.thing.name==name).select().first() >>>>>> return db((db.closure.parent==node.id) & (db.closure.child != >>>>>> node.id)).select( >>>>>> db.thing.name, left=db.thing.on(db.thing.id==db.closure.child), >>>>>> orderby=db.closure.depth) >>>>>> >>>>>> def closure(): >>>>>> """ print closure() """ >>>>>> parent = db.thing.with_alias('parent') >>>>>> child = db.thing.with_alias('child') >>>>>> return db().select(db.closure.id, parent.name, child.name, >>>>>> db.closure.depth, >>>>>> left=(parent.on(parent.id == >>>>>> db.closure.parent), >>>>>> child.on(child.id == db.closure.child))) >>>>>> >>>>>> def test(): >>>>>> add_node('Food',None) >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> add_node('Vehicle',None) >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> add_node('Fruit','Food') >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> add_node('Meat','Food') >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> add_node('Red','Fruit') >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> add_node('Chevy','Vehicle') >>>>>> db.commit() >>>>>> print closure() >>>>>> >>>>>> print "descendants of 'Food'" >>>>>> print descendants('Food') >>>>>> >>>>>> print "ancestors of 'Red'" >>>>>> print ancestors('Red') >>>>>> >>>>>> test() >>>>>> >>>>>> >>>>>> >>>>>> On Tuesday, November 20, 2018 at 5:02:33 PM UTC-8, BigBaaadBob wrote: >>>>>>> >>>>>>> Has anyone implemented a closure table with triggers >>>>>>> <http://dirtsimple.org/2010/11/simplest-way-to-do-tree-based-queries.html> >>>>>>> approach >>>>>>> to hierarchy (specifically for a Bill of Materials (BOM) pattern) in >>>>>>> Web2Py's DAL? >>>>>>> >>>>>>> I've seen Massimo's implementation of Preorder Traversal which >>>>>>> doesn't work for BOM patterns where there are multiple roots. The >>>>>>> Adjacency >>>>>>> Table method is slow for large trees. >>>>>>> >>>>>>> In a Bill of Materials situation >>>>>>> <http://www.vertabelo.com/blog/technical-articles/identifying-the-bill-of-materials-bom-structure-in-databases>, >>>>>>> there are multiple roots in the main table, like this: >>>>>>> >>>>>>> db.define_table('item', >>>>>>> Field('name', type='string', length=128, label=T('Name'))) >>>>>>> >>>>>>> db.define_table('bill_of_materials', >>>>>>> Field('parent_item_id', type='reference item', notnull=True, >>>>>>> label=T('Parent Item')), >>>>>>> Field('child_item_id', type='reference item', notnull=True, >>>>>>> label=T('Child Item')), >>>>>>> Field('quantity', type='decimal(8,2)', default='1.0', >>>>>>> label=T('Quantity'))) >>>>>>> >>>>>>> >>>>>>> >>> Interesting reading. >>> >>> /dps >>> >>> >> -- > Resources: > - http://web2py.com > - http://web2py.com/book (Documentation) > - http://github.com/web2py/web2py (Source code) > - https://code.google.com/p/web2py/issues/list (Report Issues) > --- > You received this message because you are subscribed to the Google Groups > "web2py-users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to web2py+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.