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