Ted Lawless

Using Z39.50 to produce a Stack View.


The Harvard Library Innovation Lab has developed a library browsing tool called Stack View. It provides a way to virtual browse through a collection of items from a library.

The examples on the Stack View website show how to pull data in from a variety of sources and example scripts are provided. But many libraries might want to pull data in from their own catalog. Additionally libraries might want to display the items in call number order so that the virtual Stack View approximates what a user would see if they were actually browsing the shelves in your library.

One way to get the data necessary for Stack View is via Z39.50. Below is an example of a Stack View for "On the Road" by Jack Kerouac from the Brown University library catalog.


Z39.50 is a pre-Web protocol for search and retrieval. The major advantage to using Z39.50 for data sources in examples like these is that it has been implemented widely (if varyingly) and most ILS systems provide servers both for querying internal data and allowing others to query the data in the system. In the current library systems environment, it may be the only API to the underlying data.

For more information about the protocol and working with it I recommend the Z39.50 for Dummies series that IndexData put together in 2009. You can also read some of the arguments for and against still using Z39.50 in the comment thread of this blog post on Disruptive Library Technology Jester.

Z39.50 scan results

Some Z39.50 implementations provide a facility for 'scanning' a collection by various indexes. Scan results are like the title, call number, and subject browses seen in most 'classic' library catalogs.

The Library of Congress Z39.50 implementor agreement says that scan, "returns results that consist of terms with complementary data, representing rows from an ordered list. The results can be presented to an end user, enabling him or her to browse forward and optionally backwards.."

The examples below use the Python PyZ3950 library. You can install it with 'pip install Pyz3950'. It's also helpful to use pymarc to handle the returned MARC records.

The code below has been tested only with an Innovative Interfaces Z39.50 server but it should work with minimal modification on any Z39.50 implementations that supports scan.

Call number scan.

Below is code for a basic scan by call number. As you can see, we specify the number of results to be returned (11) and the position (6) we want the requested call number to be in that list.

from PyZ3950 import zoom

conn = zoom.Connection('library.school.edu', 210)
conn.databaseName = 'INNOPAC'

call_number = "PS3521.E716 O5 1979"

params = '@attr 1=16 "%s"' % call_number
query = zoom.Query('PQF', params)

#Number of items returned.
conn.numberOfEntries = 11
#Position in the list of the item we are requesting.
response_position = 6
conn.responsePosition = response_position

results = conn.scan(query)

for index, rec in enumerate(results):
    display = rec.get('display')
    if index + 1 == response_position:
        print '---->  ', display
        print '\t', display


Running this search against the Brown University Library library catalog, you would get results like below. The arrow indicates the position in the list of the call number we requested.

        Ps 3521 E716 D4^   1 entry
        Ps 3521 E716 D6^   1 entry
        Ps 3521 E716 D6 1959^   1 entry
        Ps 3521 E716 D6 1987^   1 entry
        Ps 3521 E716 E9^   1 entry
---->   Ps 3521 E716 O5 1979^   2 entries
        Ps 3521 E716 O77 X 2002^   1 entry
        Ps 3521 E716 T6^   1 entry
        Ps 3521 E716 V3^   2 entries
        Ps 3521 E716 V3 1969^   1 entry
        Ps 3521 E716 V48^   1 entry

Call number scan and fetching metadata

Taking this a step further, now that you have a sorted list of call numbers you might want to fetch the bibliographic details for each title. In the example below, each item in the scan result is passed to a Z39.50 search that returns the MARC record for each title. The MARC record is read with pymarc and a Record object is created that allows us to convert the data into more friendly formats.

from PyZ3950 import zoom
import pymarc

conn = zoom.Connection('library.school.edu', 210)
conn.databaseName = 'INNOPAC'

def get_record_by_call(call):
    Function to fetch a bib record by
    call number.
    params = '@attr 1=16 "%s"' % call
    query = zoom.Query('PQF', params)
    results = conn.search(query)
    for bib in results:
        return pymarc.Record(data=bib.data)

call_number = "PS3521.E716 O5"

params = '@attr 1=16 "%s"' % call_number
query = zoom.Query('PQF', params)

#Number of items returned.
conn.numberOfEntries = 20
#Position in the list of the item we are requesting.
conn.responsePosition = 11

results = conn.scan(query)

for rec in results:
    #print rec
    display = rec.get('display')
    #Get the call number
    call = rec.get('term')[1]
    #Get the bibliographic record for this call number.
    bib = get_record_by_call(call)
    print bib.title()


Web service to implement Stack View on your library website

To fully integrate this into a website, you will need to run a basic web service that can lookup the call number for a given title, scan for nearby items, and then return the metadata for those items in a Stack View compliant JSON format. I have posted code for a simple Python and Flask app on Github that implements Stack View. This code is in use on an internal website and seems to be working fine but hasn't been fully implemented yet. So please use it as a reference rather than something that can be installed and used right away.