--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<chapter xml:id="data_models_and_access" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
+ xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
+ <chapterinfo>\r
+ <title>Evergreen Data Models and Access</title>\r
+ </chapterinfo>\r
+ <abstract id="DM_abstract">\r
+ <simpara>This chapter was taken from Dan Scott's <emphasis>Developer Workshop</emphasis>, February 2010.</simpara>\r
+ </abstract>\r
+ <section id="exploring_database_schema">\r
+ <title>Exploring the Database Schema</title>\r
+ <simpara>The database schema is tied pretty tightly to PostgreSQL. Although PostgreSQL<indexterm><primary>databases</primary><secondary>PostgreSQL</secondary></indexterm>\r
+ adheres closely to ANSI SQL standards, the use of schemas, SQL functions<indexterm><primary>ANSI</primary></indexterm>\r
+ implemented in both <systemitem>plpgsql</systemitem> and <systemitem>plperl</systemitem>, and PostgreSQL’s native full-text\r
+ search would make it… challenging… to port to other database platforms.</simpara>\r
+ <simpara>A few common PostgreSQL interfaces for poking around the schema and\r
+ manipulating data are:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ psql (the command line client)<indexterm><primary>databases</primary><secondary>PostgreSQL</secondary><tertiery>psql</tertiery></indexterm>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ pgadminIII (a GUI client).<indexterm><primary>databases</primary><secondary>PostgreSQL</secondary><tertiery>pgadminIII</tertiery></indexterm>\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ <simpara>Or you can read through the source files in <filename class="directoy">Open-ILS/src/sql/Pg</filename>.</simpara>\r
+ <simpara>Let’s take a quick tour through the schemas, pointing out some highlights\r
+ and some key interdependencies:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ actor.org_unit → asset.copy_location\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ actor.usr → actor.card\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ biblio.record_entry → asset.call_number → asset.copy\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ config.metabib_field → metabib.*_field_entry\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ This documentation also contains an Appendix for the Evergreen <xref linkend="databaseschema"/>. \r
+ </section>\r
+ <section id="_database_access_methods">\r
+ <title>Database access methods</title>\r
+ <simpara>You could use direct access to the database via Perl DBI, JDBC, etc,\r
+ but Evergreen offers several database CRUD services for\r
+ creating / retrieving / updating / deleting data. These avoid tying\r
+ you too tightly to the current database schema and they funnel database\r
+ access through the same mechanism, rather than tying up connections\r
+ with other interfaces.</simpara>\r
+ </section>\r
+ <section id="_evergreen_interface_definition_language_idl">\r
+ <title>Evergreen Interface Definition Language (IDL)</title>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary></indexterm>\r
+ <simpara>Defines properties and required permissions for Evergreen classes.\r
+ To reduce network overhead, a given object is identified via a\r
+ class-hint and serialized as a JSON array of properties (no named properties).</simpara>\r
+ <simpara>As of 1.6, fields will be serialized in the order in which they appear\r
+ in the IDL definition file, and the is_new / is_changed / is_deleted\r
+ properties are automatically added. This has greatly reduced the size of\r
+ the <literal>fm_IDL.xml</literal> file and makes DRY people happier :)</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ … oils_persist:readonly tells us, if true, that the data lives in the database, but is pulled from the SELECT statement defined in the <oils_persist:source_definition> \r
+ child element\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ <simplesect id="_idl_basic_example_config_language_map">\r
+ <title>IDL basic example (config.language_map)</title>\r
+<programlisting language="xml" linenumbering="unnumbered">\r
+<class id="clm" controller="open-ils.cstore open-ils.pcrud"\r
+ oils_obj:fieldmapper="config::language_map"\r
+ oils_persist:tablename="config.language_map"\r
+ reporter:label="Language Map" oils_persist:field_safe="true"> <co id="dmCO5-1"/> <co id="dmCO5-2"/> <co id="dmCO5-3"/> <co id="dmCO5-4"/>\r
+ <fields oils_persist:primary="code" oils_persist:sequence=""> <co id="dmCO5-5"/>\r
+ <field reporter:label="Language Code" name="code"\r
+ reporter:selector="value" reporter:datatype="text"/> <co id="dmCO5-6"/>\r
+ <field reporter:label="Language" name="value"\r
+ reporter:datatype="text" oils_persist:i18n="true"/> <co id="dmCO5-7"/>\r
+ </fields>\r
+ <links/>\r
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> <co id="dmCO5-8"/>\r
+ <actions>\r
+ <create global_required="true" permission="CREATE_MARC_CODE"> <co id="dmCO5-9"/>\r
+ <retrieve global_required="true"\r
+ permission="CREATE_MARC_CODE UPDATE_MARC_CODE DELETE_MARC_CODE">\r
+ <update global_required="true" permission="UPDATE_MARC_CODE">\r
+ <delete global_required="true" permission="DELETE_MARC_CODE">\r
+ </actions>\r
+ </permacrud>\r
+</class>\r
+</programlisting>\r
+ <calloutlist>\r
+ <callout arearefs="dmCO5-1">\r
+ <simpara>\r
+ The <literal>class</literal> element defines the attributes and permissions for classes,\r
+ and relationships between classes.\r
+ </simpara>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>class element</secondary></indexterm>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>id</literal> attribute on the <literal>class</literal> element defines the class hint that is\r
+ used everywhere in Evergreen.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>controller</literal> attribute defines the OpenSRF\r
+ services that provide access to the data for the class objects.\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </callout>\r
+ <callout arearefs="dmCO5-2">\r
+ <simpara>\r
+ The <literal>oils_obj::fieldmapper</literal> attribute defines the name of the class that\r
+ is generated by <literal>OpenILS::Utils::Fieldmapper</literal>.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO5-3">\r
+ <simpara>\r
+ The <literal>oils_persist:tablename</literal> attribute defines the name of the table\r
+ that contains the data for the class objects.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO5-4">\r
+ <simpara>\r
+ The reporter interface uses <literal>reporter:label</literal> attribute values in\r
+ the source list to provide meaningful class and attribute names. The\r
+ <literal>open-ils.fielder</literal> service generates a set of methods that provide direct\r
+ access to the classes for which <literal>oils_persist:field_safe</literal> is <literal>true</literal>. For\r
+ example,\r
+ </simpara>\r
+<screen>\r
+<userinput>\r
+srfsh# request open-ils.fielder open-ils.fielder.clm.atomic \\r
+{"query":{"code":{"=":"eng"}}}\r
+\r
+Received Data: [\r
+ {\r
+ "value":"English",\r
+ "code":"eng"\r
+ }\r
+]\r
+</userinput>\r
+</screen>\r
+ </callout>\r
+ <callout arearefs="dmCO5-5">\r
+ <simpara>\r
+ The <literal>fields</literal> element defines the list of fields for the class.\r
+ </simpara>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>fields element</secondary></indexterm>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>oils_persist:primary</literal> attribute defines the column that acts as\r
+ the primary key for the table.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>oils_persist:sequence</literal> attribute holds the name of the database\r
+ sequence.\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </callout>\r
+ <callout arearefs="dmCO5-6">\r
+ <simpara>\r
+ Each <literal>field</literal> element defines one property of the class.\r
+ </simpara>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>field element</secondary></indexterm>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>name</literal> attribute defines the getter/setter method name for the field.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>reporter:label</literal> attribute defines the attribute name as used in\r
+ the reporter interface.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>reporter:selector</literal> attribute defines the field used in the reporter\r
+ filter interface to provide a selectable list. This gives the user a more\r
+ meaningful access point than the raw numeric ID or abstract code.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>reporter:datatype</literal> attribute defines the type of data held by\r
+ this property for the purposes of the reporter.\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </callout>\r
+ <callout arearefs="dmCO5-7">\r
+ <simpara>\r
+ The <literal>oils_persist:i18n</literal> attribute, when <literal>true</literal>, means that\r
+ translated values for the field’s contents may be accessible in\r
+ different locales.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO5-8">\r
+ <simpara>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>permacrud element</secondary></indexterm>\r
+ The <literal>permacrud</literal> element defines the permissions (if any) required\r
+ to <emphasis role="strong">c</emphasis>reate, <emphasis role="strong">r</emphasis>etrieve, <emphasis role="strong">u</emphasis>pdate, \r
+ and <emphasis role="strong">d</emphasis>elete data for this\r
+ class. <literal>open-ils.permacrud</literal> must be defined as a controller for the class\r
+ for the permissions to be applied.\r
+ </simpara>\r
+ \r
+ </callout>\r
+ <callout arearefs="dmCO5-9">\r
+ <simpara>\r
+ Each action requires one or more <literal>permission</literal> values that the\r
+ user must possess to perform the action.\r
+ </simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ If the <literal>global_required</literal> attribute is <literal>true</literal>, then the user must\r
+ have been granted that permission globally (depth = 0) to perform\r
+ the action.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ The <literal>context_field</literal> attribute denotes the <literal><field></literal> that identifies\r
+ the org_unit at which the user must have the pertinent permission.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>action element</secondary></indexterm>\r
+ <simpara>\r
+ An action element may contain a <literal><context_field></literal> element that\r
+ defines the linked class (identified by the <literal>link</literal> attribute) and\r
+ the field in the linked class that identifies the org_unit where\r
+ the permission must be held.\r
+ </simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <indexterm><primary>Evergreen Interface Definition Language (IDL)</primary><secondary>context_field element</secondary></indexterm>\r
+ <simpara>\r
+ If the <literal><context_field></literal> element contains a <literal>jump</literal> attribute,\r
+ then it defines a link to a link to a class with a field identifying\r
+ the org_unit where the permission must be held.\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </callout>\r
+ </calloutlist>\r
+ </simplesect>\r
+ <simplesect id="_reporter_data_types_and_their_possible_values">\r
+ <title>Reporter data types and their possible values</title>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>bool</literal>: Boolean <literal>true</literal> or <literal>false</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>id</literal>: ID of the row in the database\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>int</literal>: integer value\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>interval</literal>: PostgreSQL time interval\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>link</literal>: link to another class, as defined in the <literal><links></literal>\r
+ element of the class definition\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>money</literal>: currency amount\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>org_unit</literal>: list of org_units\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>text</literal>: text value\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>timestamp</literal>: PostgreSQL timestamp\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </simplesect>\r
+ <simplesect id="_idl_example_with_linked_fields_actor_workstation">\r
+ <title>IDL example with linked fields (actor.workstation)</title>\r
+ <simpara>Just as tables often include columns with foreign keys that point\r
+ to values stored in the column of a different table, IDL classes\r
+ can contain fields that link to fields in other classes. The <literal><links></literal>\r
+ element defines which fields link to fields in other classes, and\r
+ the nature of the relationship:</simpara>\r
+<programlisting language="xml" linenumbering="unnumbered">\r
+<class id="aws" controller="open-ils.cstore"\r
+ oils_obj:fieldmapper="actor::workstation"\r
+ oils_persist:tablename="actor.workstation"\r
+ reporter:label="Workstation">\r
+ <fields oils_persist:primary="id"\r
+ oils_persist:sequence="actor.workstation_id_seq">\r
+ <field reporter:label="Workstation ID" name="id"\r
+ reporter:datatype="id"/>\r
+ <field reporter:label="Workstation Name" name="name"\r
+ reporter:datatype="text"/>\r
+ <field reporter:label="Owning Library" name="owning_lib"\r
+ reporter:datatype="org_unit"/>\r
+ <field reporter:label="Circulations" name="circulations"\r
+ oils_persist:virtual="true" reporter:datatype="link"/> <co id="dmCO6-1"/>\r
+ </fields>\r
+ <links> <co id="dmCO6-2"/>\r
+ <link field="owning_lib" reltype="has_a" key="id"\r
+ map="" class="aou"/> <co id="dmCO6-3"/>\r
+ <link field="circulations" reltype="has_many" key="workstation"\r
+ map="" class="circ"/>\r
+ <link field="circulation_checkins" reltype="has_many"\r
+ key="checkin_workstation" map="" class="circ"/>\r
+ </links>\r
+</class>\r
+</programlisting>\r
+ <calloutlist>\r
+ <callout arearefs="dmCO6-1">\r
+ <simpara>\r
+ This field includes an <literal>oils_persist:virtual</literal> attribute with the value of\r
+ <literal>true</literal>, meaning that the linked class <literal>circ</literal> is a virtual class.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO6-2">\r
+ <simpara>\r
+ The <literal><links></literal> element contains 0 or more <literal><link></literal> elements.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO6-3">\r
+ <simpara>\r
+ Each <literal><link></literal> element defines the field (<literal>field</literal>) that links to a different\r
+ class (<literal>class</literal>), the relationship (<literal>rel_type</literal>) between this field and the target\r
+ field (<literal>key</literal>). If the field in this class links to a virtual class, the (<literal>map</literal>)\r
+ attribute defines the field in the target class that returns a list of matching\r
+ objects for each object in this class.\r
+ </simpara>\r
+ </callout>\r
+ </calloutlist>\r
+ </simplesect>\r
+ </section>\r
+ <section id="open_ils_cstore_literal_data_access_interfaces">\r
+ <title><literal>open-ils.cstore</literal> data access interfaces</title>\r
+ <indexterm><primary>cstore</primary></indexterm>\r
+ <simpara>For each class documented in the IDL, the <literal>open-ils.cstore</literal> service\r
+ automatically generates a set of data access methods, based on the\r
+ <literal>oils_persist:tablename</literal> class attribute.</simpara>\r
+ <simpara>For example, for the class hint <literal>clm</literal>, cstore generates the following\r
+ methods with the <literal>config.language_map</literal> qualifer:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.id_list {"code" { "like": "e%" } }</literal>\r
+ </simpara>\r
+ <simpara>Retrieves a list composed only of the IDs that match the query.</simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.retrieve "eng"</literal>\r
+ </simpara>\r
+ <simpara>Retrieves the object that matches a specific ID.</simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.search {"code" : "eng"}</literal>\r
+ </simpara>\r
+ <simpara>Retrieves a list of objects that match the query.</simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.create <_object_></literal>\r
+ </simpara>\r
+ <simpara>Creates a new object from the passed in object.</simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.update <_object_></literal>\r
+ </simpara>\r
+ <simpara>Updates the object that has been passed in.</simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.direct.config.language_map.delete "eng"</literal>\r
+ </simpara>\r
+ <simpara>Deletes the object that matches the query.</simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </section>\r
+ <section id="_open_ils_pcrud_data_access_interfaces">\r
+ <title>open-ils.pcrud data access interfaces</title>\r
+ <indexterm><primary>pcrud</primary></indexterm>\r
+ <simpara>For each class documented in the IDL, the <literal>open-ils.pcrud</literal> service\r
+ automatically generates a set of data access methods, based on the\r
+ <literal>oils_persist:tablename</literal> class attribute.</simpara>\r
+ <simpara>For example, for the class hint <literal>clm</literal>, <literal>open-ils.pcrud</literal> generates the following\r
+ methods that parallel the <literal>open-ils.cstore</literal> interface:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.id_list.clm <_authtoken_>, { "code": { "like": "e%" } }</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.retrieve.clm <_authtoken_>, "eng"</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.search.clm <_authtoken_>, { "code": "eng" }</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.create.clm <_authtoken_>, <_object_></literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.update.clm <_authtoken_>, <_object_></literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.delete.clm <_authtoken_>, "eng"</literal>\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ </section>\r
+ <section id="_transaction_and_savepoint_control">\r
+ <title>Transaction and savepoint control</title>\r
+ <simpara>Both <literal>open-ils.cstore</literal> and <literal>open-ils.pcrud</literal> enable you to control database transactions\r
+ to ensure that a set of operations either all succeed, or all fail,\r
+ atomically:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.transaction.begin</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.transaction.commit</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.transaction.rollback</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.transaction.begin</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.transaction.commit</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.transaction.rollback</literal>\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ <simpara>At a more granular level, <literal>open-ils.cstore</literal> and <literal>open-ils.pcrud</literal> enable you to set database\r
+ savepoints to ensure that a set of operations either all succeed, or all\r
+ fail, atomically, within a given transaction:</simpara>\r
+ <itemizedlist>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.savepoint.begin</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.savepoint.commit</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.cstore.savepoint.rollback</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.savepoint.begin</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.savepoint.commit</literal>\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ <literal>open-ils.pcrud.savepoint.rollback</literal>\r
+ </simpara>\r
+ </listitem>\r
+ </itemizedlist>\r
+ <simpara>Transactions and savepoints must be performed within a stateful\r
+ connection to the <literal>open-ils.cstore</literal> and <literal>open-ils.pcrud</literal> services.\r
+ In <literal>srfsh</literal>, you can open a stateful connection using the <literal>open</literal>\r
+ command, and then close the stateful connection using the <literal>close</literal>\r
+ command - for example:</simpara>\r
+ <screen>srfsh# open open-ils.cstore\r
+ ... perform various transaction-related work\r
+ srfsh# close open-ils.cstore</screen>\r
+ <simplesect id="_json_queries">\r
+ <title>JSON Queries</title>\r
+ <indexterm><primary>JSON</primary></indexterm>\r
+ <simpara>Beyond simply retrieving objects by their ID using the <literal>\*.retrieve</literal>\r
+ methods, you can issue queries against the <literal>\*.delete</literal> and <literal>\*.search</literal>\r
+ methods using JSON to filter results with simple or complex search\r
+ conditions.</simpara>\r
+ <simpara>For example, to generate a list of barcodes that are held in a\r
+ copy location that allows holds and is visible in the OPAC:</simpara>\r
+<programlisting language="sh" linenumbering="unnumbered">\r
+srfsh# request open-ils.cstore open-ils.cstore.json_query <co id="dmCO7-1"/>\r
+ {"select": {"acp":["barcode"], "acpl":["name"]}, <co id="dmCO7-2"/>\r
+ "from": {"acp":"acpl"}, <co id="dmCO7-3"/>\r
+ "where": [ <co id="dmCO7-4"/>\r
+ {"+acpl": "holdable"}, <co id="dmCO7-5"/>\r
+ {"+acpl": "opac_visible"} <co id="dmCO7-6"/>\r
+ ]}\r
+\r
+Received Data: {\r
+ "barcode":"BARCODE1",\r
+ "name":"Stacks"\r
+}\r
+\r
+Received Data: {\r
+ "barcode":"BARCODE2",\r
+ "name":"Stacks"\r
+}\r
+</programlisting>\r
+ <calloutlist>\r
+ <callout arearefs="dmCO7-1">\r
+ <simpara>\r
+ Invoke the <literal>json_query</literal> service.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO7-2">\r
+ <simpara>\r
+ Select the <literal>barcode</literal> field from the <literal>acp</literal> class and the <literal>name</literal>\r
+ field from the <literal>acpl</literal> class.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO7-3">\r
+ <simpara>\r
+ Join the <literal>acp</literal> class to the <literal>acpl</literal> class based on the linked field\r
+ defined in the IDL.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO7-4">\r
+ <simpara>\r
+ Add a <literal>where</literal> clause to filter the results. We have more than one\r
+ condition beginning with the same key, so we wrap the conditions inside\r
+ an array.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO7-5">\r
+ <simpara>\r
+ The first condition tests whether the boolean value of the <literal>holdable</literal>\r
+ field on the <literal>acpl</literal> class is true.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO7-6">\r
+ <simpara>\r
+ The second condition tests whether the boolean value of the\r
+ <literal>opac_visible</literal> field on the <literal>acpl</literal> class is true.\r
+ </simpara>\r
+ </callout>\r
+ </calloutlist>\r
+ <simpara>For thorough coverage of the breadth of support offered by JSON\r
+ query syntax, see <ulink url="http://open-ils.org/dokuwiki/doku.php?id=documentation:technical:jsontutorial">JSON Queries: A Tutorial</ulink>.</simpara>\r
+ </simplesect>\r
+ <simplesect id="_fleshing_linked_objects">\r
+ <title>Fleshing linked objects</title>\r
+ <simpara>A simplistic approach to retrieving a set of objects that are linked to\r
+ an object that you are retrieving - for example, a set of call numbers\r
+ linked to the barcodes that a given user has borrowed - would be to:\r
+ 1. Retrieve the list of circulation objects (<literal>circ</literal> class)\r
+ for a given user (<literal>usr</literal> class).\r
+ 2. For each circulation object, look up the target copy (<literal>target_copy</literal>\r
+ field, linked to the <literal>acp</literal> class).\r
+ 3. For each copy, look up the call number for that copy (<literal>call_number</literal>\r
+ field, linked to the <literal>acn</literal> class).</simpara>\r
+ <simpara>However, this would result in potentially hundreds of round-trip\r
+ queries from the client to the server. Even with low-latency connections,\r
+ the network overhead would be considerable. So, built into the <literal>open-ils.cstore</literal> and\r
+ <literal>open-ils.pcrud</literal> access methods is the ability to <emphasis>flesh</emphasis> linked fields -\r
+ that is, rather than return an identifier to a given linked field,\r
+ the method can return the entire object as part of the initial response.</simpara>\r
+ <simpara>Most of the interfaces that return class instances from the IDL offer the\r
+ ability to flesh returned fields. For example, the\r
+ <literal>open-ils.cstore.direct.\*.retrieve</literal> methods allow you to specify a\r
+ JSON structure defining the fields you wish to flesh in the returned object.</simpara>\r
+ <formalpara><title>Fleshing fields in objects returned by <literal>open-ils.cstore</literal></title><para>\r
+<programlisting language="sh" linenumbering="unnumbered">\r
+srfsh# request open-ils.cstore open-ils.cstore.direct.asset.copy.retrieve 1, \\r
+ {\r
+ "flesh": 1, <co id="dmCO8-1"/>\r
+ "flesh_fields": { <co id="dmCO8-2"/>\r
+ "acp": ["location"]\r
+ }\r
+ }\r
+</programlisting>\r
+ </para></formalpara>\r
+ <calloutlist>\r
+ <callout arearefs="dmCO8-1">\r
+ <simpara>\r
+ The <literal>flesh</literal> argument is the depth at which objects should be fleshed.\r
+ For example, to flesh out a field that links to another object that includes\r
+ a field that links to another object, you would specify a depth of 2.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="dmCO8-2">\r
+ <simpara>\r
+ The <literal>flesh_fields</literal> argument contains a list of objects with the fields\r
+ to flesh for each object.\r
+ </simpara>\r
+ </callout>\r
+ </calloutlist>\r
+ <simpara>Let’s flesh things a little deeper. In addition to the copy location,\r
+ let’s also flesh the call number attached to the copy, and then flesh\r
+ the bibliographic record attached to the call number.</simpara>\r
+ <formalpara><title>Fleshing fields in fields of objects returned by <literal>open-ils.cstore</literal></title><para>\r
+<programlisting language="java" linenumbering="unnumbered">\r
+request open-ils.cstore open-ils.cstore.direct.asset.copy.retrieve 1, \\r
+ {\r
+ "flesh": 2,\r
+ "flesh_fields": {\r
+ "acp": ["location", "call_number"],\r
+ "acn": ["record"]\r
+ }\r
+ }\r
+</programlisting>\r
+ </para></formalpara>\r
+ </simplesect>\r
+ </section>\r
+ <section id="_adding_an_idl_entry_for_resolverresolver">\r
+ <title>Adding an IDL entry for ResolverResolver</title>\r
+ <simpara>Most OpenSRF methods in Evergreen define their object interface in the\r
+ IDL. Without an entry in the IDL, the prospective caller of a given\r
+ method is forced to either call the method and inspect the returned\r
+ contents, or read the source to work out the structure of the JSON\r
+ payload. At this stage of the tutorial, we have not defined an entry\r
+ in the IDL to represent the object returned by the\r
+ <literal>open-ils.resolver.resolve_holdings</literal> method. It is time to complete\r
+ that task.</simpara>\r
+ <simpara>The <literal>open-ils.resolver</literal> service is unlike many of the other classes\r
+ defined in the IDL because its data is not stored in the Evergreen\r
+ database. Instead, the data is requested from an external Web service\r
+ and only temporarily cached in <literal>memcached</literal>. Fortunately, the IDL\r
+ enables us to represent this kind of class by setting the\r
+ <literal>oils_persist:virtual</literal> class attribute to <literal>true</literal>.</simpara>\r
+ <simpara>So, let’s add an entry to the IDL for the <literal>open-ils.resolver.resolve_holdings</literal>\r
+ service:</simpara>\r
+ <programlisting language="xml" linenumbering="unnumbered"></programlisting>\r
+ <simpara>And let’s make <literal>ResolverResolver.pm</literal> return an array composed of our new\r
+ <literal>rhr</literal> classes rather than raw JSON objects:</simpara>\r
+ <programlisting language="perl" linenumbering="unnumbered"></programlisting>\r
+ <simpara>Once we add the new entry to the IDL and copy the revised <literal>ResolverResolver.pm</literal>\r
+ Perl module to <literal>/openils/lib/perl5/OpenILS/Application/</literal>, we need to:</simpara>\r
+ <orderedlist numeration="arabic">\r
+ <listitem>\r
+ <simpara>\r
+ Copy the updated IDL to both the <literal>/openils/conf/</literal> and\r
+ <literal>/openils/var/web/reports/</literal> directories. The Dojo approach to\r
+ parsing the IDL uses the IDL stored in the reports directory.\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ Restart the Perl services to make the new IDL visible to the services\r
+ and refresh the <literal>open-ils.resolver</literal> implementation\r
+ </simpara>\r
+ </listitem>\r
+ <listitem>\r
+ <simpara>\r
+ Rerun <filename>/openils/bin/autogen.sh</filename> to regenerate the JavaScript versions<indexterm><primary>autogen</primary></indexterm>\r
+ of the IDL required by the HTTP translator and gateway.\r
+ </simpara>\r
+ </listitem>\r
+ </orderedlist>\r
+ <simpara>We also need to adjust our JavaScript client to use the nifty new<indexterm><primary>JavaScript</primary></indexterm>\r
+ objects that <literal>open-ils.resolver.resolve_holdings</literal> now returns.\r
+ The best approach is to use the support in Evergreen’s Dojo extensions<indexterm><primary>Dojo toolkit</primary></indexterm>\r
+ to generate the JavaScript classes directly from the IDL XML file.</simpara>\r
+ <formalpara><title>Accessing classes defined in the IDL via Fieldmapper</title><para>\r
+ <programlisting language="html" linenumbering="unnumbered"></programlisting>\r
+ </para></formalpara>\r
+ <calloutlist>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ Load the Dojo core.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ <literal>fieldmapper.AutoIDL</literal> reads <filename>/openils/var/reports/fm_IDL.xml</filename> to\r
+ generate a list of class properties.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ <literal>fieldmapper.dojoData</literal> seems to provide a store for Evergreen data\r
+ accessed via Dojo.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ <literal>fieldmapper.Fieldmapper</literal> converts the list of class properties into\r
+ actual classes.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ <literal>fieldmapper.standardRequest</literal> invokes an OpenSRF method and returns\r
+ an array of objects.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ The first argument to <literal>fieldmapper.standardRequest</literal> is an array\r
+ containing the OpenSRF service name and method name.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ The second argument to <literal>fieldmapper.standardRequest</literal> is an array\r
+ containing the arguments to pass to the OpenSRF method.\r
+ </simpara>\r
+ </callout>\r
+ <callout arearefs="">\r
+ <simpara>\r
+ As Fieldmapper has instantiated the returned objects based on their\r
+ class hints, we can invoke getter/setter methods on the objects.\r
+ </simpara>\r
+ </callout>\r
+ </calloutlist>\r
+ </section>\r
+ \r
+</chapter>\r