User Tools

Site Tools


acq:serials:proto_schema_geometry
DROP SCHEMA serials CASCADE;
 
BEGIN;
 
-- New stuff, new schema
CREATE SCHEMA serials;
 
 
-- Table to maintain entry points into the actual pattern definitions
CREATE TABLE serials.issue_label_pattern (
    id      SERIAL  PRIMARY KEY,
    name        TEXT    UNIQUE NOT NULL
);
 
/** This is how the actual patterns are defined.  The pattern templates
  * themselves are standard TO_CHAR() templates.  They support padded and unpadded
  * signed decimal numbers, roman numerals, ordianl suffixes, locale-specific
  * currency symbol and thousands grouping, and more.  For more info, see:
  * http://www.postgresql.org/docs/current/static/functions-formatting.html#FUNCTIONS-FORMATTING-NUMERIC-TABLE
 **/
CREATE TABLE serials.issue_label_pattern_part (
    id          SERIAL  PRIMARY KEY,
    issue_label_pattern INT NOT NULL,
    level           INT NOT NULL DEFAULT 1 CHECK (level > 0),       -- should add a trigger to verify this more strictly
    cycle           INT,                            -- NULL means infinite
    pattern_template    TEXT    NOT NULL,                   -- to_char() template
    CONSTRAINT level_once_per_pattern UNIQUE (level, issue_label_pattern)
);
 
 
 
 
-- This tracks the overall subscription that brings items into the library.  There
-- will be one subscription object per order of a thing represented by a bib record
CREATE TABLE serials.subscription (
    id          SERIAL              PRIMARY KEY,
    create_date     TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    edit_date       TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    creator         INT             NOT NULL,
    editor          INT             NOT NULL,
    owner           INT             NOT NULL,
    bib_record      INT             NOT NULL,
    name            TEXT                NOT NULL,
    issue_label_pattern INT             NOT NULL,
    first_issue_number  INT             NOT NULL DEFAULT 1,
    claim_interval      INTERVAL            NOT NULL DEFAULT '1 week',
    timespan        INT             NOT NULL
);
 
-- Um ... notes. For subscriptions...
CREATE TABLE serials.subscription_note (
    id      SERIAL              PRIMARY KEY,
    subscription    INT             NOT NULL,
    creator     INT             NOT NULL,
    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    VALUE       TEXT                NOT NULL
);
 
 
 
 
 
-- Issue records created inside a subscription.
CREATE TABLE serials.issue (
    id      SERIAL  PRIMARY KEY,
    subscription    INT NOT NULL,
    issue_number    INT NOT NULL,
    timespan    INT NOT NULL,
    label       TEXT    NOT NULL  -- CN label, generated using the issue_label_{pattern|path} tables
);
 
 
CREATE TABLE serials.issue_note (
    id      SERIAL              PRIMARY KEY,
    issue       INT,
    creator     INT,
    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    VALUE       TEXT                NOT NULL
);
 
 
 
 
 
 
CREATE TABLE serials.claim (
    id      SERIAL  PRIMARY KEY,
    timespan    INT,
    claimer     INT,
    claim_time  TIMESTAMP WITH TIME ZONE,
    initial_note    TEXT
);
 
CREATE TABLE serials.claim_note (
    id      SERIAL              PRIMARY KEY,
    claim       INT,
 
    creator     INT,
    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    VALUE       TEXT                NOT NULL
);
 
 
 
 
CREATE TABLE serials.receive (
    id      SERIAL              PRIMARY KEY,
    issue       INT             NOT NULL,
    claim       INT             NOT NULL,
    timespan    INT             NOT NULL,
    receiver    INT             NOT NULL,
    receive_time    TIMESTAMP WITH TIME ZONE,
    initial_note    TEXT
);
 
CREATE TABLE serials.receive_note (
    id      SERIAL              PRIMARY KEY,
    receive     INT,
    creator     INT,
    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    VALUE       TEXT                NOT NULL
);
 
 
 
 
 
CREATE TABLE serials.timespan_granularity (
    code        TEXT    PRIMARY KEY,
    label       TEXT    UNIQUE NOT NULL,
    template    TEXT    NOT NULL
);
 
INSERT INTO serials.timespan_granularity (code, label, template)
    VALUES  ('year', 'Year',     'YYYY0000000000'),
        ('month', 'Month',   'YYYYMM00000000'),
        ('day', 'Day',       'YYYYMMDD000000'),
        ('hour', 'Hour',     'YYYYMMDDHH240000'),
        ('minute', 'Minute', 'YYYYMMDDHH24MI00'),
        ('second', 'Second', 'YYYYMMDDHH24MISS');
 
 
 
 
CREATE TABLE serials.timespan (
    id      SERIAL      PRIMARY KEY,
    granularity TEXT        NOT NULL DEFAULT 'second',
    dtstart     TIMESTAMP   NOT NULL,
    dtend       TIMESTAMP   NOT NULL
);
 
 
CREATE TABLE serials.subscription_timespan () INHERITS (serials.timespan);
 
CREATE TABLE serials.issue_timespan () INHERITS (serials.timespan);
 
CREATE TABLE serials.receive_timespan () INHERITS (serials.timespan);
 
CREATE TABLE serials.claim_timespan () INHERITS (serials.timespan);
 
-- Rows here are (re)generated based on subscription and issue information, and possibly by MARC data.
CREATE TABLE serials.coverage_timespan (
    owner       INT NOT NULL,
    bib_record  INT NOT NULL
) INHERITS (serials.timespan);
 
CREATE TABLE serials.timespan_note (
    id      SERIAL              PRIMARY KEY,
    timespan    INT             NOT NULL,
    creator     INT             NOT NULL,
    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
    VALUE       TEXT                NOT NULL
);
 
 
 
 
 
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( TIMESTAMP, TIMESTAMP, TEXT ) RETURNS box AS $$
    SELECT  box(
            point( CAST( TO_CHAR($1, template) AS DOUBLE PRECISION ),0 ),
            point( CAST( TO_CHAR($2, template) AS DOUBLE PRECISION ),0 )
        )
      FROM  serials.timespan_granularity WHERE code = $3;
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( TIMESTAMP, TIMESTAMP ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1, $2, 'second' );
$$ LANGUAGE SQL IMMUTABLE;
 
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( TIMESTAMP, TEXT ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1, $1, $2 );
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( TIMESTAMP ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1, 'second' );
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( DATE, DATE, TEXT ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1::TIMESTAMP, $2::TIMESTAMP, $3 );
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( DATE, DATE ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1::TIMESTAMP, $2::TIMESTAMP, 'day' );
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( DATE, TEXT ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1, $1, $2 );
$$ LANGUAGE SQL IMMUTABLE;
 
CREATE OR REPLACE FUNCTION serials.timeline_to_box( DATE ) RETURNS box AS $$
    SELECT serials.timeline_to_box( $1, 'day' );
$$ LANGUAGE SQL IMMUTABLE;
 
 
 
 
 
CREATE INDEX sub_segment_span_intersection ON serials.subscription_timespan USING GIST ( serials.timeline_to_box(dtstart, dtend, granularity) );
CREATE INDEX issue_segment_span_intersection ON serials.issue_timespan      USING GIST ( serials.timeline_to_box(dtstart, dtend, granularity) );
CREATE INDEX receive_segment_span_intersection ON serials.receive_timespan  USING GIST ( serials.timeline_to_box(dtstart, dtend, granularity) );
CREATE INDEX claim_segment_span_intersection ON serials.claim_timespan      USING GIST ( serials.timeline_to_box(dtstart, dtend, granularity) );
CREATE INDEX coverage_segment_span_intersection ON serials.coverage_timespan    USING GIST ( serials.timeline_to_box(dtstart, dtend, granularity) );
 
 
 
 
ALTER TABLE serials.issue_label_pattern_part
    ADD CONSTRAINT  parent_pattern
      FOREIGN KEY   (issue_label_pattern)
      REFERENCES    serials.issue_label_pattern (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_creator_usr_fkey
      FOREIGN KEY   (creator)
 
      REFERENCES actor.usr (id);
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_editor_usr_fkey
      FOREIGN KEY   (editor)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_owner_org_unit_fkey
      FOREIGN KEY   (owner)
      REFERENCES    actor.org_unit (id);
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_bib_record_fkey
      FOREIGN KEY   (bib_record)
      REFERENCES    biblio.record_entry (id);
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_issue_label_pattern_fkey
      FOREIGN KEY   (issue_label_pattern)
      REFERENCES    serials.issue_label_pattern (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.subscription_timespan
    ADD CONSTRAINT  serials_subscription_timespan_pkey
      PRIMARY KEY   (id);
 
ALTER TABLE serials.subscription
    ADD CONSTRAINT  sub_timespan_fkey
      FOREIGN KEY   (timespan)
      REFERENCES    serials.subscription_timespan (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.subscription_note
    ADD CONSTRAINT  sub_note_subscription_fkey
      FOREIGN KEY   (subscription)
      REFERENCES    serials.subscription (id);
 
ALTER TABLE serials.subscription_note
    ADD CONSTRAINT  sub_note_creator_fkey
      FOREIGN KEY   (creator)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.issue
    ADD CONSTRAINT  issue_subscription_fkey
 
      FOREIGN KEY   (subscription)
      REFERENCES    serials.subscription (id);
 
ALTER TABLE serials.issue_timespan
    ADD CONSTRAINT  serials_issue_timespan_pkey
    PRIMARY KEY (id);
 
ALTER TABLE serials.issue
    ADD CONSTRAINT  issue_timespan_fkey
      FOREIGN KEY   (timespan)
      REFERENCES    serials.issue_timespan (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.issue_note
    ADD CONSTRAINT  issue_note_issue_fkey
      FOREIGN KEY   (issue)
      REFERENCES    serials.issue (id);
 
ALTER TABLE serials.issue_note
    ADD CONSTRAINT  issue_note_creator_fkey
      FOREIGN KEY   (creator)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.claim
    ADD CONSTRAINT  claim_claimer_fkey
      FOREIGN KEY   (claimer)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.claim_timespan
    ADD CONSTRAINT  serials_claim_timespan_pkey
    PRIMARY KEY (id);
 
ALTER TABLE serials.claim
    ADD CONSTRAINT  claim_timespan_fkey
      FOREIGN KEY   (timespan)
      REFERENCES    serials.claim_timespan (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.claim_note
    ADD CONSTRAINT  claim_note_claim_fkey
      FOREIGN KEY   (claim)
      REFERENCES    serials.claim (id);
 
ALTER TABLE serials.claim_note
    ADD CONSTRAINT  claim_note_creator_fkey
 
      FOREIGN KEY   (creator)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.receive
    ADD CONSTRAINT  receive_receiver_fkey
      FOREIGN KEY   (receiver)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.receive
    ADD CONSTRAINT  receive_issue_fkey
      FOREIGN KEY   (issue)
      REFERENCES    serials.issue (id);
 
ALTER TABLE serials.receive
    ADD CONSTRAINT  receive_claim_fkey
      FOREIGN KEY   (claim)
      REFERENCES    serials.claim (id);
 
ALTER TABLE serials.receive_timespan
    ADD CONSTRAINT  serials_receive_timespan_pkey
    PRIMARY KEY (id);
 
ALTER TABLE serials.receive
    ADD CONSTRAINT  receive_timespan_fkey
      FOREIGN KEY   (timespan)
      REFERENCES    serials.receive_timespan (id)
            DEFERRABLE
            INITIALLY DEFERRED;
 
ALTER TABLE serials.receive_note
    ADD CONSTRAINT  receive_note_receive_fkey
      FOREIGN KEY   (receive)
      REFERENCES    serials.receive (id);
 
ALTER TABLE serials.receive_note
    ADD CONSTRAINT  receive_note_creator_fkey
      FOREIGN KEY   (creator)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.timespan
    ADD CONSTRAINT  timespan_granularity_fkey
      FOREIGN KEY   (granularity)
      REFERENCES    serials.timespan_granularity (code);
 
/*
ALTER TABLE serials.timespan_note
    ADD CONSTRAINT  timespan_note_timespan_fkey
 
      FOREIGN KEY   (timespan)
      REFERENCES    serials.timespan (id);
*/
 
ALTER TABLE serials.timespan_note
    ADD CONSTRAINT  timespan_note_creator_fkey
      FOREIGN KEY   (creator)
      REFERENCES    actor.usr (id);
 
ALTER TABLE serials.coverage_timespan
    ADD CONSTRAINT  serials_coverage_timespan_pkey
    PRIMARY KEY (id);
 
ALTER TABLE serials.coverage_timespan
    ADD CONSTRAINT  coverage_timespan_owner_org_unit_fkey
      FOREIGN KEY   (owner)
      REFERENCES    actor.org_unit (id);
 
ALTER TABLE serials.coverage_timespan
    ADD CONSTRAINT  coverage_timespan_bib_record_fkey
      FOREIGN KEY   (bib_record)
      REFERENCES    biblio.record_entry (id);
 
 
 
 
/*
 
Now we can do things like...
 
 SELECT DISTINCT bib_record
  FROM  serials.coverage_timespan
  WHERE serials.timeline_to_box(dtstart, dtend, granularity) ~ serials.timeline_to_box('2001-03-01'::DATE);
 
to get all bib that have coverage on March 1, 2001.  This is handy for extending OPAC searches.  For an ACQ/overview of
events ocurring during a particular month we could use
 
 SELECT t.*, c.relname
  FROM  serials.coverage_timespan t
    JOIN pg_class c ON (t.tableoid = c.oid)
  WHERE serials.timeline_to_box(dtstart, dtend, granularity) && serials.timeline_to_box('2001-03-01'::TIMESTAMP,'month');
 
To get all events entirely contained within the first half of 2001 we could use
 
 SELECT t.*, c.relname
  FROM  serials.coverage_timespan t
    JOIN pg_class c ON (t.tableoid = c.oid)
 
  WHERE serials.timeline_to_box(dtstart, dtend, granularity) @ serials.timeline_to_box('2001-01-01'::DATE, '2001-06-01'::DATE, 'month');
 
Operators of obvious use are && (overlaps), ~ (left contains right), and @ (left contained in or on right).  Of possible use
are << (is strictly left of, or before), >> (is strictly right of, or after), and &< and &> (is right-bound/left-bound,
left doesn't extend after/before right).
 
The current schema does not make use of the Y-axis for calculating timespan boxes, but if we were to, say, separate each
timespan type to it's own Y-axis "level" (or "levels") then we could use pure bounding-box logic to find most related events.
The one obvious place where this breaks down is for claim events, which are tied to an issue event, but may live entirely
outside said event.  This may also be the case for receive events, and for issue events in relation to subscriptions.
 
 
To find all owned May '07 issues of a particular bib, one could do something like
 
 SELECT i.*
  FROM  serials.issue i
    JOIN serials.issue_timespan t ON (i.timespan = t.id)
    JOIN serials.subscription s ON (i.subscription = s.id)
  WHERE s.bib_record = 12345
    AND serials.timeline_to_box(t.dtstart, t.dtend, t.granularity) && serials.timeline_to_box('2007-05-01'::TIMESTAMP);
 
*/
acq/serials/proto_schema_geometry.txt · Last modified: 2022/02/10 13:34 by 127.0.0.1

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

© 2008-2022 GPLS and others. Evergreen is open source software, freely licensed under GNU GPLv2 or later.
The Evergreen Project is a U.S. 501(c)3 non-profit organization.