- This document catalogs changes to the database structure and/or graphql api.
- The "DB response" instructions only need to be followed for those with their own server instance.
- The "GraphQL response" instructions only need to be followed for those with a custom client or data-access scripts.
- When exploring the new graphql api/data-structures, one can augment the written instructions with exploration of the new api, using the
debatemap.app/app-server/gql-playgroundendpoint. (or, currently, atdebates.app/app-server/gql-playground)
- When exploring the new graphql api/data-structures, one can augment the written instructions with exploration of the new api, using the
- 1) Added some new tables and columns, for the new node "subscription" system.
- DB response:
- 1) Execute the sql in files
subscriptions.sqlandnotifications.sql. - 2) Execute sql:
ALTER TABLE app."userHiddens" ADD COLUMN "notificationPolicy" character varying(1) DEFAULT 'S' NOT NULL;
- 1) Execute the sql in files
- DB response:
- 1) Added a
deleteSubtreegraphql endpoint (plusgetPreparedDataForDeletingSubtreeendpoint), and a sql function to back it.- DB response:
- 1) Execute the sql for the function
descendants_with_ancestry_attributes, seen inGraphTraversal.sql.
- 1) Execute the sql for the function
- DB response:
- 1) Removed the
Source.claimMinerIdfield.- DB response:
- 1) Since so few entries, I just manually removed these fields from the 3 node-revisions using it.
- DB response:
- 2) Added an
Attachment.extrasfield. - 3) Changed the
runCommandBatchendpoint from a mutation to a subscription. (see commit message for the benefits)
- 1) Removed the
nodeRevealscolumn from thetimelineStepstable.- DB response:
- 1) Execute sql:
ALTER TABLE app."timelineSteps" {REMOVE_THIS_ONCE_OPERATION_IS_KNOWN_SAFE} DROP COLUMN "nodeReveals";
- 1) Execute sql:
- DB response:
- 1) Added an
extrascolumn totimelineStepstable.- DB response:
- 1) Execute sql:
ALTER TABLE app."timelineSteps" ADD COLUMN "extras" jsonb DEFAULT '{}'::jsonb NOT NULL;
- 1) Execute sql:
- DB response:
- 1) Added an
timeUntilNextStepcolumn totimelineStepstable.- DB response:
- 1) Execute sql:
ALTER TABLE app."timelineSteps" ADD COLUMN "timeUntilNextStep" real;
- 1) Execute sql:
- DB response:
- 1) Added a
searchForExternalIdsgraphql endpoint, and a sql function to back it.- DB response:
- 1) Execute the sql for the function
search_for_external_ids, seen inSearch.sql. - 2) Execute the sql to set up the
attachments_ginindex, as seen innodeRevisions.sql.
- 1) Execute the sql for the function
- DB response:
- 1) Added
PermissionSet.othersfield/group.- DB response:
- 1) Execute sql:
UPDATE "accessPolicies" SET "permissions" = jsonb_set(permissions, '{others}', '{"access": false, "modify": {"minApprovals": -1, "minApprovalPercent": -1}, "delete": {"minApprovals": -1, "minApprovalPercent": -1}}'::jsonb), "permissions_userExtends" = coalesce( ( select jsonb_object_agg(j.k, '{"others": {"access": false, "modify": {"minApprovals": -1, "minApprovalPercent": -1}, "delete": {"minApprovals": -1, "minApprovalPercent": -1}}}'::jsonb || j.v) from jsonb_each("permissions_userExtends") as j(k, v) ), '{}'::jsonb );
- 1) Execute sql:
- DB response:
- 2) Added
timelinesandtimelineStepstables.- DB response:
- 1) Execute the sql in files
timelines.sqlandtimelineSteps.sql. - 2) Execute the sql to add the new functions for those tables, in
RLSPolicies.sqlandAccessPolicyTriggers.sql
- 1) Execute the sql in files
- DB response:
- 1) Added a new node attachment-type, at:
nodeRevisions.attachments.X.description - 2) Changed attachments to only show up in the node sub-panel if they have a (new)
expandedByDefaultfield set to true.- DB response:
- 1) If you want existing nodes with quote or media attachments to have them expanded by default, execute sql:
UPDATE "nodeRevisions" SET attachments = jsonb_set(attachments, '{0,expandedByDefault}', 'true') WHERE (attachments -> 0 -> 'quote' != 'null' OR attachments -> 0 -> 'media' != 'null') AND (phrasing -> 'text_base' = 'null' OR phrasing -> 'text_base' = '""') AND (phrasing -> 'text_negation' = 'null' OR phrasing -> 'text_negation' = '""') AND (phrasing -> 'text_question' = 'null' OR phrasing -> 'question' = '""');
- 1) If you want existing nodes with quote or media attachments to have them expanded by default, execute sql:
- DB response:
- 1) Removed the
nodeRevisions.notecolumn from the database. (kept that field in the graphql api though, as a proxy ofnodeRevisions.phrasing.note)- DB response:
- 1) Execute sql:
UPDATE "nodeRevisions" SET phrasing = jsonb_set(phrasing, '{note}', to_jsonb(note)) WHERE note IS NOT NULL; - 2) [added later] The command above should have excluded notes that are empty-strings... To fix this mistake from earlier, execute the follow sql: (repeated with the
0texts changed to1,2,3, etc. up to whatever the max number of attachments are present on node-revisions in the database -- if you think there could be multiple empty-description attachments in the same node-revision, do sequence in descending order, so nothing is missed [shouldn't be necessary in this case])UPDATE "nodeRevisions" SET attachments = attachments #- '{0}' WHERE attachments -> 0 -> 'description' != 'null' AND attachments -> 0 -> 'description' -> 'text' = ANY(array['""'::jsonb, 'null']);
- 1) Execute sql:
- DB response:
- 1) Changed RLS policies slightly. [+fixed that "push" triggers weren't executing on row-deletion]
- DB response:
- 1) Re-apply the sql in
RLSHelpers.sqlandRLSPolicies.sql.
- 1) Re-apply the sql in
- DB response:
- 2) Disabled the
c_accessPolicyTargets_checkconstraint fornodeTagstable. (since it is valid for node-tag entry to have no rls-targets; not ideal, since leaves it orphaned [will add UI for that for admins later], but better than erroring)- DB response:
- 1) Execute sql:
ALTER TABLE app."nodeTags" DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check"
- 1) Execute sql:
- DB response:
- 1) Changed schema name from
app_publicto justapp, and removed the unusedpublicschema.- DB response:
- 1) Execute sql:
ALTER SCHEMA app_public RENAME TO app; ALTER DATABASE "debate-map" SET search_path TO app; -- for future pg-sessions SELECT pg_catalog.set_config('search_path', 'app', false); -- for current pg-session
- 2) Execute the sql to drop the
publicschema, if desired. (not really necessary, it's just for cleanup; if choosing to do so, make sure you don't have other data there) - 3) Re-apply the sql in
@PreTables.sql.GraphTraversal.sql,RLSHelpers.sql,Search.sql, andAccessPolicyTriggers.sql. - 4) Re-apply the
after_insert_node_revisionfunc+trigger innodeRevisions.sql. - 5) Re-apply the "search/text-match config" section in
General_Start.sql. (may need to drop old objects with those names, if present)
- 1) Execute sql:
- DB response:
- 2) Deleted the
app_userrole. (nowadaysadminis used for rls-bypassing, andrls_obeyeris used for rls-respecting)- DB response:
- 1) Drop all permissions, and the db-connect ability, then drop the role.
- DB response:
- 3) Changed structure of
commandRunstable, and added triggers for it.- DB response:
- 1) Drop the
commandRunstable (its data is temporary/droppable), and recreate it, by executing the sql incommandRuns.sql. - 2) Re-apply the sql in
AccessPolicyTriggers.sqlandGeneral_End.sql. - 3) Re-apply the foreign-key constraint for
commandRunstable, as seen inFKConstraints.sql. - 4) Re-apply the rls-policy for
commandRunstable, as seen inRLSPolicies.sql.
- 1) Drop the
- DB response:
- 1) Changed the access-policy-triggers to omit duplicate access-policy-ids in its generated arrays and to exclude "empty targets" (eg. due to node-tags with refs to nodes that no longer exist); also, updated the
do_policies_allow_accesspostgres function to reflect the fact thatnodeTagscan be "left without any policy-targets".- DB response:
- 1) Added helper function, by executing sql:
create or replace function distinct_array(a text[]) returns text[] as $$ select array ( select distinct v from unnest(a) as b(v) ) $$ language sql;
- 2) Re-apply the sql in
AccessPolicyTriggers.sqlandRLSHelpers.sql. - 3) Preferably, regenerate all the
c_accessPolicyTargetscells by runningUPDATE ___XXX___ SET "c_accessPolicyTargets" = array[]::text[];for the relevant tables. (see block in 2023-01-04 set)
- 1) Added helper function, by executing sql:
- DB response:
- 2) Fixed that some
nodeLinks.orderKeycells still had characters (from old lexorank system) that are invalid for the new fractional-indexing lib.- DB response:
- 1) Run the following SQL command:
UPDATE "nodeLinks" SET "orderKey" = replace(replace("orderKey", '^', 'Zza'), '_', 'Zzb')
- 1) Run the following SQL command:
- DB response:
- 3) Added a
searchGloballygraphql endpoint, and updated the subtree-search postgres-func to a cleaner version (faster from that, but also from an extra change just added to have it use the cached-tsvector fields).- DB response:
- 1) Apply the sql in
Search.sql.
- 1) Apply the sql in
- DB response:
- 3) Merged
nodeRevisions.phrasing1_tsvectorfield into thephrasing_tsvectorfield.- DB response:
- 1) Execute sql:
DROP INDEX app."nodeRevisions_phrasing_tsvector_idx"; ALTER TABLE app."nodeRevisions" DROP COLUMN phrasing_tsvector; ALTER TABLE app."nodeRevisions" RENAME COLUMN phrasing1_tsvector TO phrasing_tsvector; DROP INDEX IF EXISTS node_revisions_phrasing_en_idx; CREATE INDEX node_revisions_phrasing_tsvector_idx ON app."nodeRevisions" USING gin (phrasing_tsvector) WHERE ("replacedBy" IS NULL);
- 1) Execute sql:
- DB response:
- 1) Added the field
c_accessPolicyTargets(and added non-null and non-empty constraints for it) to tables:commandRuns, mapNodeEdits, nodeLinks, nodePhrasings, nodeRatings, nodeRevisions, nodeTags- DB response:
- 1) Execute sql: (if this block takes forever to execute, first disable the RLS policies of the tables-to-modify, and retry)
-- start with the columns able to be null (so other steps can be completed) BEGIN; ALTER TABLE app."commandRuns" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."mapNodeEdits" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."nodeLinks" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."nodePhrasings" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."nodeRatings" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."nodeRevisions" ADD COLUMN "c_accessPolicyTargets" text[]; ALTER TABLE app."nodeTags" ADD COLUMN "c_accessPolicyTargets" text[]; COMMIT;
- 2) You'll also need to trigger all the existing rows to have their
c_accessPolicyTargetsfields updated (and field constraints set); so after doing the db-response for root bullet-points 2 and 3 below, follow-up by executing this sql:BEGIN; UPDATE "commandRuns" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "mapNodeEdits" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "nodeLinks" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "nodePhrasings" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "nodeRatings" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "nodeRevisions" SET "c_accessPolicyTargets" = array[]::text[]; UPDATE "nodeTags" SET "c_accessPolicyTargets" = array[]::text[]; ALTER TABLE app."commandRuns" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); ALTER TABLE app."mapNodeEdits" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); ALTER TABLE app."nodeLinks" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); ALTER TABLE app."nodePhrasings" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); ALTER TABLE app."nodeRatings" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); ALTER TABLE app."nodeRevisions" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); --ALTER TABLE app."nodeTags" ALTER COLUMN "c_accessPolicyTargets" SET NOT NULL, DROP CONSTRAINT IF EXISTS "c_accessPolicyTargets_check", ADD CONSTRAINT "c_accessPolicyTargets_check" CHECK (cardinality("c_accessPolicyTargets") > 0); COMMIT;
- 1) Execute sql: (if this block takes forever to execute, first disable the RLS policies of the tables-to-modify, and retry)
- DB response:
- 2) Updated the postgres rls-helper functions and many of the rls policies.
- DB response:
- 1) Re-apply the sql in
RLSHelpers.sql, then inRLSPolicies.sql.
- 1) Re-apply the sql in
- DB response:
- 3) Added many triggers, for keeping the
c_accessPolicyTargetsfields up-to-date.- DB response:
- 1) Apply the sql in
AccessPolicyTriggers.sql.
- 1) Apply the sql in
- DB response:
- 1) Changed the "is user an admin" check in RLS policies to just call into the database, rather than relying on an app-server-supplied
current_user_adminconfig-param. (note: this simplifies app-server code, but we'll probably revert to something similar eventually for perf reasons)- DB response:
- 1) Re-apply all of the sql code in
General_End.sql,RLSPolicies.sql, andRLSHelpers.sql.
- 1) Re-apply all of the sql code in
- DB response:
- 1) Fixed that the node-tag-component structures were being exposed in the graphql api as
JSONscalars rather than full-fledged GraphQL types.- GraphQL response:
- 1) Update queries
nodeTags, etc. to select the subfields of any requested tag-components, rather than just the root field-name of the tag-component itself.
- 1) Update queries
- GraphQL response:
- 1) Updated all
nodeLinks.orderKeycells, replacing any substrings of0|or:with an empty string. (changes done to make the old lexorank order-keys compatible with the newlexicon_fractional_indexcrate)- DB response:
- 1) Run the following SQL command:
UPDATE "nodeLinks" SET "orderKey" = replace(replace("orderKey", '0|', ''), ':', '')
- 1) Run the following SQL command:
- DB response:
- 1) Renamed table:
nodeChildLinks->nodeLinks- DB response:
- 1) Directly update the table-name using DBeaver.
- 2) Update the functions in
GraphTraversal.sqlto match the newer versions. - 3) To be comprehensive, you could update the names of the linked constraints and indexes (see
nodeLinks.sql). (I wouldn't bother though, as their names themselves are unlikely to need referencing)
- GraphQL response:
- 1) Update queries:
nodeChildLinks->nodeLinks,addNodeChildLink->addNodeLink, etc.
- 1) Update queries:
- DB response:
- 2) Removed table (not in use):
visibilityDirectives- DB response:
- 1) Directly remove the table using DBeaver.
- DB response:
- 3) Fixed that
NodePhrasing.termswas being exposed in the graphql api asJSONscalars rather than full-fledged GraphQL types.- GraphQL response:
- 1) Update queries
nodePhrasings, etc. to select the subfields of the entries interms(ie.terms { id }), rather than just thetermsfield-name.
- 1) Update queries
- GraphQL response:
- 4) Fixed that
NodeRevision.phrasingwas being exposed in the graphql api as aJSONscalar rather than a full-fledged GraphQL type.- GraphQL response:
- 1) Update queries
nodeRevisions, etc. to select the subfields ofNodeRevision.phrasing(eg.phrasing { text_base }), rather than just thephrasingfield-name.
- 1) Update queries
- GraphQL response:
- 1) Renamed field (to make consistent with the rest):
nodeRevisions.replaced_by->nodeRevisions.replacedBy- DB response:
- 1) Directly update the column-name using DBeaver.
- 2) Update the
app.after_insert_node_revision()function using DBeaver. (seenodeRevisions.sqlfor new version)
- GraphQL response:
- 1) Update queries
nodeRevisions, etc. to select the fieldreplacedByrather thanreplaced_by.
- 1) Update queries
- DB response: