Difference between revisions of "Lazy loading Trees"
(→QA and test cases) |
(→Testcases=) |
||
Line 135: | Line 135: | ||
− | ===Testcases==== | + | ===Testcases=== |
+ | <div style="float: left; margin-right: 10px; width: 100%;"> | ||
+ | {| width="100%" cellspacing="0" cellpadding="5" border="1" style="font-size: 0.86em; line-height: 0.9em;" | ||
+ | |- | ||
+ | ! style="width: 180px; color: rgb(255, 255, 255); background-color: grey;" | Task | ||
+ | ! style="width: 170px; color: rgb(255, 255, 255); background-color: grey;" | Expectation | ||
+ | ! style="width: 70%; color: rgb(255, 255, 255); background-color: grey;" | Result | ||
+ | ! style="width: 50px; color: rgb(255, 255, 255); background-color: grey;" | Status | ||
+ | |- | ||
+ | | asd | ||
+ | | asd | ||
+ | | asd | ||
+ | | asd<br> | ||
+ | |- | ||
+ | | [[Benutzer:Mo|Mo]] 08:23, 20. Dec. 2012 (CEST) | ||
+ | | In Development | ||
+ | | Second development iteration. Cleaner code, buxfixes, improvements, more stability. | ||
+ | | <br> | ||
+ | |} | ||
+ | </div> <div style="clear: both;"></div> | ||
== Development infrastructure == | == Development infrastructure == |
Revision as of 01:57, 21 December 2012
Contents
- 1 Lazy loading Trees - Functional Specifications
- 1.1 Status
- 1.2 Contributors
- 1.3 Overview
- 1.4 Purpose
- 1.5 Miscellaneous
- 1.6 Design Considerations
- 1.7 Glossary
- 1.8 Functional Requirements
- 1.9 Acceptance criteria
- 1.10 QA and test cases
- 1.11 Development infrastructure
- 1.12 Technical Requirements
- 1.13 Data Requirements
- 1.14 Non-Functional Requirements
- 1.15 Open Discussion Items
- 1.16 Closed Discussion Items
Lazy loading Trees - Functional Specifications
Status
User, Date | Status | Description | Points |
---|---|---|---|
Mo 11:15, 17. Dec. 2012 (CEST) | In Development | First development iteration. Implementation of lazy loading tree nodes. Loading trees with some million nodes on several levels is now possible.There are still some todos in the current code. | |
Mo 08:23, 20. Dec. 2012 (CEST) | In Development | Second development iteration. Cleaner code, buxfixes, improvements, more stability. | |
Mo 11:42, 12. Jul. 2010 (CEST) | Technical Team: Approval | ... Further Information ... | |
Mo 14:46, 23. Nov. 2010 (CET) | Functional Team: Approval | ... Further Information ... | |
Ts 18:55, 26. Nov. 2010 (CET) | Other Status ... | ... Further Information ... | |
Contributors
This feature is contributed by evenos GmbH
Overview
Currently Trees in adempiere are implemented not very well. When a tree is loaded (e. g. via VTreePanel in the swing client or via ADTreePanel in the webui), the whole tree is loaded at once by MTree.java. With lazy loading, a tree should only load a node when it needs the node. This means, if only the first level of the tree is shown, the second level is not loaded now. Only if a first level node is opened, the second level nodes for only this node are loaded. Also most of the loading behaviour should be placed in the nodes itselfs or in the model, not in MTree.
Purpose
The purpose for this development is simple: it's needed. Imagin a tree with 100 noded. Each node has 100 subnodes itself and each of the subnodes has again 100 subnodes. Together, in this tree there are 1,000,000 nodes. Adempiere would not only need to query the million nodes from the database, it also would need to create a million MTreeNode objects. This will probably take some time. In fact, if you try to create a tree like this and open it with adempiere, the swing client will be not responsive for several minutes. The webui will possible throw an error like "Java heap space".
In this feature, we will refactore the whole tree handling without losing functionality.
Miscellaneous
Some parts of the tree functionality didn't work as expected. We fixed some of these issues including:
- Removing sections from the Favourites Bar when no node is left in this section
- Show Tooltips with the node description when hovering with the mouse over a node
- Searching in Trees now makes use of the name AND the description of a node
- Dropping a node on one of its children or itself is not possible anymore
- Deleting a summary node moves all its children to its parent instead of removing the nodes from the tree. This is a better solution because otherwise there would be entries in the grid which never show up in the tree!
- Pasting the same node multiple times is not possible anymore because we clear the clipboard after the paste
- Moving a node now saves the correct seqno (see https://groups.google.com/forum/#!topic/idempiere/ETF7fymVZRM 2nd post)
Design Considerations
Assumptions
- A Node is loaded when it is needed
- No more loading the whole tree at once
- Cleaner code / responsibilities
- Cleaner seperation between Swing Client / Webui
Dependencies
None. This feature should work out of the box by replacing the changed files. There are no other/new dependencies.
Constraints
- There is some code which may only work with PostgreSQL (SQL: "with recursive...") which need some adjustment to work with oracle and mysql (probably in the "convert" classes).
- Searching in the tree is not top to bottom anymore but random due to the problem that we don't have the whole tree loaded which we can search from top to bottom
- A node can still be cut (ctrl+x) even if there is already a node in the clipboard. This causes the previous cutted node to disappear (till cache is resetted or user log out and in again which causes the tree to reload)
- A node can be dragged or pasted into trees of other windows
Glossary
Functional Requirements
There are no functional requirements. Just replace the changed files.
Acceptance criteria
QA and test cases
These Trees should work like before and as expected.
Prerequisites
First we need a really big tree so we can see what the problem is with the default behaviour compared to our lazy implementation. We decided to use the Organization since we don't need much information to create a tree there.
Run the following script on your database (PostgreSQL) to create to create a tree with 100 nodes each level and 3 levels:
--generate toplevel nodes INSERT INTO adempiere.ad_treenode (ad_tree_id, node_id, ad_client_id, ad_org_id, isactive, created, createdby, updated, updatedby, parent_id, seqno) SELECT 50, 2000000 + y.id, 0, 0, 'Y', now(), 0, now(), 0, 0, 999 FROM generate_series(1,99) AS y(id); --generate subnodes INSERT INTO ad_treenode (ad_tree_id, node_id, ad_client_id, ad_org_id, isactive, created, createdby, updated, updatedby, parent_id, seqno) SELECT 50, 2000000 + y.id + x.id*100, 0, 0, 'Y', now(), 0, now(), 0, 2000000 + x.id, 999 FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id); --generate subsubnodes INSERT INTO ad_treenode (ad_tree_id, node_id, ad_client_id, ad_org_id, isactive, created, createdby, updated, updatedby, parent_id, seqno) SELECT 50, 2000000 + y.id + x.id*100 + z.id*10000, 0, 0, 'Y', now(), 0, now(), 0, 2000000 + y.id + x.id *100 , 999 FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id), generate_series(1,99) AS z(id); --generate toplevel organizations INSERT INTO ad_org SELECT 2000000 + y.id , 0, 'Y', now(), 100, now(), 100, 2000000 + y.id, 2000000 + y.id, 2000000 + y.id, 'Y', NULL FROM generate_series(1,99) AS y(id); --generate suborganizations INSERT INTO ad_org SELECT 2000000 + y.id + x.id*100, 0, 'Y', now(), 100, now(), 100, 2000000 + y.id + x.id*100, 2000000 + y.id + x.id*100, 2000000 + y.id + x.id*100, 'Y', NULL FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id); --generate subsuborganizations INSERT INTO ad_org SELECT 2000000 + y.id + x.id*100 + z.id*10000, 0, 'Y', now(), 100, now(), 100, 2000000 + y.id + x.id*100 + z.id*10000, 2000000 + y.id + x.id*100 + z.id*10000, 2000000 + y.id + x.id*100 + z.id*10000, 'Y', NULL FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id), generate_series(1,99) AS z(id); --generate toplevel orginfo INSERT INTO ad_orginfo (ad_org_id, ad_client_id, isactive, created, createdby, updated, updatedby, duns, taxid) SELECT 2000000 + y.id, 0, 'Y', now(), 100, now(), 100, 0, 0 FROM generate_series(1,99) AS y(id); --generate suborginfo INSERT INTO ad_orginfo (ad_org_id, ad_client_id, isactive, created, createdby, updated, updatedby, duns, taxid) SELECT 2000000 + y.id + x.id*100, 0, 'Y', now(), 100, now(), 100, 0, 0 FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id); --generate subsuborginfo INSERT INTO ad_orginfo (ad_org_id, ad_client_id, isactive, created, createdby, updated, updatedby, duns, taxid) SELECT 2000000 + y.id + x.id*100 + z.id*10000, 0, 'Y', now(), 100, now(), 100, 0, 0 FROM generate_series(1,99) AS x(id), generate_series(1,99) AS y(id), generate_series(1,99) AS z(id);
If you want to delete all the nodes/organizations later, just run:
delete from ad_treenode where node_id > 2000000; delete from ad_org where ad_org_id > 2000000;
Testcases
Task | Expectation | Result | Status |
---|---|---|---|
asd | asd | asd | asd |
Mo 08:23, 20. Dec. 2012 (CEST) | In Development | Second development iteration. Cleaner code, buxfixes, improvements, more stability. | |