sru

package
v0.33.0 Latest Latest
Warning

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

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

Documentation

Overview

Package sru is a client for the SRU (Search/Retrieve via URL) protocol, the modern HTTP successor to Z39.50 used by library catalogs for search and retrieval. It performs a searchRetrieve operation over net/http and hands the bibliographic records embedded in the XML response to libcodex's decoders, using only the standard library.

SRU responses embed each record in a recordData element in a negotiated schema. This client decodes MARCXML records into *codex.Record (via the marcxml package) and exposes any other schema's payload (MODS, Dublin Core, ...) as raw XML bytes, since those crosswalks are encode-only in this library. The Reader implements codex.RecordReader, so a catalog search is a drop-in source for codex.Convert:

c := sru.NewClient("http://lx2.loc.gov:210/lcdb")
rd := c.NewReader(ctx, `dc.title = "moby dick"`)
codex.Convert(rd, marcjson.NewWriter(os.Stdout))

It targets SRU 1.1/1.2 (the widely deployed versions); the query is CQL, passed through verbatim -- use Quote to escape a user-supplied term.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Quote

func Quote(term string) string

Quote wraps a CQL search term in double quotes, escaping embedded backslashes and quotes, so a user-supplied term is safe to place in a CQL query:

client.NewReader(ctx, "dc.title = "+sru.Quote(userInput))

Raw CQL strings pass through the client verbatim; Term and the boolean combinators build them safely.

Types

type Client

type Client struct {
	BaseURL    string       // SRU endpoint URL (scheme://host/path)
	HTTPClient *http.Client // nil uses http.DefaultClient
	Version    string       // SRU version; "" uses defaultVersion ("1.2")
	Schema     string       // default recordSchema; "" uses "marcxml"
	MaxRecords int          // records requested per page; <=0 uses defaultMaxRecords

	// MaxResponseBytes bounds how much of a response body is buffered: 0 uses a
	// generous 64 MiB default, negative means unlimited. A response over the
	// limit fails with a distinct error rather than being truncated.
	MaxResponseBytes int64
}

Client is an SRU endpoint. The zero value is not usable; construct one with NewClient. HTTPClient, Version, Schema and MaxRecords are optional overrides.

func NewClient

func NewClient(baseURL string) *Client

NewClient returns a Client for the SRU endpoint at baseURL with default settings (SRU 1.2, marcxml schema, http.DefaultClient).

func (*Client) NewReader

func (c *Client) NewReader(ctx context.Context, query string) *Reader

NewReader returns a Reader over the result set for query, using the client's default schema and page size. The context governs every underlying HTTP request; cancel it to stop an in-progress stream.

func (*Client) SearchRetrieve

func (c *Client) SearchRetrieve(ctx context.Context, r Request) (*Response, error)

SearchRetrieve runs one searchRetrieve request and parses the response. It returns a transport or parse error with a nil Response; on a well-formed response it returns the Response together with its Response.Err (a *DiagnosticsError when the search failed with diagnostics, else nil), so the records and counts remain available for inspection.

type Diagnostic

type Diagnostic struct {
	URI     string // diagnostic identifier, e.g. info:srw/diagnostic/1/7
	Message string // human-readable message
	Details string // extra context, e.g. the offending value
}

Diagnostic is one SRU diagnostic (an error or warning) from the server.

type DiagnosticsError

type DiagnosticsError struct {
	Diagnostics []Diagnostic
}

DiagnosticsError reports that a searchRetrieve returned diagnostics and no records. It carries every diagnostic the server sent.

func (*DiagnosticsError) Error

func (e *DiagnosticsError) Error() string

Error summarizes the first diagnostic and the count of any others.

type Query added in v0.12.0

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

Query is a typed CQL query, mirroring the z3950 package's builder so one query shape drives either transport:

q := sru.And(sru.Term("author", "melville"), sru.Term("title", "moby dick"))
rd := client.NewReader(ctx, q.String())

Access points map to the context set most deployments index -- Dublin Core for descriptive fields (dc.title, dc.author, ...) and the Bath profile for identifiers (bath.isbn, bath.issn, bath.lccn); an index name containing a dot (e.g. "bath.possessingInstitution") passes through unchanged for servers using another set. This is a query writer only -- it does not parse CQL.

func And added in v0.12.0

func And(a, b Query) Query

And matches records satisfying both queries.

func Not added in v0.12.0

func Not(a, b Query) Query

Not matches records satisfying a but not b.

func Or added in v0.12.0

func Or(a, b Query) Query

Or matches records satisfying either query.

func Term added in v0.12.0

func Term(index, term string) Query

Term is a single-term query against a named access point ("any", "title", "author", "subject", "isbn", "issn", "lccn", "id"); any other name (e.g. "bath.possessingInstitution") is used as the CQL index verbatim.

func (Query) String added in v0.12.0

func (q Query) String() string

String renders the query as CQL with every term quoted, parenthesizing boolean branches so nesting is unambiguous.

type Reader

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

Reader streams the records of a search result set as *codex.Record, paging through the SRU result set on demand. It implements codex.RecordReader, so an SRU search is a source for codex.Convert. Only MARCXML records decode to codex.Record; records in other schemas are skipped (inspect them with Client.SearchRetrieve instead).

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) Read

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

Read returns the next MARCXML record as a *codex.Record, fetching further pages as needed, and io.EOF once the result set is exhausted. Records in a non-MARCXML schema are skipped. A transport, parse or diagnostic error is sticky: once returned, every later call returns it too.

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 when that is unknown: before the first successful fetch, and on servers that omit numberOfRecords, which SRU 2.0 permits. Zero is a real answer, meaning the search matched nothing. It satisfies codex.RecordCounter, so a caller holding a codex.RecordReader can ask without a type switch over the protocols.

type Record

type Record struct {
	Schema   string // normalized record schema: "marcxml", "mods", "dc", or the raw id
	Packing  string // "xml" or "string"
	Position int    // 1-based position in the result set
	Data     []byte // the record payload as XML bytes, in its schema
}

Record is one record carried in a searchRetrieve response.

func (Record) Decode

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

Decode parses a MARCXML record payload into a *codex.Record. It returns an error for any other schema, whose payload remains available in Data.

type Request

type Request struct {
	Query       string // CQL query, passed through verbatim
	StartRecord int    // 1-based position of the first record; <1 means 1
	MaxRecords  int    // records to return; <=0 uses the Client default
	Schema      string // recordSchema; "" uses the Client default
}

Request is one searchRetrieve page. Only Query is required; the rest fall back to the Client's defaults.

type Response

type Response struct {
	Version            string       // the server's reported protocol version
	NumberOfRecords    int          // total hits in the result set; 0 when the server omits it, which SRU 2.0 permits
	NextRecordPosition int          // start of the next page, or 0 when exhausted
	Records            []Record     // the records on this page
	Diagnostics        []Diagnostic // non-fatal or fatal diagnostics the server returned
	// contains filtered or unexported fields
}

Response is one parsed searchRetrieve response.

func (*Response) Err

func (r *Response) Err() error

Err returns a *DiagnosticsError when the response carries diagnostics but no records (a failed search), or nil otherwise. A response with both records and diagnostics is treated as a successful partial result.

Jump to

Keyboard shortcuts

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