inserted emacs nxml-mode in bottom of xml file
[yazpp-moved-to-github.git] / doc / zoom.xml
1 <chapter id="zoom">
2  <!-- $Id: zoom.xml,v 1.14 2006-04-24 12:47:34 marc Exp $ -->
3  <title>ZOOM-C++</title>
4
5
6  <sect1 id="zoom-introduction">
7   <title>Introduction</title>
8   <para>
9    <ulink url="http://zoom.z3950.org/">ZOOM</ulink>
10    is the emerging standard API for information retrieval programming
11    using the Z39.50 protocol.  ZOOM's
12    <ulink url="http://zoom.z3950.org/api/">Abstract API</ulink>
13    specifies semantics for classes representing key IR concepts such as
14    connections, queries, result sets and records; and there are various
15    <ulink url="http://zoom.z3950.org/bind/">bindings</ulink>
16    specifying how those concepts should be represented in various
17    programming languages.
18   </para>
19   <para>
20    The YAZ++ library includes an implementation of the <ulink
21    url="http://zoom.z3950.org/bind/cplusplus/"
22         >C++ binding</ulink>
23    for ZOOM, enabling quick, easy development of client applications.
24   </para>
25   <para>
26    For example, here is a tiny Z39.50 client that fetches and displays
27    the MARC record for Farlow &amp; Brett Surman's
28    <citetitle>The Complete Dinosaur</citetitle>
29    from the Library of Congress's Z39.50 server:
30   </para>
31   <programlisting>
32     #include &lt;iostream&gt;
33     #include &lt;yazpp/zoom.h&gt;
34
35     using namespace ZOOM;
36
37     int main(int argc, char **argv)
38     {
39         connection conn("z3950.loc.gov", 7090);
40         conn.option("databaseName", "Voyager");
41         conn.option("preferredRecordSyntax", "USMARC");
42         resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
43         const record *rec = rs.getRecord(0);
44         cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
45     }
46   </programlisting>
47    <note>
48     <para>
49      For the sake of simplicity, this program does not check
50      for errors: we show a more robust version of the same program
51      <link linkend="revised-sample">later</link>.)
52     </para>
53    </note>
54   <para>
55    YAZ++'s implementation of the C++ binding is a thin layer over YAZ's
56    implementation of the C binding.  For information on the supported
57    options and other such details, see the ZOOM-C documentation, which
58    can be found on-line at
59    <ulink url="http://www.indexdata.dk/yaz/doc/zoom.tkl"/>
60   </para>
61   <para>
62    All of the classes defined by ZOOM-C++ are in the
63    <literal>ZOOM</literal> namespace.  We will now consider
64    the five main classes in turn:
65
66    <itemizedlist>
67     <listitem>
68      <para>
69       <literal>connection</literal>
70      </para>
71     </listitem>
72
73     <listitem>
74      <para>
75       <literal>query</literal> and its subclasses
76       <literal>prefixQuery</literal> and
77       <literal>CCLQuery</literal>
78      </para>
79     </listitem>
80
81     <listitem>
82      <para>
83       <literal>resultSet</literal>
84      </para>
85     </listitem>
86
87     <listitem>
88      <para>
89       <literal>record</literal>
90      </para>
91     </listitem>
92
93     <listitem>
94      <para>
95       <literal>exception</literal> and its subclasses
96       <literal>systemException</literal>,
97       <literal>bib1Exception</literal> and
98       <literal>queryException</literal>
99      </para>
100     </listitem>
101    </itemizedlist>
102   </para>
103  </sect1>
104
105
106  <sect1 id="zoom-connection">
107   <title><literal>ZOOM::connection</literal></title>
108   <para>
109    A <literal>ZOOM::connection</literal> object represents an open
110    connection to a Z39.50 server.  Such a connection is forged by
111    constructing a <literal>connection</literal> object.
112   </para>
113   <para>
114    The class has this declaration:
115   </para>
116   <synopsis>
117     class connection {
118     public:
119       connection (const char *hostname, int portnum);
120       ~connection ();
121       const char *option (const char *key) const;
122       const char *option (const char *key, const char *val);
123     };
124   </synopsis>
125   <para>
126    When a new <literal>connection</literal> is created, the hostname
127    and port number of a Z39.50 server must be supplied, and the
128    network connection is forged and wrapped in the new object.  If the
129    connection can't be established - perhaps because the hostname
130    couldn't be resolved, or there is no server listening on the
131    specified port - then an
132    <link linkend="zoom-exception"><literal>exception</literal></link>
133    is thrown.
134   </para>
135   <para>
136    The only other methods on a <literal>connection</literal> object
137    are for getting and setting options.  Any name-value pair of
138    strings may be set as options, and subsequently retrieved, but
139    certain options have special meanings which are understood by the
140    ZOOM code and affect the behaviour of the object that carries
141    them.  For example, the value of the
142    <literal>databaseName</literal> option is used as the name of the
143    database to query when a search is executed against the
144    <literal>connection</literal>.  For a full list of such special
145    options, see the ZOOM abstract API and the ZOOM-C documentation
146    (links below).
147   </para>
148
149   <sect2>
150    <title>References</title>
151    <itemizedlist>
152     <listitem>
153      <para>
154       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.2"
155         >Section 3.2 (Connection) of the ZOOM Abstract API</ulink>
156      </para>
157     </listitem>
158     <listitem>
159      <para>
160       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.tkl#zoom.connections"
161         >The Connections section of the ZOOM-C documentation</ulink>
162      </para>
163     </listitem>
164    </itemizedlist>
165   </sect2>
166  </sect1>
167
168
169  <sect1 id="zoom-query">
170   <title><literal>ZOOM::query</literal> and subclasses</title>
171   <para>
172    The <literal>ZOOM::query</literal> class is a virtual base class,
173    representing a query to be submitted to a server.  This class has
174    no methods, but two (so far) concrete subclasses, each implementing
175    a specific query notation.
176   </para>
177
178   <sect2>
179    <title><literal>ZOOM::prefixQuery</literal></title>
180    <synopsis>
181     class prefixQuery : public query {
182     public:
183       prefixQuery (const char *pqn);
184       ~prefixQuery ();
185     };
186    </synopsis>
187    <para>
188     This class enables a query to be created by compiling YAZ's
189     cryptic but powerful
190     <ulink url="http://www.indexdata.dk/yaz/doc/tools.tkl#PQF"
191         >Prefix Query Notation (PQN)</ulink>.
192    </para>
193   </sect2>
194
195   <sect2>
196    <title><literal>ZOOM::CCLQuery</literal></title>
197    <synopsis>
198     class CCLQuery : public query {
199     public:
200       CCLQuery (const char *ccl, void *qualset);
201       ~CCLQuery ();
202     };
203    </synopsis>
204    <para>
205     This class enables a query to be created using the simpler but
206     less expressive
207     <ulink url="http://www.indexdata.dk/yaz/doc/tools.tkl#CCL"
208         >Common Command Language (CCL)</ulink>.
209     The qualifiers recognised by the CCL parser are specified in an
210     external configuration file in the format described by the YAZ
211     documentation.
212    </para>
213    <para>
214     If query construction fails for either type of
215     <literal>query</literal> object - typically because the query
216     string itself is not valid PQN or CCL - then an
217     <link linkend="zoom-exception"><literal>exception</literal></link>
218     is thrown.
219    </para>
220   </sect2>
221
222   <sect2>
223    <title>Discussion</title>
224    <para>
225     It will be readily recognised that these objects have no methods
226     other than their constructors: their only role in life is to be
227     used in searching, by being passed to the
228     <literal>resultSet</literal> class's constructor.
229    </para>
230    <para>
231     Given a suitable set of CCL qualifiers, the following pairs of
232     queries are equivalent:
233    </para>
234    <screen>
235     prefixQuery("dinosaur");
236     CCLQuery("dinosaur");
237
238     prefixQuery("@and complete dinosaur");
239     CCLQuery("complete and dinosaur");
240
241     prefixQuery("@and complete @or dinosaur pterosaur");
242     CCLQuery("complete and (dinosaur or pterosaur)");
243
244     prefixQuery("@attr 1=7 0253333490");
245     CCLQuery("isbn=0253333490");
246    </screen>
247   </sect2>
248
249   <sect2>
250    <title>References</title>
251    <itemizedlist>
252     <listitem>
253      <para>
254       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.3"
255         >Section 3.3 (Query) of the ZOOM Abstract API</ulink>
256      </para>
257     </listitem>
258     <listitem>
259      <para>
260       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.query.tkl"
261         >The Queries section of the ZOOM-C documentation</ulink>
262      </para>
263     </listitem>
264    </itemizedlist>
265   </sect2>
266  </sect1>
267
268
269  <sect1 id="zoom-resultset">
270   <title><literal>ZOOM::resultSet</literal></title>
271   <para>
272    A <literal>ZOOM::resultSet</literal> object represents a set of
273    records identified by a query that has been executed against a
274    particular connection.  The sole purpose of both
275    <literal>connection</literal> and <literal>query</literal> objects
276    is that they can be used to create new
277    <literal>resultSet</literal>s - that is, to perform a search on the
278    server on the remote end of the connection.
279   </para>
280   <para>
281    The class has this declaration:
282   </para>
283   <synopsis>
284     class resultSet {
285     public:
286       resultSet (connection &amp;c, const query &amp;q);
287       ~resultSet ();
288       const char *option (const char *key) const;
289       const char *option (const char *key, const char *val);
290       size_t size () const;
291       const record *getRecord (size_t i) const;
292     };
293   </synopsis>
294   <para>
295    New <literal>resultSet</literal>s are created by the constructor,
296    which is passed a <literal>connection</literal>, indicating the
297    server on which the search is to be performed, and a
298    <literal>query</literal>, indicating what search to perform.  If
299    the search fails - for example, because the query uses attributes
300    that the server doesn't implement - then an
301    <link linkend="zoom-exception"><literal>exception</literal></link>
302    is thrown.
303   </para>
304   <para>
305    Like <literal>connection</literal>s, <literal>resultSet</literal>
306    objects can carry name-value options.  The special options which
307    affect ZOOM-C++'s behaviour are the same as those for ZOOM-C and
308    are described in its documentation (link below).  In particular,
309    the <literal>preferredRecordSyntax</literal> option may be set to
310    a string such as ``USMARC'', ``SUTRS'' etc. to indicate what the
311    format in which records should be retrieved; and the
312    <literal>elementSetName</literal> option indicates whether brief
313    records (``B''), full records (``F'') or some other composition
314    should be used.
315   </para>
316   <para>
317    The <literal>size()</literal> method returns the number of records
318    in the result set.  Zero is a legitimate value: a search that finds
319    no records is not the same as a search that fails.
320   </para>
321   <para>
322    Finally, the <literal>getRecord</literal> method returns the
323    <parameter>i</parameter>th record from the result set, where
324    <parameter>i</parameter> is zero-based: that is, legitmate values
325    range from zero up to one less than the result-set size.  If the
326    method fails, for example because the requested record is out of
327    range, it <literal>throw</literal>s an
328    <link linkend="zoom-exception"><literal>exception</literal></link>.
329   </para>
330
331   <sect2>
332    <title>References</title>
333    <itemizedlist>
334     <listitem>
335      <para>
336       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.4"
337         >Section 3.4 (Result Set) of the ZOOM Abstract API</ulink>
338      </para>
339     </listitem>
340     <listitem>
341      <para>
342       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.resultsets.tkl"
343         >The Result Sets section of the ZOOM-C documentation</ulink>
344      </para>
345     </listitem>
346    </itemizedlist>
347   </sect2>
348  </sect1>
349
350
351  <sect1 id="zoom-record">
352   <title><literal>ZOOM::record</literal></title>
353   <para>
354    A <literal>ZOOM::record</literal> object represents a chunk of data
355    from a <literal>resultSet</literal> returned from a server.
356   </para>
357   <para>
358    The class has this declaration:
359   </para>
360   <synopsis>
361     class record {
362     public:
363       ~record ();
364       enum syntax {
365         UNKNOWN, GRS1, SUTRS, USMARC, UKMARC, XML
366       };
367       record *clone () const;
368       syntax recsyn () const;
369       const char *render () const;
370       const char *rawdata () const;
371     };
372   </synopsis>
373   <para>
374    Records returned from Z39.50 servers are encoded using a record
375    syntax: the various national MARC formats are commonly used for
376    bibliographic data, GRS-1 or XML for complex structured data, SUTRS
377    for simple human-readable text, etc.  The
378    <literal>record::syntax</literal> enumeration specifies constants
379    representing common record syntaxes, and the
380    <literal>recsyn()</literal> method returns the value corresponding
381    to the record-syntax of the record on which it is invoked.
382    <note>
383     <para>
384      Because this interface uses an enumeration, it is difficult to
385      extend to other record syntaxes - for example, DANMARC, the MARC
386      variant widely used in Denmark.  We might either grow the
387      enumeration substantially, or change the interface to return
388      either an integer or a string.
389     </para>
390    </note>
391   </para>
392   <para>
393    The simplest thing to do with a retrieved record is simply to
394    <literal>render()</literal> it.  This returns a human-readable, but
395    not necessarily very pretty, representation of the contents of the
396    record.  This is useful primarily for testing and debugging, since
397    the application has no control over how the record appears.
398    (The application must <emphasis>not</emphasis>
399    <literal>delete</literal> the returned string - it is ``owned'' by
400    the record object.)
401   </para>
402   <para>
403    More sophisticated applications will want to deal with the raw data
404    themselves: the <literal>rawdata()</literal> method returns it.
405    Its format will vary depending on the record syntax: SUTRS, MARC
406    and XML records are returned ``as is'', and GRS-1 records as a
407    pointer to their top-level node, which is a
408    <literal>Z_GenericRecord</literal> structure as defined in the
409    <literal>&lt;yaz/z-grs.h&gt;</literal> header file.
410    (The application must <emphasis>not</emphasis>
411    <literal>delete</literal> the returned data - it is ``owned'' by
412    the record object.)
413   </para>
414   <para>
415    Perceptive readers will notice that there are no methods for access
416    to individual fields within a record.  That's because the different
417    record syntaxes are so different that there is no even a uniform
418    notion of what a field is across them all, let alone a sensible way
419    to implement such a function.  Fetch the raw data instead, and pick
420    it apart ``by hand''.
421   </para>
422
423   <sect2>
424    <title>Memory Management</title>
425    <para>
426     The <literal>record</literal> objects returned from
427     <literal>resultSet::getRecord()</literal> are ``owned'' by the
428     result set object: that means that the application is not
429     responsible for <literal>delete</literal>ing them - each
430     <literal>record</literal> is automatically deallocated when the
431     <literal>resultSet</literal> that owns it is
432     <literal>delete</literal>d.
433    </para>
434    <para>
435     Usually that's what you want: it means that you can easily fetch a
436     record, use it and forget all about it, like this:
437    </para>
438    <programlisting>
439     resultSet rs(conn, query);
440     cout &lt;&lt; rs.getRecord(0)-&gt;render();
441    </programlisting>
442    <para>
443     But sometimes you want a <literal>record</literal> to live on past
444     the lifetime of the <literal>resultSet</literal> from which it was
445     fetched.  In this case, the <literal>clone(f)</literal> method can
446     be used to make an autonomous copy.  The application must
447     <literal>delete</literal> it when it doesn't need it any longer:
448    </para>
449    <programlisting>
450     record *rec;
451     {
452         resultSet rs(conn, query);
453         rec = rs.getRecord(0)-&gt;clone();
454         // `rs' goes out of scope here, and is deleted
455     }
456     cout &lt;&lt; rec-&gt;render();
457     delete rec;
458    </programlisting>
459   </sect2>
460
461   <sect2>
462    <title>References</title>
463    <itemizedlist>
464     <listitem>
465      <para>
466       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.5"
467         >Section 3.5 (Record) of the ZOOM Abstract API</ulink>
468      </para>
469     </listitem>
470     <listitem>
471      <para>
472       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.records.tkl"
473         >The Records section of the ZOOM-C documentation</ulink>
474      </para>
475     </listitem>
476    </itemizedlist>
477   </sect2>
478  </sect1>
479
480
481  <sect1 id="zoom-exception">
482   <title><literal>ZOOM::exception</literal> and subclasses</title>
483   <para>
484    The <literal>ZOOM::exception</literal> class is a virtual base
485    class, representing a diagnostic generated by the ZOOM-C++ library
486    or returned from a server.  Its subclasses represent particular
487    kinds of error.
488   </para>
489   <para>
490    When any of the ZOOM methods fail, they respond by
491    <literal>throw</literal>ing an object of type
492    <literal>exception</literal> or one of its subclasses.  This most
493    usually happens with the <literal>connection</literal> constructor,
494    the various query constructors, the <literal>resultSet</literal>
495    constructor (which is actually the searching method) and
496    <literal>resultSet::getRecord()</literal>.
497   </para>
498   <para>
499     The base class has this declaration:
500   </para>
501   <synopsis>
502     class exception {
503     public:
504       exception (int code);
505       int errcode () const;
506       const char *errmsg () const;
507     };
508   </synopsis>
509   <para>
510    It has three concrete subclasses:
511   </para>
512
513   <sect2>
514    <title><literal>ZOOM::systemException</literal></title>
515    <synopsis>
516     class systemException: public exception {
517     public:
518       systemException ();
519       int errcode () const;
520       const char *errmsg () const;
521     };
522    </synopsis>
523    <para>
524     Represents a ``system error'', typically indicating that a system
525     call failed - often in the low-level networking code that
526     underlies Z39.50.  <literal>errcode()</literal> returns the value
527     that the system variable <literal>errno</literal> had at the time
528     the exception was constructed; and <literal>errmsg()</literal>
529     returns a human-readable error-message corresponidng to that error
530     code.
531    </para>
532   </sect2>
533
534   <sect2>
535    <title><literal>ZOOM::bib1Exception</literal></title>
536    <synopsis>
537     class bib1Exception: public exception {
538     public:
539       bib1Exception (int errcode, const char *addinfo);
540       int errcode () const;
541       const char *errmsg () const;
542       const char *addinfo () const;
543     };
544    </synopsis>
545    <para>
546     Represents an error condition communicated by a Z39.50 server.
547     <literal>errcode()</literal> returns the BIB-1 diagnostic code of
548     the error, and <literal>errmsg()</literal> a human-readable error
549     message corresponding to that code.  <literal>addinfo()</literal>
550     returns any additional information associated with the error.
551    </para>
552    <para>
553     For example, if a ZOOM application tries to search in the
554     ``Voyager'' database of a server that does not have a database of
555     that name, a <literal>bib1Exception</literal> will be thrown in
556     which <literal>errcode()</literal> returns 109,
557     <literal>errmsg()</literal> returns the corresponding error
558     message ``Database unavailable'' and <literal>addinfo()</literal>
559     returns the name of the requested, but unavailable, database.
560    </para>
561   </sect2>
562
563   <sect2>
564    <title><literal>ZOOM::queryException</literal></title>
565    <synopsis>
566     class queryException: public exception {
567     public:
568       static const int PREFIX = 1;
569       static const int CCL = 2;
570       queryException (int qtype, const char *source);
571       int errcode () const;
572       const char *errmsg () const;
573       const char *addinfo () const;
574     };
575    </synopsis>
576    <para>
577     This class represents an error in parsing a query into a form that
578     a Z39.50 can understand.  It must be created with the
579     <literal>qtype</literal> parameter equal to one of the query-type
580     constants, which can be retrieved via the
581     <literal>errcode()</literal> method; <literal>errmsg()</literal>
582     returns an error-message specifying which kind of query was
583     malformed; and <literal>addinfo()</literal> returns a copy of the
584     query itself (that is, the value of <literal>source</literal> with
585     which the exception object was created.)
586    </para>
587   </sect2>
588
589   <sect2 id="revised-sample">
590    <title>Revised Sample Program</title>
591    <para>
592     Now we can revise the sample program from the
593     <link linkend="zoom-introduction">introduction</link>
594     to catch exceptions and report any errors:
595    </para>
596    <programlisting>
597     /* g++ -o zoom-c++-hw zoom-c++-hw.cpp -lzoompp -lyaz */
598
599     #include &lt;iostream&gt;
600     #include &lt;yazpp/zoom.h&gt;
601
602     using namespace ZOOM;
603
604     int main(int argc, char **argv)
605     {
606         try {
607             connection conn("z3950.loc.gov", 7090);
608             conn.option("databaseName", "Voyager");
609             conn.option("preferredRecordSyntax", "USMARC");
610             resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
611             const record *rec = rs.getRecord(0);
612             cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
613         } catch (systemException &amp;e) {
614             cerr &lt;&lt; "System error " &lt;&lt;
615                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
616         } catch (bib1Exception &amp;e) {
617             cerr &lt;&lt; "BIB-1 error " &lt;&lt; 
618                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
619         } catch (queryException &amp;e) {
620             cerr &lt;&lt; "Query error " &lt;&lt;
621                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
622         } catch (exception &amp;e) {
623             cerr &lt;&lt; "Error " &lt;&lt;
624                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
625         }
626     }
627    </programlisting>
628    <para>
629     The heart of this program is the same as in the original version,
630     but it's now wrapped in a <literal>try</literal> block followed by
631     several <literal>catch</literal> blocks which try to give helpful
632     diagnostics if something goes wrong.
633    </para>
634    <para>
635     The first such block diagnoses system-level errors such as memory
636     exhaustion or a network connection being broken by a server's
637     untimely death; the second catches errors at the Z39.50 level,
638     such as a server's report that it can't provide records in USMARC
639     syntax; the third is there in case there's something wrong with
640     the syntax of the query (although in this case it's correct); and
641     finally, the last <literal>catch</literal> block is a
642     belt-and-braces measure to be sure that nothing escapes us.
643    </para>
644   </sect2>
645
646   <sect2>
647    <title>References</title>
648    <itemizedlist>
649     <listitem>
650      <para>
651       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.7"
652         >Section 3.7 (Exception) of the ZOOM Abstract API</ulink>
653      </para>
654     </listitem>
655     <listitem>
656      <para>
657       <ulink url="http://lcweb.loc.gov/z3950/agency/defns/bib1diag.html"
658         >Bib-1 Diagnostics</ulink> on the
659       <ulink url="http://lcweb.loc.gov/z3950/agency/"
660         >Z39.50 Maintenance Agency</ulink> site.
661      </para>
662     </listitem>
663    </itemizedlist>
664    <para>
665     Because C does not support exceptions, ZOOM-C has no API element
666     that corresponds directly with ZOOM-C++'s
667     <literal>exception</literal> class and its subclasses.  The
668     closest thing is the <literal>ZOOM_connection_error</literal>
669     function described in
670     <ulink url="http://www.indexdata.dk/yaz/doc/zoom.tkl#zoom.connections"
671         >The Connections section</ulink> of the documentation.
672    </para>
673   </sect2>
674  </sect1>
675
676 </chapter>
677
678  <!-- Keep this Emacs mode comment at the end of the file
679 Local variables:
680 mode: nxml
681 End:
682 -->
683