z3950

package
v0.30.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package z3950 is a client for the Z39.50 information-retrieval protocol (ANSI/NISO Z39.50 / ISO 23950), the classic library search protocol that SRU succeeded. It speaks BER-encoded APDUs over TCP -- Initialize, Search (Type-1 RPN queries over the bib-1 attribute set), Present and Close -- implemented from the published standard using only the standard library.

Retrieved records decode through libcodex's readers by record syntax: MARC21 via iso2709, UNIMARC via unimarc, MARCXML via marcxml; SUTRS text is exposed raw. The Reader implements codex.RecordReader, so a Z39.50 search is a drop-in source for codex.Convert, mirroring the sru package:

c := z3950.NewClient("lx2.loc.gov:210/LCDB")
rd := c.NewReader(ctx, z3950.Term("title", "moby dick"))
codex.Convert(rd, marcjson.NewWriter(os.Stdout))

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Circulation added in v0.12.0

type Circulation struct {
	AvailableNow     bool
	AvailabilityDate string
	ItemID           string
	Renewable        bool
	OnHold           bool
}

Circulation is one circulation record of a holding.

type Client

type Client struct {
	Address   string   // host:port of the Z39.50 server
	Databases []string // databases to search
	Syntax    string   // preferred record syntax: "marc21" (default), "unimarc", "xml", "sutrs"
	PageSize  int      // records per Present; <=0 uses 10

	// Authentication, sent as idAuthentication in the Initialize request. User,
	// Password and Group select the structured idPass form; AuthOpen sends the
	// single-string open form instead, for the rare server that only accepts it.
	// All empty means anonymous (the field is omitted).
	User     string
	Password string
	Group    string
	AuthOpen string
}

Client holds the target address and session defaults. The zero value is not usable; construct one with NewClient.

func NewClient

func NewClient(target string) *Client

NewClient returns a Client for a target in host:port/database form (the conventional Z39.50 target notation, e.g. "lx2.loc.gov:210/LCDB").

func (*Client) Connect

func (c *Client) Connect(ctx context.Context) (*Conn, error)

Connect dials the target and negotiates the session with an Initialize exchange. Close the Conn when done.

func (*Client) NewReader

func (c *Client) NewReader(ctx context.Context, q Query) *Reader

NewReader returns a Reader over the result set for the query. The connection is dialed lazily on the first Read and closed automatically at the end of the result set or on error; call Reader.Close to abandon a stream early. The context governs the dial and every request.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is one Z39.50 session: a TCP connection that has completed Initialize. It is not safe for concurrent use; the protocol is strictly request/response.

func (*Conn) Close

func (co *Conn) Close() error

Close sends a Close APDU (best effort) and closes the connection.

func (*Conn) Present

func (co *Conn) Present(ctx context.Context, start, count int) ([]Record, error)

Present fetches count records from the current result set starting at the 1-based position, in the session's preferred record syntax.

func (*Conn) Search

func (co *Conn) Search(ctx context.Context, q Query) (*Result, error)

Search runs a Type-1 RPN query against the client's databases, replacing the session's default result set. Records are then fetched with Conn.Present.

type Diagnostic

type Diagnostic struct {
	Set       string // diagnostic set ("bib-1")
	Condition int    // bib-1 condition code
	Message   string // human-readable addinfo
}

Diagnostic is one bib-1 diagnostic from the server.

func (Diagnostic) String

func (d Diagnostic) String() string

type DiagnosticsError

type DiagnosticsError struct {
	Diagnostics []Diagnostic
}

DiagnosticsError reports that a search or present failed with diagnostics.

func (*DiagnosticsError) Error

func (e *DiagnosticsError) Error() string

type Holding added in v0.12.0

type Holding struct {
	NUCCode          string // holding institution (MARC organization code)
	LocalLocation    string
	ShelvingLocation string
	CallNumber       string
	CopyNumber       string
	PublicNote       string
	EnumAndChron     string // enumeration/chronology (volume, year)
	Circulation      []Circulation
}

Holding is one holdings statement from an OPAC record: where a copy lives and how it circulates. Members the server omits are empty; unknown members are skipped, never a parse failure.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query is a Z39.50 Type-1 (RPN) query over the bib-1 attribute set. Build one with Term and combine with And, Or and AndNot:

q := z3950.And(z3950.Term("author", "melville"), z3950.Term("title", "moby dick"))

A term carries a use attribute plus a structure attribute chosen automatically: phrase for multi-word terms, word otherwise (strict servers reject multi-word terms without one). Query.Phrase, Query.Word, Query.Truncated and Query.Exact refine a term, and a trailing "*" means right truncation ("mob*" finds moby; escape a literal asterisk as "\*"). Full bib-1 generality (proximity, other relations) is out of scope.

func And

func And(a, b Query) Query

And matches records satisfying both queries.

func AndNot

func AndNot(a, b Query) Query

AndNot matches records satisfying a but not b.

func Or

func Or(a, b Query) Query

Or matches records satisfying either query.

func Term

func Term(index, term string) Query

Term is a single-term query against a named access point: one of "any", "title", "author", "subject", "isbn", "issn", "lccn" or "id".

func (Query) Exact

func (q Query) Exact() Query

Exact matches the complete field exactly: relation equal, first-in-field position, complete-field completeness. Suits control numbers and uniform identifiers more than free text.

func (Query) Phrase

func (q Query) Phrase() Query

Phrase forces structure=phrase, overriding the automatic choice.

func (Query) Truncated

func (q Query) Truncated() Query

Truncated searches the term as a right-truncated stem ("mob" finds moby).

func (Query) Word

func (q Query) Word() Query

Word forces structure=word, overriding the automatic choice.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader streams a search's records as *codex.Record, dialing on first use and fetching Present pages on demand. It implements codex.RecordReader, so a Z39.50 search is a source for codex.Convert, mirroring the sru package's Reader. Records whose syntax has no codex decoder (e.g. SUTRS) are skipped; use Conn.Present directly to inspect them.

func (*Reader) All

func (rd *Reader) All() iter.Seq2[*codex.Record, error]

All returns an iterator over the remaining records, for use as "for rec, err := range r.All()". It stops at the first error.

func (*Reader) Close

func (rd *Reader) Close() error

Close releases the connection; safe to call at any time. Read reports io.EOF afterward unless another error was already sticky.

func (*Reader) Read

func (rd *Reader) Read() (*codex.Record, error)

Read returns the next decodable record, fetching further Present pages as needed, and io.EOF once the result set is exhausted. Errors (including io.EOF) are sticky.

func (*Reader) Total added in v0.23.0

func (rd *Reader) Total() int

Total reports the number of records the server said the result set holds, or -1 before the first successful fetch, which is when the search runs. Zero is a real answer, meaning the search matched nothing. A Z39.50 searchResponse always carries a result count, so for this reader -1 never survives a successful fetch.

It satisfies codex.RecordCounter, but do not lean on that guarantee through the interface: a caller holding a codex.RecordReader cannot tell this reader from github.com/freeeve/libcodex/sru.Reader, whose server may omit the count and leave Total at -1 for the life of the stream. Interface callers must handle -1 either way.

type Record

type Record struct {
	Syntax   string      // "marc21", "unimarc", "xml", "sutrs", "opac", or a dotted OID
	Data     []byte      // raw record payload in its syntax
	Diag     *Diagnostic // set instead of Data for a surrogate diagnostic
	Holdings []Holding   // holdings data from an OPAC record; nil otherwise
}

Record is one retrieved record: its record syntax, the raw payload, or a surrogate diagnostic when the server could not deliver this record. An OPAC record is unwrapped on arrival: Syntax/Data carry its embedded bibliographic record (so Decode works transparently) and Holdings carries its holdings.

func (Record) Decode

func (r Record) Decode() (*codex.Record, error)

Decode parses the record into a *codex.Record for the MARC syntaxes: MARC21 (ISO 2709), UNIMARC and MARCXML. Other syntaxes return an error; their payload remains available in Data.

type Result

type Result struct {
	Count int // total hits in the result set
}

Result reports the outcome of a Search: the total hit count on the server's result set, retrievable with Conn.Present.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL