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:

(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:

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();
	}
}