# ZZPerl - Perl interface to Z39.50

## Class Hierarchy

All classes defined by the Z3950 Perl module (note the absence of the dot - this really is a Perl module name) are of the form Z3950::*.

### Z3950::Manager

This encapsulate the Z3950 module's global state - option for search parsing, preferred record syntaxes, compiled configuration files, etc. - as well as a list of references to all the open connections. We would normally expect there to be just one of these, but I suppose there's no reason why you shouldn't make more if you want.

### Z3950::Connection

Represents an established connection to a particular server on a particular port, together with options such as the default database in which to search. Maintains a record of all outstanding requests, and maybe - why not? - history of previous requests.

(Could have this merely store information and not actually do the the INIT until you do a search.)

### Z3950::Search

Represents a compiled search, ready to be sent to a server. May be built by calling a constructor to compile a search command (e.g. using CCL with a specified set of qualifiers) or Index Data's prefix notation; or a Type-1 or 101 query may be built "by hand" by creating an composing nodes of type Z3950::Search::Type1 and promoting the top-level node to a fully-fledged Z3950::Search.

### Z3950::ResultSet

Represents the result of executing a search against a specific database on a specific connection, and contains an ordered list of zero or more elements each of which is either a record or a surrogate diagnostic (see below).

### Z3950::Record

This is an abstract type, in the sense that it is never actually instantiated. Instead, it is used as a base class - really an interface specification - for a variety of concrete derivatives, of which the most interesting are:

- Z3950::Record::SUTRS
- Z3950::Record::USMARC
- Z3950::Record::DANMARC
- etc.
- Z3950::Record::GRS1
- Z3950::Record::XML

(Alternatively, the various *MARC record types could be represented as Z3950::Record::MARC::US etc.)

Each of these record types defines standard methods for operations such as as ``what's your type?'' and ``render yourself''.

An obvious starting point would be to implement SUTRS only - it's easy to translate a C-level SUTRS record (basically a string) into a Perl version.

The various MARC formats could perhaps also be dealt with trivially by simply returning a flat block of data to be interpreted by a pre-existing MARC package - is there such a beast on CPAN?

The real challenge of course is translating GRS-1 records from Yaz's format into Perl data structures: this will presumably require us to define yet more types such as Z3950::Record::GRS1::Element and build recursive trees reflecting the GRS-1 ASN.1 in Appendix RET.

XML records can be passed through "as it".

### Z3950::Diagnostic

Represents a surrogate diagnostic occurring in a result set in place of a record. (Non-surrogate diagnostics do not have an explicit representation - they are indicated by methods returning an undefined value and storing error information in the object.)

### Future Directions

The type system described in this document provides an interface to only four Z39.50 services - INIT, SEARCH, PRESENT and (implicitly) CLOSE. A future version could also provide objects to invoke SCAN, EXTENDED SERVICES, etc.

### Note

This system appears simple and indeed obvious. Is that because it's trivial, or is it just elegantly minimal and generally perfect? (And do those two options even conflict?)

## GRS-1 Interface

Apart from the obvious traversal functions, we want to be able to say:

- Get me all top-level elements tagged (2,1)
- Get me all elements with tag-path (2,1)/(4,2)/(1,1)
- zthes-like selection by related element.

## Simple Client Code

>     use Z3950;
>     $conn = new Z3950::Connection('indexdata.dk');
>     $rs = $conn->search('au=kernighan');
>     foreach $rec ($rs->records()) {
>     	print $rec->render();
>     }

## Multiplexing Client Code

>     use Z3950;
>     $mgr = new Z3950::Manager(-mode => 'async');
>     my @conn;
>     foreach $host ('indexdata.dk', 'tecc.co.uk') {
>     	push @conn, $mgr->startConnect($host);
>     }
>     foreach $conn (@conn) {
>     	$conn->startSearch('au=kernighan');
>     }
>     while ($conn = $mgr->wait()) {
>     	if ($conn->failed()) {
>     		die "error " . $conn->errcode() .
>     			"( " . $conn->addinfo() . ")" .
>     			" in " . $conn->where();
>     	}
>     	$op = $conn->op();
>     	if ($op == Z3950::Op::Search) {
>     		$rs = $conn->resultSet();
>     		$size = $rs->size();
>     		$rs->startGet(1, $size);
>     	} elsif ($op == Z3950::Op::Get) {
>     		foreach $rec ($conn->records()) {
>     		print $rec->render();
>     	}
>     }