Add draft for article & constitution, auto set last_version column

This commit is contained in:
2019-09-06 00:36:41 +02:00
parent d7d88a3295
commit 56ea1507f7
5 changed files with 341 additions and 166 deletions

View File

@@ -13,7 +13,7 @@ begin
delete from article_relations; delete from article_relations;
delete from article; delete from article;
insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags, created_at) insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags, created_at, is_draft)
select select
uuid_in(md5('article'||row_number() over ())::cstring), uuid_in(md5('article'||row_number() over ())::cstring),
uuid_in(md5('article_v'||row_number() over () % (_citizen_count / 2))::cstring), uuid_in(md5('article_v'||row_number() over () % (_citizen_count / 2))::cstring),
@@ -23,7 +23,8 @@ begin
'content' || row_number() over (), 'content' || row_number() over (),
'description' || row_number() over (), 'description' || row_number() over (),
_tags[(row_number() over () % 5):(row_number() over () % 9)], _tags[(row_number() over () % 5):(row_number() over () % 9)],
now() + (row_number() over () * interval '7 minute 3 second') now() + (row_number() over () * interval '7 minute 3 second'),
(row_number() over () % 7) = 0
from citizen z; from citizen z;
insert into article_relations (source_id, target_id, created_by_id, comment) insert into article_relations (source_id, target_id, created_by_id, comment)

View File

@@ -11,6 +11,8 @@ begin
into resource into resource
from article as a from article as a
where a.version_id = _version_id where a.version_id = _version_id
and a.is_draft = false
and a.deleted_at is null
order by a.version_number desc order by a.version_number desc
limit 1 limit 1
) as t; ) as t;

View File

@@ -27,11 +27,19 @@ drop table if exists title;
drop function if exists set_constitution_link(); drop function if exists set_constitution_link();
drop trigger if exists generate_version_number_trigger on article; drop trigger if exists generate_version_number_trigger on article;
drop trigger if exists set_to_last_version_trigger on article;
drop trigger if exists set_last_version_trigger on article;
drop table if exists article; drop table if exists article;
drop function if exists generate_version_number(regclass, uuid); drop function if exists generate_version_number(regclass, uuid);
drop function if exists set_all_version_to_old(regclass, uuid);
drop trigger if exists generate_version_number_trigger on constitution; drop trigger if exists generate_version_number_trigger on constitution;
drop trigger if exists set_to_last_version_trigger on constitution;
drop trigger if exists set_last_version_trigger on constitution;
drop table if exists constitution; drop table if exists constitution;
drop function if exists set_version_number(); drop function if exists set_version_number();
drop function if exists set_to_last_version();
drop function if exists set_last_version();
drop function if exists set_correct_last_version();
-- User -- User
drop table if exists moderator; drop table if exists moderator;

View File

@@ -51,7 +51,10 @@ create table moderator
user_id uuid not null references "user" (id) user_id uuid not null references "user" (id)
); );
-- Article & Constitution
-------------------------------------
-- Article & Constitution triggers --
-------------------------------------
create or replace function generate_version_number(tablename regclass, version_id uuid, out generated_number int) create or replace function generate_version_number(tablename regclass, version_id uuid, out generated_number int)
language plpgsql as language plpgsql as
@@ -83,6 +86,65 @@ begin
end; end;
$$; $$;
create or replace function set_all_version_to_old(tablename regclass, version_id uuid) returns void
language plpgsql as
$$
declare
_version_id alias for version_id;
begin
if (tablename = 'article'::regclass) then
update article a
set is_last_version = false
where a.version_id = _version_id and a.is_last_version = true;
elseif (tablename = 'constitution'::regclass) then
update constitution c
set is_last_version = false
where c.version_id = _version_id and c.is_last_version = true;
else
raise exception '% is not implemented', tablename::text;
end if;
end;
$$;
create or replace function set_correct_last_version(tablename regclass, version_id uuid) returns void
language plpgsql as
$$
declare
_version_id alias for version_id;
begin
perform set_all_version_to_old(tablename, _version_id);
if (tablename = 'article'::regclass) then
update article a1
set is_last_version = true
from (
select id from article a2
where a2.version_id = _version_id
and a2.is_draft = false
and a2.deleted_at is null
order by version_number desc
limit 1
) as a3
where a1.version_id = _version_id and a1.id = a3.id;
elseif (tablename = 'constitution'::regclass) then
update constitution c1
set is_last_version = true
from (
select id from constitution c2
where c2.version_id = _version_id
and c2.is_draft = false
and c2.deleted_at is null
order by version_number desc
limit 1
) as c3
where c1.version_id = _version_id and c1.id = c3.id;
else
raise exception '% is not implemented', tablename::text;
end if;
end;
$$;
create or replace function set_version_number() returns trigger create or replace function set_version_number() returns trigger
language plpgsql as language plpgsql as
$$ $$
@@ -92,48 +154,115 @@ begin
end; end;
$$; $$;
create or replace function set_to_last_version() returns trigger
language plpgsql as
$$
begin
if (new.is_draft = false and new.deleted_at is null) then
perform set_all_version_to_old(tg_table_name::regclass, new.version_id);
new.is_last_version = true;
else
new.is_last_version = false;
end if;
return new;
end;
$$;
create or replace function set_last_version() returns trigger
language plpgsql as
$$
begin
if (new.is_draft != old.is_draft or new.deleted_at != old.deleted_at) then
perform set_correct_last_version(tg_table_name::regclass, new.version_id);
end if;
return new;
end;
$$;
-------------
-- Article --
-------------
create table article create table article
( (
id uuid default uuid_generate_v4() not null primary key, id uuid default uuid_generate_v4() not null primary key,
created_at timestamptz default now() not null, created_at timestamptz default now() not null,
created_by_id uuid not null references citizen (id), created_by_id uuid not null references citizen (id),
version_id uuid default uuid_generate_v4() not null, version_id uuid default uuid_generate_v4() not null,
version_number int not null, version_number int not null,
title text not null check ( length(title) < 128 ), title text not null check ( length(title) < 128 ),
anonymous boolean default false not null, anonymous boolean default false not null,
content text not null check ( content != '' and length(content) < 4096 ), content text not null check ( content != '' and length(content) < 4096 ),
description text null check ( description != '' and length(description) < 4096 ), description text null check ( description != '' and length(description) < 4096 ),
tags varchar(32)[] default '{}' not null, tags varchar(32)[] default '{}' not null,
deleted_at timestamptz default null null, deleted_at timestamptz default null null,
is_draft boolean default false not null,
is_last_version boolean default false not null,
unique (version_id, version_number) unique (version_id, version_number)
); );
create unique index last_version_article_idx on article (is_last_version, version_id) where is_last_version = true;
create trigger generate_version_number_trigger create trigger generate_version_number_trigger
before insert before insert
on article on article
for each row for each row
execute function set_version_number(); execute function set_version_number();
create trigger set_to_last_version_trigger
before insert
on article
for each row
execute function set_to_last_version();
create trigger set_last_version_trigger
after update
on article
for each row
execute function set_last_version();
------------------
-- Constitution --
------------------
create table constitution create table constitution
( (
id uuid default uuid_generate_v4() not null primary key, id uuid default uuid_generate_v4() not null primary key,
created_at timestamptz default now() not null, created_at timestamptz default now() not null,
created_by_id uuid not null references citizen (id), created_by_id uuid not null references citizen (id),
version_id uuid default uuid_generate_v4() not null, version_id uuid default uuid_generate_v4() not null,
version_number int not null, version_number int not null,
title text not null check ( length(title) < 128 ), title text not null check ( length(title) < 128 ),
intro text null check ( length(intro) < 4096 ), intro text null check ( length(intro) < 4096 ),
anonymous boolean default false not null, anonymous boolean default false not null,
deleted_at timestamptz default null null, deleted_at timestamptz default null null,
is_draft boolean default false not null,
is_last_version boolean default false not null,
unique (version_id, version_number) unique (version_id, version_number)
); );
create unique index last_version_constitution_idx on constitution (is_last_version, version_id) where is_last_version = true;
create trigger generate_version_number_trigger create trigger generate_version_number_trigger
before insert before insert
on constitution on constitution
for each row for each row
execute procedure set_version_number(); execute procedure set_version_number();
create trigger set_to_last_version_trigger
before insert
on constitution
for each row
execute function set_to_last_version();
create trigger set_last_version_trigger
after update
on constitution
for each row
execute function set_last_version();
------
create table title create table title
( (
id uuid default uuid_generate_v4() not null primary key, id uuid default uuid_generate_v4() not null primary key,
@@ -364,196 +493,207 @@ create table resource_view
-------------- --------------
-- ZOMBO DB -- -- ZOMBO DB --
-------------- --------------
-- Filter -- Filter
SELECT zdb.define_filter('french_stop', '{ select zdb.define_filter('french_stop', '{
"type": "stop", "type": "stop",
"stopwords": "_french_", "stopwords": "_french_",
"ignore_case": true "ignore_case": true
}'); }');
SELECT zdb.define_filter('french_elision', '{ select zdb.define_filter('french_elision', '{
"type": "elision", "type": "elision",
"articles": [ "articles": [
"à", "à",
"ainsi", "ainsi",
"alors", "alors",
"assez", "assez",
"au", "au",
"aussi", "aussi",
"aux", "aux",
"c", "c",
"ça", "ça",
"car", "car",
"ce", "ce",
"cela", "cela",
"ces", "ces",
"ceux", "ceux",
"ci", "ci",
"celle", "celle",
"celles", "celles",
"d", "d",
"de", "de",
"déjà", "déjà",
"depuis", "depuis",
"des", "des",
"donc", "donc",
"du", "du",
"et", "et",
"ici", "ici",
"l", "l",
"la", "la",
"là", "là",
"le", "le",
"les", "les",
"leur", "leur",
"leurs", "leurs",
"ma", "ma",
"mais", "mais",
"même", "même",
"mes", "mes",
"mon", "mon",
"ne", "ne",
"ni", "ni",
"notre", "notre",
"nous", "nous",
"ou", "ou",
"où", "où",
"s", "s",
"sa", "sa",
"ses", "ses",
"son", "son",
"t", "t",
"ta", "ta",
"tant", "tant",
"tantôt", "tantôt",
"tels", "tels",
"tes", "tes",
"ton", "ton",
"tôt", "tôt",
"toujours", "toujours",
"trop", "trop",
"un", "un",
"une", "une",
"votre", "votre",
"vos" "vos"
], ],
"ignore_case": true "ignore_case": true
}'); }');
SELECT zdb.define_filter('french_stemmer', '{ select zdb.define_filter('french_stemmer', '{
"type": "stemmer", "type": "stemmer",
"language": "light_french" "language": "light_french"
}'); }');
SELECT zdb.define_filter('worddelimiter', '{ select zdb.define_filter('worddelimiter', '{
"type": "word_delimiter" "type": "word_delimiter"
}'); }');
-- Tokenizer -- Tokenizer
SELECT zdb.define_tokenizer('ngram_tokenizer', '{ select zdb.define_tokenizer('ngram_tokenizer', '{
"type": "nGram", "type": "nGram",
"min_gram": 3, "min_gram": 3,
"max_gram": 7, "max_gram": 7,
"token_chars": ["letter", "digit"] "token_chars": [
"letter",
"digit"
]
}'); }');
-- Analyzer -- Analyzer
SELECT zdb.define_analyzer('name_analyzer', '{ select zdb.define_analyzer('name_analyzer', '{
"type": "custom", "type": "custom",
"tokenizer": "ngram_tokenizer", "tokenizer": "ngram_tokenizer",
"filter": ["lowercase", "asciifolding"] "filter": [
"lowercase",
"asciifolding"
]
}'); }');
SELECT zdb.define_analyzer('fr_analyzer', '{ select zdb.define_analyzer('fr_analyzer', '{
"tokenizer": "standard", "tokenizer": "standard",
"filter": ["french_elision", "worddelimiter", "asciifolding", "lowercase", "french_stop", "french_stemmer"] "filter": [
"french_elision",
"worddelimiter",
"asciifolding",
"lowercase",
"french_stop",
"french_stemmer"
]
}'); }');
-- INDEX article table -- INDEX article table
SELECT zdb.define_field_mapping('article', 'title', '{ select zdb.define_field_mapping('article', 'title', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
SELECT zdb.define_field_mapping('article', 'content', '{ select zdb.define_field_mapping('article', 'content', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
SELECT zdb.define_field_mapping('article', 'description', '{ select zdb.define_field_mapping('article', 'description', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
CREATE INDEX article_idx create index article_idx
ON article on article
USING zombodb ((article.*)) using zombodb ((article.*))
WITH (ALIAS='article_idx'); with (alias ='article_idx');
REINDEX INDEX article_idx; reindex index article_idx;
-- INDEX constitution table -- INDEX constitution table
SELECT zdb.define_field_mapping('constitution', 'title', '{ select zdb.define_field_mapping('constitution', 'title', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
SELECT zdb.define_field_mapping('constitution', 'intro', '{ select zdb.define_field_mapping('constitution', 'intro', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
CREATE INDEX constitution_idx create index constitution_idx
ON constitution on constitution
USING zombodb ((constitution.*)) using zombodb ((constitution.*))
WITH (ALIAS='constitution_idx'); with (alias ='constitution_idx');
REINDEX INDEX constitution_idx; reindex index constitution_idx;
-- INDEX coment table -- INDEX coment table
SELECT zdb.define_field_mapping('comment', 'content', '{ select zdb.define_field_mapping('comment', 'content', '{
"type": "text", "type": "text",
"analyzer": "fr_analyzer", "analyzer": "fr_analyzer",
"search_analyzer": "fr_analyzer" "search_analyzer": "fr_analyzer"
}'); }');
CREATE INDEX comment_idx create index comment_idx
ON comment on comment
USING zombodb ((comment.*)) using zombodb ((comment.*))
WITH (ALIAS='comment_idx'); with (alias ='comment_idx');
REINDEX INDEX comment_idx; reindex index comment_idx;
-- INDEX citizen table -- INDEX citizen table
SELECT zdb.define_field_mapping('citizen', 'first_name', '{ select zdb.define_field_mapping('citizen', 'first_name', '{
"type": "text", "type": "text",
"analyzer": "name_analyzer", "analyzer": "name_analyzer",
"search_analyzer": "name_analyzer" "search_analyzer": "name_analyzer"
}'); }');
SELECT zdb.define_field_mapping('citizen', 'last_name', '{ select zdb.define_field_mapping('citizen', 'last_name', '{
"type": "text", "type": "text",
"analyzer": "name_analyzer", "analyzer": "name_analyzer",
"search_analyzer": "name_analyzer" "search_analyzer": "name_analyzer"
}'); }');
CREATE INDEX citizen_idx create index citizen_idx
ON citizen on citizen
USING zombodb ((citizen.*)) using zombodb ((citizen.*))
WITH (ALIAS='citizen_idx'); with (alias ='citizen_idx');
REINDEX INDEX citizen_idx; reindex index citizen_idx;

View File

@@ -6,6 +6,9 @@ declare
_citizen_id uuid; _citizen_id uuid;
created_citizen json := '{"name": {"first_name":"George", "last_name":"MICHEL"}, "birthday": "2001-01-01"}'; created_citizen json := '{"name": {"first_name":"George", "last_name":"MICHEL"}, "birthday": "2001-01-01"}';
created_article json := '{"version_id":"933b6a1b-50c9-42b6-989f-c02a57814ef9", "title": "Love the world", "anonymous": false, "content": "bla bal bla", "tags": ["love", "test"]}'; created_article json := '{"version_id":"933b6a1b-50c9-42b6-989f-c02a57814ef9", "title": "Love the world", "anonymous": false, "content": "bla bal bla", "tags": ["love", "test"]}';
created_article_v2 json;
first_article_id uuid;
second_article_id uuid;
selected_article json; selected_article json;
selected_total int; selected_total int;
begin begin
@@ -25,9 +28,14 @@ begin
select upsert_article(created_article) into created_article; select upsert_article(created_article) into created_article;
assert created_article->>'version_id' is not null, 'version_id should not be null'; assert created_article->>'version_id' is not null, 'version_id should not be null';
assert (created_article->>'version_number')::int = 1, format('version_number must be equal to 1, %s instead', created_article->>'version_number'); assert (created_article->>'version_number')::int = 1, format('version_number must be equal to 1, %s instead', created_article->>'version_number');
assert (created_article->>'is_last_version')::bool = true, 'The first insert must be set to the last version';
first_article_id = (created_article->>'id')::uuid;
-- try to create new version -- try to create new version
select upsert_article(created_article) into created_article; select upsert_article(created_article) into created_article_v2;
assert (created_article->>'version_number')::int = 2, format('version_number must be equal to 2, %s instead', created_article->>'version_number'); assert (created_article_v2->>'version_number')::int = 2, format('version_number must be equal to 2, %s instead', created_article_v2->>'version_number');
assert (created_article_v2->>'is_last_version')::bool = true, 'The second insert must be set to the last version';
second_article_id = (created_article_v2->>'id')::uuid;
-- get articles versions by version_id -- get articles versions by version_id
select resource, total into selected_article, selected_total from find_articles_versions_by_version_id((created_article->>'version_id')::uuid); select resource, total into selected_article, selected_total from find_articles_versions_by_version_id((created_article->>'version_id')::uuid);
@@ -42,13 +50,29 @@ begin
assert selected_total = 2, format('the total must be 2, %s instead', selected_total); assert selected_total = 2, format('the total must be 2, %s instead', selected_total);
-- get article by id and check the title -- get article by id and check the title
select find_article_by_id((created_article->>'id')::uuid) into selected_article; select find_article_by_id((created_article_v2->>'id')::uuid) into selected_article;
assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title'); assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title');
-- get article by version_id and check the title -- get article by version_id and check the title
select find_last_article_by_version_id((created_article->>'version_id')::uuid) into selected_article; select find_last_article_by_version_id((created_article_v2->>'version_id')::uuid) into selected_article;
assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title'); assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title');
assert (selected_article->>'version_number')::int = 2, format('version_id must be 2, %s instead', selected_article->>'version_number'); assert (selected_article->>'version_number')::int = 2, format('version_id must be 2, %s instead', selected_article->>'version_number');
-- update to draft, then the last_version column must be change
update article
set is_draft = true
where id = second_article_id;
select find_last_article_by_version_id((created_article_v2->>'version_id')::uuid) into selected_article;
assert (selected_article->>'version_number')::int = 1, format('version_id must be 1, %s instead', selected_article->>'version_number');
update article
set is_draft = false
where id = second_article_id;
select find_last_article_by_version_id((created_article_v2->>'version_id')::uuid) into selected_article;
assert (selected_article->>'version_number')::int = 2, format('version_id must be 2, %s instead', selected_article->>'version_number');
-- -- check if user id is returned -- -- check if user id is returned
-- assert (selected_article#>>'{created_by, user, id}')::uuid = _user_id, format('user_id must be %s instead of %s', _user_id, (selected_article#>>'{created_by, user, id}')::uuid); -- assert (selected_article#>>'{created_by, user, id}')::uuid = _user_id, format('user_id must be %s instead of %s', _user_id, (selected_article#>>'{created_by, user, id}')::uuid);