SimpleServer(3pm)
๐ฆ SimpleServer(3pm) ๐ฆ
User Contributed Perl Documentation โข SimpleServer(3pm)
๐ NAME ๐
Net::Z3950::SimpleServer โ your fave Simple Perl API for building Z39.50 servers. periodt. ๐
โจ SYNOPSIS โจ
Okay bestie, here's the whole vibe. This is literally the shape of a SimpleServer, so drink some water and read every line. ๐ซถ
use Net::Z3950::SimpleServer;
sub my_search_handler {
my $args = shift;
my $set_id = $args->{SETNAME};
my @database_list = @{ $args->{DATABASES} };
my $query = $args->{QUERY};
## Perform the query on the specified set of databases
## and return the number of hits:
$args->{HITS} = $hits;
}
sub my_fetch_handler { # Get a record for the user
my $args = shift;
my $set_id = $args->{SETNAME};
my $record = fetch_a_record($args->{OFFSET});
$args->{RECORD} = $record;
if (number_of_hits() == $args->{OFFSET}) { ## Last record in set?
$args->{LAST} = 1;
} else {
$args->{LAST} = 0;
}
}
## Register custom event handlers:
my $z = new Net::Z3950::SimpleServer(GHANDLE = $someObject,
INIT => "main::my_init_handler",
CLOSE => "main::my_close_handler",
SEARCH => "main::my_search_handler",
FETCH => "main::my_fetch_handler");
## Launch server:
$z->launch_server("ztest.pl", @ARGV);
๐ DESCRIPTION ๐
The SimpleServer module is a tool for constructing Z39.50 "Information Retrieval" servers in Perl. The module is easy to use, but it does help to have an understanding of the Z39.50 query structure and the construction of structured retrieval records. no cap. ๐ง
Z39.50 is a network protocol for searching remote databases and retrieving the results in the form of structured "records". It is widely used in libraries around the world, as well as in the US Federal Government. In addition, it is generally useful whenever you wish to integrate a number of different database systems around a shared, abstract data model. iconic behavior, honestly. ๐
The model of the module is simple: It implements a "generic" Z39.50 server, which invokes callback functions supplied by you to search for content in your database. You can use any tools available in Perl to supply the content, including modules like DBI and WWW::Search. slay. ๐ฅ
The server will take care of managing the network connections for you, and it will spawn a new process (or thread, in some environments) whenever a new connection is received. multitasking queen. ๐
The programmer can specify subroutines to take care of the following type of events:
- Start service (called once).
- Initialize request
- Search request
- Present request
- Fetching of records
- Scan request (browsing)
- Closing down connection
Note that only the Search and Fetch handler functions are required. The module can supply default responses to the other on its own. so you don't have to do everything, bestie. ๐ซถ
After the launching of the server, all control is given away from the Perl script to the server. The server calls the registered subroutines to field incoming requests from Z39.50 clients. it's giving main character energy, but the server is the main character. ๐ญ
A reference to an anonymous hash is passed to each handler. Some of the entries of these hashes are to be considered input and others output parameters. keep up, this matters. ๐ธ
The Perl programmer specifies the event handlers for the server by means of the SimpleServer object constructor:
my $z = new Net::Z3950::SimpleServer(
START => "main::my_start_handler",
INIT => "main::my_init_handler",
CLOSE => "main::my_close_handler",
SEARCH => "main::my_search_handler",
PRESENT => "main::my_present_handler",
SCAN => "main::my_scan_handler",
FETCH => "main::my_fetch_handler",
EXPLAIN => "main::my_explain_handler",
DELETE => "main::my_delete_handler",
ESREQUEST => "main::my_esrequest_handler",
SORT => "main::my_sort_handler");
In addition, the arguments to the constructor may include GHANDLE, a global handle which is made available to each invocation of every callback function. This is typically a reference to either a hash or an object. (replace main with your package, if not main). ๐
After the custom event handlers are declared, the server is launched by means of the method
$z->launch_server("MyServer.pl", @ARGV);
Notice, the first argument should be the name of your server script (for logging purposes), while the rest of the arguments are documented in the YAZ toolkit manual: The section on application invocation: <http://www.indexdata.com/yaz/doc/server.invocation.html>
In particular, you need to use the -T switch to start your SimpleServer in threaded mode. threaded mode said "let me cook". ๐
๐ Start handler ๐
The start handler is called when service is started. The argument hash passed to the start handler has the form
$args = {
CONFIG => "default-config" ## GFS config (as given by -c)
};
The purpose of the start handler is to read the configuration file for the Generic Frontend Server . This is specified by option -c. If -c is omitted, the configuration file is set to "default-config".
The start handler is optional. It is supported in Simpleserver 1.16 and later. so if you're on an old version, don't come for me. ๐
๐ง Init handler ๐ง
The init handler is called whenever a Z39.50 client is attempting to logon to the server. The exchange of parameters between the server and the handler is carried out via an anonymous hash reached by a reference, i.e.
$args = shift;
The argument hash passed to the init handler has the form
$args = {
## Response parameters:
PEER_NAME => "", ## Name or IP address of connecting client
IMP_ID => "", ## Z39.50 Implementation ID
IMP_NAME => "", ## Z39.50 Implementation name
IMP_VER => "", ## Z39.50 Implementation version
ERR_CODE => 0, ## Error code, cnf. Z39.50 manual
ERR_STR => "", ## Error string (additional info.)
USER => "xxx" ## If Z39.50 authentication is used,
## this member contains user name
PASS => "yyy" ## Under same conditions, this member
## contains the password in clear text
GROUP => "zzz" ## Under same conditions, this member
## contains the group in clear text
GHANDLE => $obj ## Global handle specified at creation
HANDLE => undef ## Handler of Perl data structure
};
The HANDLE member can be used to store any scalar value which will then be provided as input to all subsequent calls (i.e. for searching, record retrieval, etc.). A common use of the handle is to store a reference to a hash which may then be used to store session-specific parameters. If you have any session-specific information (such as a list of result sets or a handle to a back-end search engine of some sort), it is always best to store them in a private session structure - rather than leaving them in global variables in your script. yes, protect your peace and your session state. ๐
The Implementation ID, name and version are only really used by Z39.50 client developers to see what kind of server they're dealing with. Filling these in is optional. optional but cute. ๐
The ERR_CODE should be left at 0 (the default value) if you wish to accept the connection. Any other value is interpreted as a failure and the client will be shown the door, with the code and the associated additional information, ERR_STR returned. not the door, bestie. ๐ญ
๐ฅ Search handler ๐ฅ
Similarly, the search handler is called with a reference to an anonymous hash. The structure is the following:
$args = {
## Request parameters:
GHANDLE => $obj # Global handle specified at creation
HANDLE => ref, # Your session reference.
SETNAME => "id", # ID of the result set
REPL_SET => 0, # Replace set if already existing?
DATABASES => ["xxx"], # Reference to a list of databases to search
QUERY => "query", # The query expression as a PQF string
RPN => $obj, # Reference to a Net::Z3950::APDU::Query
CQL => $x, # A CQL query, if this is provided instead of Type-1
SRW_SORTKEYS => $x, # XXX to be described
PID => $x, # XXX to be described
PRESENT_NUMBER => $x, # XXX to be described
EXTRA_ARGS => $x, # XXX to be described
INPUTFACETS => $x, # Specification of facets required: see below.
## Response parameters:
ERR_CODE => 0, # Error code (0=Successful search)
ERR_STR => "", # Error string
HITS => 0, # Number of matches
ESTIMATED_HIT_COUNT => $x, # XXX to be described
EXTRA_RESPONSE_DATA => $x, # XXX to be described
OUTPUTFACETS => $x # Facets returned: see below.
};
Note that a search which finds 0 hits is considered successful in Z39.50 terms - you should only set the ERR_CODE to a non-zero value if there was a problem processing the request. The Z39.50 standard provides a comprehensive list of standard diagnostic codes, and you should use these whenever possible. zero hits? still a win. periodt. ๐
๐ Query structures ๐
In Z39.50, the most comment kind of query is the so-called Type-1 _query, a tree-structure of terms combined by operators, the terms being qualified by lists of attributes.
The QUERY parameter presented this tree to the search function in the Prefix Query Format (PQF) which is used in many applications based on the YAZ toolkit. The full grammar is described in the YAZ manual.
The following are all examples of valid queries in the PQF.
dylan
"bob dylan"
@or "dylan" "zimmerman"
@set Result-1
@or @and bob dylan @set Result-1
@and @attr 1=1 "bob dylan" @attr 1=4 "slow train coming"
@attrset @attr 4=1 @attr 1=4 "self portrait"
Okay so here's the deal with queries, bestie: you're gonna need to write a recursive function</orange> (or something like it) to parse incoming query expressions, and lowkey that's where most of the work in writing a database-backend lives. ๐ฎโ๐จ
But the good news? You don't have to support any more functionality than you actually want to. Like, refusing to accept boolean operators is perfectly legal โ nobody's gonna come for you. Just try to return good error codes if you run into something you can't (or won't) support. ๐ซถ
A way comfier alternative to the QUERY member is the RPN member โ that one's a reference to a Net::Z3950::APDU::Query object representing the RPN query tree. Its structure is supposed to be self-documenting, but here's a brief summary of what you get, because we're being nice today:
Net::Z3950::APDU::Queryis a hash with two fields:attributeSetโ optional. If it's present, it's a reference to aNet::Z3950::APDU::OID. That's a string of dot-separated integers representing the OID of the query's top-level attribute set.queryโ mandatory, no skipping: a reference to the RPN tree itself.
- Each node of the tree is an object of one of the following types:
Net::Z3950::RPN::And,Net::Z3950::RPN::Or,Net::Z3950::RPN::AndNotโ all three of these classes are arrays of two elements, and each of those elements is a node.Net::Z3950::RPN::Termโ a query term. Details down below. ๐Net::Z3950::RPN::RSIDโ a reference to a result-set ID, meaning "hey, this points at a previous search". The ID of the result-set is hanging out in theidelement.
Net::Z3950::RPN::Termis a hash with two fields:termโ a string containing the search term itself.attributesโ a reference to aNet::Z3950::RPN::Attributesobject.
Net::Z3950::RPN::Attributesis an array of references toNet::Z3950::RPN::Attributeobjects. (Note the plural/singular distinction โ yes, bestie, read it twice. ๐ )Net::Z3950::RPN::Attributeis a hash with three elements:attributeSetโ optional. If present, it's a dot-separated OID string, as above.attributeTypeโ an integer indicating the type of the attribute. For example, under the BIB-1 attribute set, type 1 indicates ause'' attribute, type 2 arelation'' attribute, etc.attributeValueโ an integer or string indicating the value of the attribute. For example, under BIB-1, if the attribute type is 1, then value 4 indicates a title search and 7 indicates an ISBN search; but if the attribute type is 2, then value 4 indicates a ``greater than or equal'' search, and 102 indicates a relevance match.
Every one of these classes โ except Attributes and Attribute โ is a subclass of the abstract class Net::Z3950::RPN::Node. That class has a single method, toPQF(), which you can use to turn an RPN tree, or part of one, back into a textual prefix query. ๐ช
And a heads up, because it's important: apart from toPQF(), none of these classes have any methods at all. The blessing into classes is largely just a documentation thing, so that โ for example โ if you do
{ use Data::Dumper; print Dumper($args->{RPN}) }
you get something fairly human-readable. But of course, the type distinction between the three different kinds of boolean node is important. ๐จ
By adding your own methods to these classes (building what I call ``augmented classes''), you can easily build code that walks the tree of the incoming RPN. Go peek at samples/render-search.pl for a sample implementation of that augmented classes technique. ๐
Finally โ and this is the plot twist โ when SimpleServer is invoked using SRU/SRW (and indeed using Z39.50 if the unusual type-104 query is used), the query that is passed is expressed in CQL, the Contextual Query Language. In this case, the query string is made available in the CQL argument. ๐
๐ธ Facets ๐ธ
Servers may support the provision of facets โ counted lists of field values which may subsequently be used as query terms to narrow the search. Very "refine your vibe" of them. โจ
In SRU, facets may be requested by the facetLimit parameter, as documented in the OASIS standard that formalises the SRU specification: http://docs.oasis-open.org/search-ws/searchRetrieve/v1.0/os/part3-sru2.0/searchRetrieve-v1.0-os-part3-sru2.0.html#_Toc324162453. Its value is a string consisting of a comma-separated list of facet specifications. Each facet specification consists of a count, a colon and a fieldname. For example, facetLimit=10:title,5:author asks for ten title facets and five author facets. ๐
๐จ Request format
The facet request is passed to the search-handler function in the INPUTFACETS parameter. Its value is rather complex, due to backwards compatibility with Z39.50:
- The top-level value is a
Net::Z3950::FacetListarray. - This is an array of
Net::Z3950::FacetFieldobjects. - Each of these is an object with two members,
attributesandterms. attributeshas typeNet::Z3950::RPN::Attributesand is a list of objects of typeNet::Z3950::RPN::Attribute.- Each attribute has two elements,
attributeTypeandattributeValue. Each value is interpreted according to its type. The meanings of the types are as follows:- The name of the field to provide values of the facets.
- The order in which to sort the values. (But it's not clear how this is to be interpreted: it may be implementation dependent. ๐ญ)
- The number of facets to include for the specified field.
- The first facet to include in the response: for example, if this is 11, then the first ten facets should be skipped.
So for example, the SRU facet specification facetLimit=10:title,5:author would be translated as a Net::Z3950::FacetList list of two Net::Z3950::FacetFields. The attributes of the first would be [1="title", 3=10], and those of the second would be [1="author", 3=5]. ๐
It is not clear what the purpose of terms is, but for the record, this is how it is represented:
termsis aNet::Z3950::FacetTermsarray.- This is an array of
Net::Z3950::FacetTermobjects. - Each of these is an object with two members,
termandcount. The first of these is an integer, the second a string.
๐ค Response format
Having generated facets corresponding to the request, the search handler should return them in the OUTPUTFACETS argument. The structure of this response is similar to that of the request:
- The top-level value is a
Net::Z3950::FacetListarray. - This is an array of
Net::Z3950::FacetFieldobjects. - Each of these is an object with two members,
attributesandterms. attributeshas typeNet::Z3950::RPN::Attributesand is a list of objects of typeNet::Z3950::RPN::Attribute.- Each attribute has two elements,
attributeTypeandattributeValue. Each value is interpreted according to its type. The meanings of the types are as follows:- The name of the field for which terms are provided.
(That is the only type used.)
termsis aNet::Z3950::FacetTermsarray.- This is an array of
Net::Z3950::FacetTermobjects. - Each of these is an object with two members,
termandcount. The first of these is a string containing one of the facet terms, and the second is an integer indicating how many times it occurs in the records that were found by the search.
The example SimpleServer application server ztest.pl includes code that shows how to examine the INPUTFACETS data structure and create the OUTPUTFACETS structure. Go look, it's right there. ๐ซถ
๐ Present handler ๐
The presence of a present handler in a SimpleServer front-end is optional. Each time a client wishes to retrieve records, the present service is called. The present service allows the origin to request a certain number of records retrieved from a given result set. When the present handler is called, the front-end server should prepare a result set for fetching. In practice, this means getting access to the data from the backend database and storing the data in a temporary fashion for fast and efficient fetching. โก
And listen โ the present handler does not fetch anything. That task is taken care of by the fetch handler, which will be called the correct number of times by the YAZ library. More about this below. ๐
If no present handler is implemented in the front-end, the YAZ toolkit will take care of a minimum of preparations itself. This default present handler is sufficient in many situations, where only a small amount of records are expected to be retrieved. If, on the other hand, large result sets are likely to occur, the implementation of a reasonable present handler can gain performance significantly.
The information exchanged between client and present handler is:
$args = {
## Client/server request:
GHANDLE => $obj ## Global handle specified at creation
HANDLE => ref, ## Reference to datastructure
SETNAME => "id", ## Result set ID
START => xxx, ## Start position
COMP => "", ## Desired record composition
SCHEMA_OID => "", ## Z39.50 schema (OID), if any
NUMBER => yyy, ## Number of requested records
## Response parameters:
HITS => zzz, ## Number of returned records
ERR_CODE => 0, ## Error code
ERR_STR => "" ## Error message
};
๐ฆ Fetch handler ๐ฆ
The fetch handler is asked to retrieve a SINGLE record from a given result set (the front-end server will automatically call the fetch handler as many times as required). ๐
The parameters exchanged between the server and the fetch handler are:
$args = {
## Client/server request:
GHANDLE => $obj ## Global handle specified at creation
HANDLE => ref ## Reference to data structure
SETNAME => "id" ## ID of the requested result set
OFFSET => nnn ## Record offset number
REQ_FORM => "n.m.k.l"## Client requested format OID
COMP => "xyz" ## Formatting instructions
SCHEMA_OID => "", ## Z39.50 schema (OID), if any
SCHEMA => "abc" ## Requested schema (string), if any
## Handler response:
Filling in the rest of that response hash, the record-returning bits look like this:
RECORD => "" ## Record string
BASENAME => "" ## Origin of returned record
LAST => 0 ## Last record in set?
ERR_CODE => 0 ## Error code
ERR_STR => "" ## Error string
SUR_FLAG => 0 ## Surrogate diagnostic flag
REP_FORM => "n.m.k.l"## Provided format OID
SCHEMA => "abc" ## Provided schema, if any
};
REP_FORM starts out life holding whatever REQ_FORM was, but your handler is 100% allowed to swap it for something else if the vibe calls for it. BASENAME is supposed to hold the name of the database that the returned record actually came from, okay bestie, be honest about your sources ๐
. And ERR_CODE + ERR_STR? They do exactly the same thing they do in the search handler, no plot twist there.
Now if an error condition happens, SUR_FLAG is the diva that tells you which kind of error it is: either the problem is about the specific record you're fetching right now, or it's about the operation as a whole (classic example: client asked for a result set that does not exist, rip ๐ญ).
If you need to return USMARC records, go peek at the MARC module on CPAN โ unless you already have your own way of generating those, in which case slay, carry on ๐.
NOTE: the record offset is 1-indexed, so 1 is the offset of the first record in the set. Not 0. Never 0. Do not come for me about it โจ.
๐ Scan handler ๐
A full featured Z39.50 server should support scan (or, in some literature, browse). The client hands you a starting term for the scan, and your server is supposed to hand back an ordered list of a specified length, made up of terms that actually occur in the database. Every term in that list should be close to or equal to the term the client originally asked for. The whole point of scan, versus a plain simple search, is that it's a guarantee of hits โ it's literally like flipping through the index of a book, you always find something! ๐
The parameters being exchanged are:
$args = {
## Client request
GHANDLE => $obj, ## Global handle specified at creation
HANDLE => $ref, ## Reference to data structure
DATABASES => ["xxx"], ## Reference to a list of data-
## bases to search
TERM => 'start', ## The start term
RPN => $obj, ## Reference to a Net::Z3950::RPN::Term
attributeSet => OID, ## OID String (optional)
NUMBER => xx, ## Number of requested terms
POS => yy, ## Position of starting point
## within returned list
STEP => 0, ## Step size
## Server response
ERR_CODE => 0, ## Error code
ERR_STR => '', ## Diagnostic message
NUMBER => zz, ## Number of returned terms
STATUS => $status, ## ScanSuccess/ScanFailure
ENTRIES => $entries ## Referenced list of terms
};
The term list comes back by reference in the scalar $entries, and it should point at a data structure shaped like this:
my $entries = [
{ TERM => 'energy',
OCCURRENCE => 5 },
{ TERM => 'energy density',
OCCURRENCE => 6, },
{ TERM => 'energy flow',
OCCURRENCE => 3 },
...
...
];
The $status flag only means anything after a successful scan, and it should get one of exactly two values:
Net::Z3950::SimpleServer::ScanSuccess Full success (default)
Net::Z3950::SimpleServer::ScanPartial Fewer terms returned than requested
The STEP member holds the requested number of entries in the term-list between two adjacent entries in the response.
A way better alternative to the TERM member is the RPN member โ that's a reference to a Net::Z3950::RPN::Term object representing the scan clause. Its structure is the same as for Term objects included as part of the RPN tree handed to search handlers. Why is it better? Because it carries attributes (like, the access points associated with the term), which the plain TERM element just tosses out the window ๐. It's giving "why would you downgrade yourself."
๐ช Close handler ๐ธ
The argument hash the close handler receives has literally two elements, that's it, that's the whole thing:
$args = {
## Server provides:
GHANDLE => $obj ## Global handle specified at creation
HANDLE => ref ## Reference to data structure
};
Whichever data structure HANDLE points at goes out of scope right after this call. So if you need to shut down a connection to your server or do anything like that โ this is the place, bestie, don't sleep on it ๐.
๐ฃ๏ธ Explain handler โจ
The argument hash the explain handler receives has these elements:
$args = {
## Request parameters:
GHANDLE => $obj, # Global handle specified at creation
HANDLE => ref, # Reference to data structure
DATABASE => $dbname, # Name of database to be explained
## Response parameters:
EXPLAIN => $zeerex # ZeeRex record for specified database
};
Your handler should return a string containing the ZeeRex XML that describes whichever database was nominated. Iconic. Periodt. ๐
๐๏ธ Delete handler ๐
The argument hash the delete handler receives has these elements:
$args = {
## Client request:
GHANDLE => $obj, ## Global handle specified at creation
HANDLE => ref, ## Reference to data structure
SETNAME => "id", ## Result set ID
## Server response:
STATUS => 0 ## Deletion status
};
The SETNAME element may or may not be defined โ pay attention to this one:
- If it is defined,
SETNAMEis the name of the result set that needs to be deleted. - If it is not defined, then all result-sets associated with the current session should be deleted.
Either way, your callback function reports success or failure by setting STATUS to either zero for success, or an integer from 1 to 10 to flag one of the ten possible failure codes described in section 3.2.4.1.4 of the Z39.50 standard โ see http://www.loc.gov/z3950/agency/markup/05.html#Delete-list-statuses1
๐ค Sort handler ๐ฆ
The argument hash the sort handler receives has these elements:
$args = {
## Client request:
GHANDLE => $obj, ## Global handle specified at creation
HANDLE => ref, ## Reference to data structure
INPUT => [ a, b ... ], ## Names of result-sets to sort
OUTPUT => "name", ## Name of result-set to sort into
SEQUENCE ## Sort specification: see below
## Server response:
STATUS => 0, ## Success, Partial or Failure
ERR_CODE => 0, ## Error code
ERR_STR => '', ## Diagnostic message
};
The SEQUENCE element is a reference to an array, and every element of that array is a hash representing a sort key. Each of those hashes contains the following elements:
RELATIONโ0for an ascending sort,1for descending,3for ascending by frequency, or4for descending by frequency.CASEโ0for a case-sensitive sort,1for case-insensitive.MISSINGโ how to respond if one or more records in the set you're sorting are missing the fields named in the sort specification.1to abort the sort,2to use a "null value",3if a value is provided to use in place of the missing data (although in that last case, the actual value to use is currently not made available, so this is useless) ๐.
And one or other of the following:
SORTFIELDโ a string indicating the field to be sorted, which the server may interpret as it sees fit (presumably by some out-of-band agreement with the client).ELEMENTSPEC_TYPEandELEMENTSPEC_VALUEโ I have no idea what this is. ๐ญ Not me being honest.ATTRSETandSORT_ATTRโATTRSETis the attribute set the attributes are taken from, andSORT_ATTRis a reference to an array containing the attributes themselves. Each attribute is represented by (are you following this carefully?) yet another hash, this one containing the elementsATTR_TYPEandATTR_VALUE: for example,type=1andvalue=4in the BIB-1 attribute set would indicate access-point 4, which is title โ so a sort of title is what's being requested. Nested hashes all the way down, besties ๐ง.
Precisely why all of the above is so is not clear, but it goes some way to explain why, in the Z39.50 world, the developers of the standard are not so much worshiped as blamed. ๐
Your backend function should set STATUS to 0 on success, 1 for "partial success" (don't ask) or 2 on failure โ and in the failure case, ERR_CODE and ERR_STR should be set too.
๐ Support for SRU and SRW ๐
Since release 1.0, SimpleServer includes support for serving the SRU and SRW protocols as well as Z39.50. These ``web-friendly'' protocols enable similar functionality to that of Z39.50, but by means of rich URLs in the case of SRU, and a SOAP-based web-service in the case of SRW. These protocols are described at http://www.loc.gov/standards/sru/
To serve these protocols from a SimpleServer-based application, you have to launch the application with a YAZ Generic Frontend Server (GFS) configuration file, which you can specify using the command-line argument "-f" filename. A minimal configuration file looks like this:
<yazgfs>
<server>
<cql2rpn>pqf.properties</cql2rpn>
</server>
</yazgfs>
What this file says is literally just that "pqf.properties" should be used to translate the CQL queries of SRU and SRW into corresponding Z39.50 Type-1 queries. For more info about YAZ GFS configuration, including how to specify an Explain record, see the Virtual Hosts section of the YAZ manual at http://www.indexdata.com/yaz/doc/server.vhosts.html
The mapping of CQL queries into Z39.50 Type-1 queries is specified by a file that indicates which BIB-1 attributes should be generated for each CQL index, relation, modifiers, etc. A typical section of this file looks like this:
index.dc.title = 1=4
index.dc.subject = 1=21
index.dc.creator = 1=1003
relation.< = 2=1
relation.le = 2=2
So bestie, lowkey what this config file is: it's the thing that maps your BIB-1 access points (that's type=1 btw) for the Dublin Core indexes title, subject and creator โ and then the BIB-1 relations (type=2) that line up with the CQL relations < and <=. That's it, that's the whole vibe of the file. ๐
Wanna know more about how this file is even formatted? The CQL section of the YAZ manual has you covered at http://www.indexdata.com/yaz/doc/tools.html#cql โ go read it, it's giving homework but make it helpful. โจ
And ooh, the YAZ distribution ships with a sample CQL-to-PQF mapping config file named pqf.properties. That one is lowkey enough for sooo many applications, and it's genuinely a great base to build off for basically all the others. ๐
Now here's the tea tho ๐ต โ if a SimpleServer-based application runs without this SRU-specific configuration, it can still serve SRU just fine. BUT. CQL queries won't get translated at all โ they'll just get passed straight through to the search-handler function as the "CQL" member of the parameters hash. So the back-end application is fully responsible for parsing and handling that CQL query itself. The easiest way to do that? Ed Summers' truly iconic CQL::Parser module, available from CPAN at http://search.cpan.org/~esummers/CQL-Parser/ โ no cap, it ate. ๐
๐ AUTHORS ๐
The masterminds, the main characters, the ones who did that:
- Anders Sรธnderberg (
sondberg@indexdata.dk) ๐ซถ - Sebastian Hammer (
quinn@indexdata.com) โจ - Mike Taylor (
indexdata.com) ๐
๐ COPYRIGHT AND LICENCE ๐
Copyright (C) 2000-2017 by Index Data.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.4 or, at your option, any later version of Perl 5 you may have available. Fr fr, free as in go wild (respectfully). ๐ฆ
๐ SEE ALSO ๐
Literally any Perl module that's useful for accessing the data source of your choice. That's it, that's the rec โ pick your fave, pick your source, slay. โญ
And that's the whole page, bestie!! ๐ This one's from perl v5.42.3, dated 2026-08-19 โ so bookmark it, screenshot it, print it out and put it on your wall, whatever feels right. SimpleServer(3pm), you were an absolute icon. ๐ ๐ธ
๐ฆ where this page comes from
- ubuntu stonking: libnet-z3950-simpleserver-perl 1.29-1build2 ยท BSD-3-Clause
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms