Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

]project-open[ Data-Model “Categories”


Published on

This tutorial explains the purpose and use of “categories” in ]project-open[. Categories are the most widely used data-structure in the system. They are used to represent the status and type of business objects and as general classifiers.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

]project-open[ Data-Model “Categories”

  1. 1. ]po[ “Categories” The ]project-open[ Data-Model , Frank Bergmann, 2010-09-22 This tutorial explains the purpose and use of “categories” in ]project-open[. Categories are the most widely used data-structure in the system. They are used to represent the status and type of business objects and as general classifiers. In other words: Categories represent the values of most GUI drop-down boxes in the system.
  2. 2. GUI Example <ul><li>The screenshot at the right shows a typical drop-down box in ]project-open[. The values correspond to the lifecycle of a project from acquisition through operations to invoicing. </li></ul><ul><li>Hierarchical structure: Please note the sub-states of “Potential” and “Closed”. When searching for “Potential” projects, ]po[ will include projects in status “Inquiring”, “Qualifying” etc., </li></ul>(The list of Project States from / )
  3. 3. Problems Solved by “Categories” <ul><li>Nearly every business object in every business application requires a field for: </li></ul><ul><ul><li>Status – The lifecycle of the object: Typical values include “to be approved”, “finished”, “deleted” etc. </li></ul></ul><ul><ul><li>Type – The sub-type of the object </li></ul></ul><ul><li>Following best practices in database modeling, these fields are usually moved into a separate table. </li></ul><ul><li>As a result, 2 out of 3 tables in most data-models are such status or type tables. ]po[ includes 130 business objects, so we would need some 500 status and type tables. A lot of repetitive code needs to be written to maintain the values of these tables, while providing little business value. </li></ul><ul><li>Most status and type table are “flat”, while states and types in the real world are closer to trees or DAGs (= Directed Acyclic Graphs - trees with multiple inheritance). However, hierarchical queries on trees and DAGs are usually very slow and cumbersome. </li></ul><ul><li>]po[ provides a unified system to represent states and types </li></ul><ul><li>]po[ includes support for high-speed category queries </li></ul>(The list of Project States from / )
  4. 4. Solution: &quot;Categories&quot; <ul><li>Conventional/ traditional database design normally </li></ul><ul><li>includes “foreign key tables” defining the type and </li></ul><ul><li>status of an object. </li></ul><ul><li>] po [ takes a different approach in order to minimize </li></ul><ul><li>the number of database tables. We use a single </li></ul><ul><li>&quot;im_categories&quot; table for all types of states: </li></ul><ul><li>Pros: </li></ul><ul><li>The total number of DB tables is reduced to a third. </li></ul><ul><li>A single maintenance screen can deal with all categories. </li></ul><ul><li>Built-in features: </li></ul><ul><ul><li>Translation/Localization </li></ul></ul><ul><ul><li>Hierarchical categories </li></ul></ul><ul><ul><li>Common GUI widgets </li></ul></ul><ul><li>Referential Integrity is enforced </li></ul><ul><li>Cons: </li></ul><ul><li>In theory it is possible to assign the wrong Category to a field. However this has never happened in practice yet. </li></ul>object_type_id object_status_id object s object_type_id name object_type s description name description ... object_type_id object_status_id object s name description ... category_id name im_categories description category_type ] po [ DB-Design: All type and status information is stored in a single &quot;im_categories&quot; table. The &quot;Classical&quot; DB-Design: Every table has it's own tables for type, status and similar information. object_status_id name object_stat e s description
  5. 5. Hierarchical Categories <ul><li>To get closed to the real-world, categories can be hierarchical. </li></ul><ul><li>Every arrow in the diagram at the left corresponds to one entry in the im_category_hierarchy table. </li></ul>Potential “ Top” Open Closed Inquiring Qualifying Declined Delivered Quoting Quote Out Invoiced Deleted Canceled (The list of Project States from / ) Every arrow corresponds to one entry in the im_category_hierarchy table
  6. 6. Hierarchical Categories <ul><li>The table “im_category_hierarchy” contains the mapping between parents and children of the category hierarchy. </li></ul><ul><li>The SQL and the results on the right hand side show the values from the Project Status example. </li></ul>im_category_hierarchy object_type_id object_status_id object name description ... category_id name im_categories description category_type ] po [ DB-Design: The table im_category_hierarchy contains the is-parent-of relationship on categories. parent_category_id child_category_id SELECT parent_id, im_category_from_id(parent_id) as parent, hild_id, im_category_from_id(child_id) as child FROM im_category_hierarchy WHERE parent_id in ( select project_status_id from im_project_status ); parent_id | parent | child_id | child -----------+-----------+----------+------------ 81 | Closed | 83 | Canceled 81 | Closed | 77 | Declined 81 | Closed | 82 | Deleted 81 | Closed | 78 | Delivered 81 | Closed | 79 | Invoiced 71 | Potential | 72 | Inquiring 71 | Potential | 73 | Qualifying 71 | Potential | 75 | Quote Out 71 | Potential | 74 | Quoting
  7. 7. DAG Hierarchies (“Multiple Inheritance”) <ul><li>]project-open[ categories even allow for “multiple inheritance”. That is, one category can have more then one parent. </li></ul><ul><li>In the case of “Company Type” above, the “CustOrIntl” category (Customer or Internal) represents a super-category of both “Customer” and “Internal”. </li></ul><ul><li>Please note the additional arrows in the diagram. Each arrow represents an entry in the im_category_hierarchy table. </li></ul>Customer “ Top” Internal Provider Translation Customer Software Customer Freelance Provider Office Equipment Provider Law Company CustOrIntl Every arrow corresponds to one entry in the im_category_hierarchy table Every arrow corresponds to one entry in the im_category_hierarchy table SELECT parent_id, im_category_from_id(parent_id) as parent, child_id, im_category_from_id(child_id) as child FROM im_category_hierarchy WHERE parent_id in (select company_type_id from im_company_types) ORDER BY parent, child; parent_id | parent | child_id | child -----------+------------+----------+--------------------------- 57 | Customer | 10245 | IT Consultancy 57 | Customer | 10244 | Law Company 57 | Customer | 54 | MLV Translation Agency 57 | Customer | 55 | Software Company 10246 | CustOrIntl | 57 | Customer 10246 | CustOrIntl | 53 | Internal 10246 | CustOrIntl | 10245 | IT Consultancy 10246 | CustOrIntl | 10244 | Law Company 10246 | CustOrIntl | 54 | MLV Translation Agency 10246 | CustOrIntl | 55 | Software Company 56 | Provider | 58 | Freelance Provider 56 | Provider | 59 | Office Equipment Provider Every arrow corresponds to one entry in the im_category_hierarchy table Every arrow corresponds to one entry in the im_category_hierarchy table
  8. 8. Categories Shortcut Functions <ul><li>im_category_from_id(category_id): Returns the category name for the ID. This function is useful, because it saves an SQL join in the SQL clause. Otherwise SQL clauses could get quite complex. </li></ul><ul><li>im_sub_category(caregory_id): Returns the set of sub-categories. </li></ul><ul><li>im_category_new(category_id, category, category_type): Creates a new “category” of type “Category”. </li></ul>SELECT im_category_from_id( 71 ) ; im_category_from_id --------------------- Potential (1 row) select * from im_sub_categories(57); im_sub_categories ------------------- 54 55 57 10244 10245 select im_category_new( nextval('im_categories_seq')::integer, 'Test', 'Intranet Project Status‘ ); im_category_new ----------------- 0
  9. 9. SQL Queries with Hierarchical Categories <ul><li>Let’s assume we want to list all “Potential” projects. This listing allows us for example to calculate the value of the projects in the “pre-sales pipeline”. </li></ul><ul><li>We use the im_sub_categories() function in an “in” sub-query to return all project states below and including “potential”. </li></ul>SELECT p.project_nr, p.project_name, im_category_from_id(p.project_status_id) as status, im_category_from_id(p.project_type_id) as type, p.cost_quotes_cache FROM im_projects p WHERE p.project_status_id in (select * from im_sub_categories(71)); project_nr | project_name | status | type | cost_quotes_cache ------------+--------------------------+-----------+-------------+------------------ 2010_0036 | Motor Development | Potential | Development | 12000 .00 2010_0002 | Rollout ABC | Potential | Rollout | 50000 .00 2010_0007 | Resource Planning Detail | Quoting | Development | 6500 .00 (3 rows)
  10. 10. Categories as Constants <ul><li>Categories are used as constants in many parts of ]project-open[. </li></ul><ul><li>This is necessary because there is business logic that behaves differently for “potential” vs. “open” projects (for example, you can configure in ]po[ whether users are allowed to log hours on potential projects or not). </li></ul><ul><li>Constants are defined by convention in the head of the TCL libraries. </li></ul><ul><li>Usually, the top level of a category type is used as constants. </li></ul><ul><li>Changing these constants will break the business logic. However, the user can add sub-categories without problems. </li></ul>„ All“ Open Inquiring Qualifying Quoting Quote Out Declined Delivered Invoiced Constants, don't change! Freely Configurable Deleted Canceled Closed Potential
  11. 11. Categories SQL Examples “Cookbook” <ul><li>Select Everything About Categories </li></ul><ul><li>select </li></ul><ul><li>c.*, </li></ul><ul><li>im_category_from_id(aux_int1) as aux_int1_cat, </li></ul><ul><li>im_category_from_id(aux_int2) as aux_int2_cat, </li></ul><ul><li>h.parent_id, </li></ul><ul><li>im_category_from_id(h.parent_id) as parent </li></ul><ul><li>from </li></ul><ul><li>im_categories c </li></ul><ul><li>left outer join im_category_hierarchy h </li></ul><ul><li>on (c.category_id = h.child_id) </li></ul><ul><li>where </li></ul><ul><li>c.category_type = 'Intranet Cost Types' </li></ul><ul><li>order by </li></ul><ul><li>category_type, </li></ul><ul><li>category_id; </li></ul>Categories Definition -- We use categories as a universal storage for business -- object states and types, instead of a zillion of tables -- like 'im_project_status' and 'im_project_type'. create sequence im_categories_seq start 100000; create table im_categories ( category_id integer constraint im_categories_pk primary key, category varchar(50) not null, category_description varchar(4000), category_type varchar(50), category_gif varchar(100) default 'category', enabled_p char(1) default 't' constraint im_enabled_p_ck check(enabled_p in ('t','f')), -- used to indicate &quot;abstract&quot; -- super-categorys that are not -- valid values for objects. -- For example: &quot;Translation -- Project&quot; is not a project_type, -- but a class of project_types. parent_only_p char(1) default 'f' constraint im_parent_only_p_ck check(parent_only_p in ('t','f')) ); Category Hierarchy -- Optional system to put categories in a hierarchy. -- This table stores the &quot;transitive closure&quot; of the -- is-a relationship between categories in a kind of matrix. -- Let's asume: B isa A and C isa B. So we'll store -- the tupels (C,A), (C,B) and (B,A). -- This structure is a very fast structure for asking: -- -- &quot;is category A a subcategory of B?&quot; -- -- but requires n^2 storage space in the worst case and -- it's a mess retracting settings from the hierarchy. -- We won't have very deep hierarchies, so storage complexity -- is not going to be a problem. create table im_category_hierarchy ( parent_id integer constraint im_parent_category_fk references im_categories, child_id integer constraint im_child_category_fk references im_categories, constraint category_hierarchy_un unique (parent_id, child_id) ); Extract Categories Without Join -- A helper functions to make our queries easier to read create or replace function im_category_from_id (integer) returns varchar as ' DECLARE p_category_id alias for $1; v_category varchar(50); BEGIN select category into v_category from im_categories where category_id = p_category_id; return v_category; end;' language 'plpgsql'; -- Example: -- select im_category_from_id(48); Create a New Category Entry insert into im_categories ( category_id, category, category_type, category_description, enabled_p, aux_int1, aux_int2, aux_string1, aux_string2 ) values ( :category_id, :category, :category_type, :category_description, :enabled_p, :aux_int1, :aux_int2, :aux_string1, :aux_string2 );
  12. 12. Frank Bergmann [email_address]