+++ /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