# ZOOM

&zoom; is an acronym for 'Z39.50 Object-Orientation Model' and is an initiative started by Mike Taylor (Mike is from the UK, which explains the peculiar name of the model). The goal of &zoom; is to provide a common Z39.50 client API not bound to a particular programming language or toolkit.

A recent addition to &yaz; is SRW support. You can now make SRW ZOOM connections by specifying scheme `http://` for the hostname for a connection.

The lack of a simple Z39.50 client API for &yaz; has become more and more apparent over time. So when the first &zoom; specification became available, an implementation for &yaz; was quickly developed. For the first time, it is now as easy (or easier!) to develop clients than servers with &yaz;. This chapter describes the &zoom; C binding. Before going further, please reconsider whether C is the right programming language for the job. There are other language bindings available for &yaz;, and still more are in active development. See the ZOOM web-site for more information.

In order to fully understand this chapter you should read and try the example programs `zoomtst1.c`, `zoomtst2.c`, .. in the `zoom` directory.

The C language misses features found in object oriented languages such as C++, Java, etc. For example, you'll have to manually, destroy all objects you create, even though you may think of them as temporary. Most objects has a `_create` - and a `_destroy` variant. All objects are in fact pointers to internal stuff, but you don't see that because of typedefs. All destroy methods should gracefully ignore a `NULL` pointer.

In each of the sections below you'll find a sub section called protocol behavior, that describes how the API maps to the Z39.50 protocol.

## Connections

The Connection object is a session with a target.

        #include <yaz/zoom.h>

        ZOOM_connection ZOOM_connection_new (const char *host, int portnum);

        ZOOM_connection ZOOM_connection_create (ZOOM_options options);

        void ZOOM_connection_connect(ZOOM_connection c, const char *host,
                                     int portnum);
        void ZOOM_connection_destroy (ZOOM_connection c);

Connection objects are created with either function `ZOOM_connection_new` or `ZOOM_connection_create`. The former creates and automatically attempts to establish a network connection with the target. The latter doesn't establish a connection immediately, thus allowing you to specify options before establishing network connection using the function `ZOOM_connection_connect`. If the port number, `portnum`, is zero, the `host` is consulted for a port specification. If no port is given, 210 is used. A colon denotes the beginning of a port number in the host string. If the host string includes a slash, the following part specifies a database for the connection.

You can prefix the host with a scheme followed by colon. The default scheme is `tcp` (Z39.50 protocol). The scheme `http` selects SRW over HTTP.

Connection objects should be destroyed using the function `ZOOM_connection_destroy`.

        void ZOOM_connection_option_set (ZOOM_connection c,
                                         const char *key,
                                         const char *val);

        const char *ZOOM_connection_option_get (ZOOM_connection c,
                                                const char *key);

The `ZOOM_connection_option_set` allows you to set an option given by `key` to the value `value` for the connection. Function `ZOOM_connection_option_get` returns the value for an option given by `key`.

### ZOOM Connection Options

Option | Description | Default
--- | --- | ---
implementationName | Name of Your client | none
user | Authentication user name | none
group | Authentication group name | none
pass | Authentication password | none
host | Target host. This setting is "read-only". It's automatically set internally when connecting to a target. | none
proxy | Proxy host | none
async | If true (1) the connection operates in asynchronous operation which means that all calls are non-blocking except `ZOOM_event`. | 0
maximumRecordSize | Maximum size of single record. | 1 MB
preferredMessageSize | Maximum size of multiple records. | 1 MB
lang | Language for negotiation. | none
charset | Character set for negotiation. | none
targetImplementationId | Implementation ID of target. | none
targetImplementationName | Implementation Name of target. | none
targetImplementationVersion | Implementation Version of target. | none
databaseName | One or more database names separated by character plus (`+`), which to be used by subsequent search requests on this Connection. | Default

If either option `lang` or `charset` is set, then Character Set and Language Negotiation is in effect.

         int ZOOM_connection_error (ZOOM_connection c, const char **cp,
                                    const char **addinfo);
         int ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
                                      const char **addinfo, const char **dset);

Function `ZOOM_connection_error` checks for errors for the last operation(s) performed. The function returns zero if no errors occurred; non-zero otherwise indicating the error. Pointers `cp` and `addinfo` holds messages for the error and additional-info if passed as non-`NULL`. Function `ZOOM_connection_error_x` is an extended version of `ZOOM_connection_error` that is capable of returning name of diagnostic set in `dset`.

### Z39.50 Protocol behavior

The calls `ZOOM_connection_new` and `ZOOM_connection_connect` establishes a TCP/IP connection and sends an Initialize Request to the target if possible. In addition, the calls waits for an Initialize Response from the target and the result is inspected (OK or rejected).

If `proxy` is set then the client will establish a TCP/IP connection with the peer as specified by the `proxy` host and the hostname as part of the connect calls will be set as part of the Initialize Request. The proxy server will then "forward" the PDU's transparently to the target behind the proxy.

For the authentication parameters, if option `user` is set and both options `group` and `pass` are unset, then Open style authentication is used (Version 2/3) in which case the username is usually followed by a slash, then by a password. If either `group` or `pass` is set then idPass authentication (Version 3 only) is used. If none of the options are set, no authentication parameters are set as part of the Initialize Request (obviously).

When option `async` is 1, it really means that all network operations are postponed (and queued) until the function `ZOOM_event` is invoked. When doing so it doesn't make sense to check for errors after `ZOOM_connection_new` is called since that operation "connecting - and init" is still incomplete and the API cannot tell the outcome (yet).

### SRW Protocol behavior

The SRW protocol doesn't feature an Inititialize Request, so the connection phase merely establishes a TCP/IP connection with the SOAP service.

Most of the ZOOM connection options do not affect SRW and they are ignored. However, future versions of &yaz; might honor `implementationName` and put that as part of User-Agent header for HTTP requests.

The `charset` is used in the Content-Type header of HTTP requests.

## Queries

Query objects represents queries.

         ZOOM_query ZOOM_query_create(void);

         void ZOOM_query_destroy(ZOOM_query q);

         int ZOOM_query_prefix(ZOOM_query q, const char *str);

         int ZOOM_query_cql(ZOOM_query s, const char *str);

         int ZOOM_query_sortby(ZOOM_query q, const char *criteria);

Create query objects using `ZOOM_query_create` and destroy them by calling `ZOOM_query_destroy`. RPN-queries can be specified in PQF notation by using the function `ZOOM_query_prefix`. The `ZOOM_query_cql` specifies a CQL query to be sent to the server/target. More query types will be added in future versions of &yaz;, such as CCL to RPN-mapping, native CCL query, etc. In addition to a search, a sort criteria may be set. Function `ZOOM_query_sortby` specifies a sort criteria using the same string notation for sort as offered by the YAZ client.

### Protocol behavior

The query object is just an interface for the member Query in the SearchRequest. The sortby-function is an interface to the sortSequence member of the SortRequest.

## Result sets

The result set object is a container for records returned from a target.

         ZOOM_resultset ZOOM_connection_search(ZOOM_connection,
                                               ZOOM_query q);

         ZOOM_resultset ZOOM_connection_search_pqf(ZOOM_connection c,
                                                   const char *q);

         void ZOOM_resultset_destroy(ZOOM_resultset r);

Function `ZOOM_connection_search` creates a result set given a connection and query. Destroy a result set by calling `ZOOM_resultset_destroy`. Simple clients may using PQF only may use function `ZOOM_connection_search_pqf` in which case creating query objects is not necessary.

         void ZOOM_resultset_option_set (ZOOM_resultset r,
                                          const char *key,
                                          const char *val);

         const char *ZOOM_resultset_option_get (ZOOM_resultset r,
                                                 const char *key);

         size_t ZOOM_resultset_size (ZOOM_resultset r);

Functions `ZOOM_resultset_options_set` and `ZOOM_resultset_get` sets and gets an option for a result set similar to `ZOOM_connection_option_get` and `ZOOM_connection_option_set`.

The number of hits also called result-count is returned by function `ZOOM_resultset_size`.

### ZOOM Result set Options

Option | Description | Default
--- | --- | ---
piggyback | True (1) if piggyback should be used in searches; false (0) if not. | 1
start | Offset of first record to be retrieved from target. First record has offset 0 unlike the protocol specifications where first record has position 1. | 0
count | Number of records to be retrieved. | 0
step | Number of records to be retrieved in one chunk. The value, 0 means unchunked. | 0
elementSetName | Element-Set name of records. Most targets should honor element set name `B` and `F` for brief and full respectively. | none
preferredRecordSyntax | Preferred Syntax, such as `USMARC`, `SUTRS`, etc. | none
schema | Schema for retrieval, such as `Gils-schema`, `Geo-schema`, etc. | none
smallSetUpperBound | If hits is less than or equal to this value, then target will return all records using small element set name | 0
largeSetLowerBound | If hits is greater than this value, the target will return no records. | 1
mediumSetPresentNumber | This value represents the number of records to be returned as part of a search when when hits is less than or equal to large set lower bound and if hits is greater than small set upper bound. | 0
smallSetElementSetName | The element set name to be used for small result sets. | none
mediumSetElementSetName | The element set name to be for medium-sized result sets. | none
setname | Name of Result Set (Result Set ID). If this option isn't set, the ZOOM module will automatically allocate a result set name. | default

### Z39.50 Protocol behavior

The creation of a result set involves at least a SearchRequest - SearchResponse protocol handshake. Following that, if a sort criteria was specified as part of the query, a SortRequest - SortResponse handshake takes place. Note that it is necessary to perform sorting before any retrieval takes place, so no records will be returned from the target as part of the SearchResponse because these would be unsorted. Hence, piggyback is disabled when sort criteria is set. Following Search - and a Possible sort, Retrieval takes place - as one or more Present Requests - Present Response being transferred.

The API allows for two different modes for retrieval. A high level mode which is somewhat more powerful and a low level one. The low level is "enabled" when the settings `smallSetUpperBound`, `mediumSetPresentNumber` and `largeSetLowerBound` are set. The low level mode thus allows you to precisely set how records are returned as part of a search response as offered by the Z39.50 protocol. Since the client may be retrieving records as part of the search response, this mode doesn't work well if sorting is used.

The high-level mode allows you to fetch a range of records from the result set with a given start offset. When you use this mode the client will automatically use piggyback if that is possible with the target and perform one or more present requests as needed. Even if the target returns fewer records as part of a present response because of a record size limit, etc. the client will repeat sending present requests. As an example, if option `start` is 0 (default) and `count` is 4, and `piggyback` is 1 (default) and no sorting criteria is specified, then the client will attempt to retrieve the 4 records as part the search response (using piggyback). On the other hand, if either `start` is positive or if a sorting criteria is set, or if `piggyback` is 0, then the client will not perform piggyback but send Present Requests instead.

If either of the options `mediumSetElementSetName` and `smallSetElementSetName` are unset, the value of option `elementSetName` is used for piggyback searches. This means that for the high-level mode you only have to specify one elementSetName option rather than three.

### SRW Protocol behavior

Current version of &yaz; does not take advantage of a result set id returned by the SRW server. Future versions might do, however. Since, the ZOOM driver does not save result set IDs any present (retrieval) is transformed to a SRW SearchRetrieveRequest with same query but, possibly, different offsets.

Option `schema` specifies SRW schema for retrieval. However, options `elementSetName` and `preferredRecordSyntax` are ignored.

Options `start` and `count` are supported by SRW. The remaining options `piggyback`, `smallSetUpperBound`, `largeSetLowerBound`, `mediumSetPresentNumber`, `mediumSetElementSetName`, `smallSetElementSetName` are unsupported.

SRW supports CQL queries, not PQF. If PQF is used, however, the PQF query is transferred anyway using non-standard element `pQuery` in SRW SearchRetrieveRequest.

Unfortunately, SRW does not define a database setting. Hence, `databaseName` is unsupported and ignored. However, the path part in host parameter for functions `ZOOM_connecton_new` and `ZOOM_connection_connect` acts as a database (at least for the &yaz; SRW server).

## Records

A record object is a retrieval record on the client side - created from result sets.

         void ZOOM_resultset_records (ZOOM_resultset r,
                                      ZOOM_record *recs,
                                      size_t start, size_t count);
         ZOOM_record ZOOM_resultset_record (ZOOM_resultset s, size_t pos);

         const char *ZOOM_record_get (ZOOM_record rec, const char *type,
                                      size_t *len);

         ZOOM_record ZOOM_record_clone (ZOOM_record rec);

         void ZOOM_record_destroy (ZOOM_record rec);

References to temporary records are returned by functions `ZOOM_resultset_records` or `ZOOM_resultset_record`.

If a persistent reference to a record is desired `ZOOM_record_clone` should be used. It returns a record reference that should be destroyed by a call to `ZOOM_record_destroy`.

A single record is returned by function `ZOOM_resultset_record` that takes a position as argument. First record has position zero. If no record could be obtained `NULL` is returned.

Function `ZOOM_resultset_records` retrieves a number of records from a result set. Parameter `start` and `count` specifies the range of records to be returned. Upon completion array `recs[0], ..recs[count-1]` holds record objects for the records. The array of records `recs` should be allocated prior the call `ZOOM_resultset_records`. Note that for those records that couldn't be retrieved from the target `recs[ ..]` is set to `NULL`.

In order to extract information about a single record, `ZOOM_record_get` is provided. The function returns a pointer to certain record information. The nature (type) of the pointer depends on the parameter, `type`.

The `type` is a string of the format:

form[; charset=from[,to]]

where form specifies the format of the returned record, from specifies the character set of the record in its original form (as returned by the server), to specifies the output (returned) character set encoding. If charset is not given, then no character set conversion takes place. If to is omitted UTF-8 is assumed.

In addition, for certain types, the length `len` passed will be set to the size in bytes of the returned information.

The following are the supported values for form.

`database`

  Database of record is returned as a C null-terminated string. Return type `const char *`.

`syntax`

  The transfer syntax of the record is returned as a C null-terminated string containing the symbolic name of the record syntax, e.g. `Usmarc`. Return type is `const char *`.

`render`

  The record is returned in a display friendly format. Upon completion buffer is returned (type `const char *`) and length is stored in `*len`.

`raw`

  The record is returned in the internal YAZ specific format. For GRS-1, Explain, and others, the raw data is returned as type `Z_External *` which is just the type for the member `retrievalRecord` in type `NamePlusRecord`. For SUTRS and octet aligned record (including all MARCs) the octet buffer is returned and the length of the buffer.

`xml`

  The record is returned in XML if possible. SRW/SRU and Z39.50 records with transfer syntax XML are returned verbatim. MARC records are returned in MARCXML (converted from ISO2709 to MARCXML by YAZ). GRS-1 and OPAC records are not supported for this form. Upon completion, the XML buffer is returned (type `const char *`) and length is stored in `*len`.

`opac`

  OPAC for record is returned in XML.

Most MARC21 records uses the MARC-8 character set encoding. An application that wishes to display in Latin-1 would use

         render; charset=marc8,iso-8859-1

### Z39.50 Protocol behavior

The functions `ZOOM_resultset_record` and `ZOOM_resultset_records` inspects the client-side record cache. Records not found in cache are fetched using Present. The functions may block (and perform network I/O) - even though option `async` is 1, because they return records objects. (and there's no way to return records objects without retrieving them!).

There is a trick, however, in the usage of function `ZOOM_resultset_records` that allows for delayed retrieval (and makes it non-blocking). By using a null pointer for `recs` you're indicating you're not interested in getting records objects now.

### SRW Protocol behavior

The ZOOM driver for SRW treats records returned by a SRW server as if they where Z39.50 records with transfer syntax XML and no element set name or database name.

## Scan

This section describes an interface for Scan. Scan is not an official part of the ZOOM model yet. The result of a scan operation is the `ZOOM_scanset` which is a set of terms returned by a target.

The Scan interface is Z39.50 only. SRW version 1.0 does not support this.

        ZOOM_scanset ZOOM_connection_scan (ZOOM_connection c,
                                           const char *startterm);

        size_t ZOOM_scanset_size(ZOOM_scanset scan);

        const char * ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
                                       int *occ, size_t *len);

        void ZOOM_scanset_destroy (ZOOM_scanset scan);

        const char *ZOOM_scanset_option_get (ZOOM_scanset scan,
                                             const char *key);

        void ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
                                      const char *val);

The scan set is created by function `ZOOM_connection_scan` which performs a scan operation on the connection and start term given. If the operation was successful, the size of the scan set can be retrieved by a call to `ZOOM_scanset_size`. Like result sets, the items are numbered 0,..size-1. To obtain information about a particular scan term, call function `ZOOM_scanset_term`. This function takes a scan set offset `pos` and returns a pointer to an actual term or `NULL` if non-present. If present, the `occ` and `len` are set to the number of occurrences and the length of the actual term respectively. A scan set may be freed by a call to function `ZOOM_scanset_destroy`. Functions `ZOOM_scanset_option_get` and `ZOOM_scanset_option_set` retrieves and sets an option respectively.

### ZOOM Scan Set Options

Option | Description | Default
--- | --- | ---
number | Number of Scan Terms requested in next scan. After scan it holds the actual number of terms returned. | 10
position | Preferred Position of term in response in next scan; actual position after completion of scan. | 1
stepSize | Step Size | 0
scanStatus | An integer indicating the Scan Status of last scan. | 0

## Options

Most &zoom; objects provide a way to specify options to change behavior. From an implementation point of view a set of options is just like an associative array / hash array, etc.

         ZOOM_options ZOOM_options_create (void);

         ZOOM_options ZOOM_options_create_with_parent (ZOOM_options parent);

         void ZOOM_options_destroy (ZOOM_options opt);

         const char *ZOOM_options_get (ZOOM_options opt, const char *name);

         void ZOOM_options_set (ZOOM_options opt, const char *name,
                                const char *v);

         typedef const char *(*ZOOM_options_callback)
                                         (void *handle, const char *name);

         ZOOM_options_callback
                 ZOOM_options_set_callback (ZOOM_options opt,
                                            ZOOM_options_callback c,
                                            void *handle);

## Events

If you're developing non-blocking applications, you have to deal with events.

        int ZOOM_event (int no, ZOOM_connection *cs);

The `ZOOM_event` executes pending events for a number of connections. Supply the number of connections in `no` and an array of connections in `cs` (`cs[0] ... cs[no-1]`). A pending event could be a sending a search, receiving a response, etc. When an event has occurred for one of the connections, this function returns a positive integer `n` denoting that an event occurred for connection `cs[n-1]`. When no events are pending for the connections, a value of zero is returned. To ensure that all outstanding requests are performed call this function repeatedly until zero is returned.