ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / db/migrations/0090_saved_searches_and_lists.sql

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / db/migrations/0090_saved_searches_and_lists.sql
DateNot specified in source
RepositoryJanady13/missionmatch
Revision720d47fae476b0d38477ae4401697d372b1ea2bc
Branchfeature/opportunity-to-outcome
Path or archive memberdb/migrations/0090_saved_searches_and_lists.sql
Original SHA-25612bf23d1ee5a9d6b0163fe59e119cb759d353cfd146c3514f79ce876267fbc7e
Git blob SHA34d81ab8dc0ba35a388af1a7d7f13b873ca9b6c8
Reading-copy SHA-25612bf23d1ee5a9d6b0163fe59e119cb759d353cfd146c3514f79ce876267fbc7e

Original source ↗

Recovered source transcript

-- +goose Up -- SEARCH, SAVED SEARCHES, AND LISTS. The recruiter's actual working surface. -- -- The gap was worse than the research suggested. There were no saved searches, and there was no SEARCH: the -- candidates route took a limit and an offset and nothing else, so "TS/SCI cleared network engineers in -- Maryland who are available" was not a question the product could be asked. The indexes to answer it have -- existed since migration 0002, a GIN index over search_tsv and an HNSW index over resume_embedding, and -- nothing was mounted on top of them. -- -- So this migration adds the two things a search becomes useful WITH, and the search itself is built on the -- indexes that were already there. -- -- saved_searches a named set of filters, re run on demand. The question you ask every morning. -- candidate_lists a named STATIC set of people. A shortlist, a bench for a bid, a do not contact roster. -- -- THE TWO ARE DIFFERENT AND BOTH ARE NEEDED, which is why this is not one table with a flag. A saved search -- is a QUESTION and its answer changes as the bench changes; a list is an ANSWER somebody committed to and it -- must not change underneath them. An agency that shortlists nine people for a bid needs those nine to still -- be those nine next week. -- ============================================================================ -- 1. saved_searches -- ============================================================================ -- The criteria are JSONB rather than typed columns, and that is the opposite of the choice made for -- org_settings in migration 0087. The reasoning differs because the risk differs. A setting decides who may -- see pay data, so a key value bag would make the wrong setting a one line insert; a saved search is a -- person's own query with no security semantics, and the filter vocabulary genuinely grows. What protects -- this is that the DATABASE NEVER INTERPRETS THE JSON: it is validated back into a typed struct in Go on -- every execution and every field becomes a bound parameter, so a stored criteria blob is never a query. CREATE TABLE saved_searches ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), name text NOT NULL, -- owner_user_id is who it belongs to. A PRIVATE search is visible only to them; a SHARED one is visible -- to the organization. Shared is not the default, because a half finished search somebody was -- experimenting with should not appear on a colleague's screen. owner_user_id uuid NOT NULL, visibility text NOT NULL DEFAULT 'private' CHECK (visibility IN ('private', 'shared')), criteria jsonb NOT NULL DEFAULT '{}'::jsonb, -- Pinning is what makes this a working surface rather than a filing cabinet: the three searches a -- recruiter runs every morning sit at the top and the other forty do not. pinned boolean NOT NULL DEFAULT false, last_run_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (organization_id, id), CONSTRAINT fk_saved_searches_owner FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE CASCADE, CONSTRAINT ck_saved_search_name CHECK (length(btrim(name)) BETWEEN 1 AND 120), -- A criteria blob is bounded so a saved search cannot become a place to store arbitrary data. CONSTRAINT ck_saved_search_size CHECK (length(criteria::text) <= 8000) ); -- One person may not have two searches by the same name. Two people may. CREATE UNIQUE INDEX uq_saved_searches_name ON saved_searches (organization_id, owner_user_id, lower(btrim(name))); CREATE INDEX idx_saved_searches_visible ON saved_searches (organization_id, visibility, pinned DESC, lower(name)); ALTER TABLE saved_searches ENABLE ROW LEVEL SECURITY; ALTER TABLE saved_searches FORCE ROW LEVEL SECURITY; CREATE POLICY saved_searches_isolation ON saved_searches USING (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid) WITH CHECK (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid); -- DELETE is granted. A saved search is a person's own working tool, not a historical claim about anybody, so -- deleting one destroys no evidence. This is the same reasoning recorded for team_members. GRANT SELECT, INSERT, UPDATE, DELETE ON saved_searches TO missionmatch_app; -- ============================================================================ -- 2. candidate_lists -- ============================================================================ CREATE TABLE candidate_lists ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), name text NOT NULL, description text NOT NULL DEFAULT '', owner_user_id uuid NOT NULL, visibility text NOT NULL DEFAULT 'shared' CHECK (visibility IN ('private', 'shared')), -- purpose is a label, and it matters for one value in particular. A do_not_contact list is a compliance -- artefact: a person who asked not to be approached again. Naming the purpose means the interface can -- treat that list differently from a shortlist rather than trusting somebody to read the title. purpose text NOT NULL DEFAULT 'shortlist' CHECK (purpose IN ('shortlist','bench','pipeline','do_not_contact','other')), status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','archived')), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (organization_id, id), CONSTRAINT uq_candidate_lists_org_id UNIQUE (organization_id, id), CONSTRAINT fk_candidate_lists_owner FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE CASCADE, CONSTRAINT ck_candidate_list_name CHECK (length(btrim(name)) BETWEEN 1 AND 120) ); CREATE UNIQUE INDEX uq_candidate_lists_name ON candidate_lists (organization_id, lower(btrim(name))) WHERE status = 'active'; ALTER TABLE candidate_lists ENABLE ROW LEVEL SECURITY; ALTER TABLE candidate_lists FORCE ROW LEVEL SECURITY; CREATE POLICY candidate_lists_isolation ON candidate_lists USING (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid) WITH CHECK (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid); -- NO DELETE. A list is ARCHIVED, because "who did we shortlist for that bid in March" is a question somebody -- will ask in June, and because a do_not_contact list that can be deleted is a compliance record that can be -- deleted. GRANT SELECT, INSERT, UPDATE ON candidate_lists TO missionmatch_app; -- ============================================================================ -- 3. candidate_list_members -- ============================================================================ CREATE TABLE candidate_list_members ( organization_id uuid NOT NULL REFERENCES organizations(id), list_id uuid NOT NULL, candidate_id uuid NOT NULL, -- Why this person is on this list. One line, written when they were added, readable by whoever picks the -- list up later. A shortlist with no reasons is a list somebody has to rebuild from memory. note text NOT NULL DEFAULT '', added_by uuid, added_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (organization_id, list_id, candidate_id), CONSTRAINT fk_list_members_list FOREIGN KEY (organization_id, list_id) REFERENCES candidate_lists (organization_id, id) ON DELETE CASCADE, CONSTRAINT fk_list_members_candidate FOREIGN KEY (organization_id, candidate_id) REFERENCES candidates (organization_id, id) ON DELETE CASCADE, CONSTRAINT fk_list_members_adder FOREIGN KEY (organization_id, added_by) REFERENCES users (organization_id, id) ON DELETE SET NULL, CONSTRAINT ck_list_member_note CHECK (length(note) <= 500) ); -- The reverse lookup: which lists is this person on. Asked on every candidate screen, so it gets its own -- index rather than a sequential scan of the membership table. CREATE INDEX idx_list_members_candidate ON candidate_list_members (organization_id, candidate_id); CREATE INDEX idx_list_members_list ON candidate_list_members (organization_id, list_id, added_at DESC); ALTER TABLE candidate_list_members ENABLE ROW LEVEL SECURITY; ALTER TABLE candidate_list_members FORCE ROW LEVEL SECURITY; CREATE POLICY candidate_list_members_isolation ON candidate_list_members USING (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid) WITH CHECK (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid); -- DELETE is granted here, unlike the list itself. Removing somebody from a shortlist is a correction, not the -- destruction of a record, and the alternative is shortlists that only ever grow. GRANT SELECT, INSERT, UPDATE, DELETE ON candidate_list_members TO missionmatch_app; -- ============================================================================ -- 4. the indexes search needs that did not exist -- ============================================================================ -- The GIN index over search_tsv and the HNSW index over resume_embedding have been there since 0002. These -- are the STRUCTURED filters a recruiter combines with the text: clearance and availability together are the -- first question asked on a cleared desk, and years of experience is the usual third. CREATE INDEX idx_candidates_clearance_avail ON candidates (organization_id, clearance_level, available) WHERE deleted_at IS NULL; CREATE INDEX idx_candidates_years ON candidates (organization_id, years_experience) WHERE deleted_at IS NULL; SELECT mm_mirror_staging_role_privileges(); -- +goose Down DROP INDEX IF EXISTS idx_candidates_years; DROP INDEX IF EXISTS idx_candidates_clearance_avail; DROP TABLE IF EXISTS candidate_list_members; DROP TABLE IF EXISTS candidate_lists; DROP TABLE IF EXISTS saved_searches;

Search equations, builds, manuscript text, and recovered sources.