705 lines
20 KiB
PL/PgSQL
705 lines
20 KiB
PL/PgSQL
-- Users
|
|
create table "user"
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
updated_at timestamptz default now() not null check ( updated_at >= created_at ),
|
|
blocked_at timestamptz default null null,
|
|
username varchar(64) not null check ( username != '' and lower(username) = username) unique,
|
|
password text not null check ( password != '' ),
|
|
roles text[] default '{}' not null
|
|
);
|
|
|
|
create table citizen
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
name jsonb not null check ( name ? 'first_name' and name ? 'last_name' ),
|
|
birthday date not null,
|
|
user_id uuid not null references "user" (id) unique,
|
|
vote_anonymous boolean default true not null,
|
|
follow_anonymous boolean default true not null
|
|
);
|
|
|
|
create table workgroup
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
updated_at timestamptz default now() not null check ( updated_at >= created_at ),
|
|
created_by_id uuid not null references citizen (id),
|
|
name varchar(128) not null,
|
|
description text null,
|
|
anonymous boolean default false not null,
|
|
logo text null,
|
|
owner_id uuid not null references citizen (id)
|
|
);
|
|
|
|
create table citizen_in_workgroup
|
|
(
|
|
citizen_id uuid not null references citizen (id),
|
|
workgroup_id uuid not null references workgroup (id),
|
|
created_at timestamptz default now() not null,
|
|
primary key (citizen_id, workgroup_id)
|
|
);
|
|
|
|
create table moderator
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
updated_at timestamptz default now() not null check ( updated_at >= created_at ),
|
|
assigned_period tstzrange[] default '{}' not null,
|
|
user_id uuid not null references "user" (id)
|
|
);
|
|
|
|
|
|
-------------------------------------
|
|
-- Article & Constitution triggers --
|
|
-------------------------------------
|
|
|
|
create or replace function generate_version_number(tablename regclass, version_id uuid, out generated_number int)
|
|
language plpgsql as
|
|
$$
|
|
declare
|
|
_version_id alias for version_id;
|
|
begin
|
|
if (tablename = 'article'::regclass) then
|
|
select version_number + 1
|
|
into generated_number
|
|
from article as t
|
|
where t.version_id = _version_id
|
|
order by version_number desc
|
|
limit 1;
|
|
elseif tablename = 'constitution'::regclass then
|
|
select version_number + 1
|
|
into generated_number
|
|
from constitution as t
|
|
where t.version_id = _version_id
|
|
order by version_number desc
|
|
limit 1;
|
|
else
|
|
raise exception '% is not implemented', tablename::text;
|
|
end if;
|
|
|
|
if not found then
|
|
generated_number := 1;
|
|
end if;
|
|
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 last_version = false
|
|
where a.version_id = _version_id
|
|
and a.last_version = true;
|
|
elseif (tablename = 'constitution'::regclass) then
|
|
update constitution c
|
|
set last_version = false
|
|
where c.version_id = _version_id
|
|
and c.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 last_version = true
|
|
from (
|
|
select id
|
|
from article a2
|
|
where a2.version_id = _version_id
|
|
and a2.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 last_version = true
|
|
from (
|
|
select id
|
|
from constitution c2
|
|
where c2.version_id = _version_id
|
|
and c2.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
|
|
language plpgsql as
|
|
$$
|
|
begin
|
|
new.version_number = generate_version_number(tg_table_name::regclass, new.version_id);
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function set_to_last_version() returns trigger
|
|
language plpgsql as
|
|
$$
|
|
begin
|
|
if (new.draft = false and new.deleted_at is null) then
|
|
perform set_all_version_to_old(tg_table_name::regclass, new.version_id);
|
|
new.last_version = true;
|
|
else
|
|
new.last_version = false;
|
|
end if;
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function set_last_version() returns trigger
|
|
language plpgsql as
|
|
$$
|
|
begin
|
|
if (new.draft != old.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
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid not null references citizen (id),
|
|
version_id uuid default uuid_generate_v4() not null,
|
|
version_number int not null,
|
|
title text not null check ( length(title) < 128 ),
|
|
anonymous boolean default false not null,
|
|
content text not null check ( content != '' and length(content) < 4096 ),
|
|
description text null check ( description != '' and length(description) < 4096 ),
|
|
tags varchar(32)[] default '{}' not null,
|
|
deleted_at timestamptz default null null,
|
|
draft boolean default false not null,
|
|
last_version boolean default false not null,
|
|
unique (version_id, version_number)
|
|
);
|
|
|
|
create unique index last_version_article_idx on article (last_version, version_id) where last_version = true;
|
|
|
|
create trigger generate_version_number_trigger
|
|
before insert
|
|
on article
|
|
for each row
|
|
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
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid not null references citizen (id),
|
|
version_id uuid default uuid_generate_v4() not null,
|
|
version_number int not null,
|
|
title text not null check ( length(title) < 128 ),
|
|
intro text null check ( length(intro) < 4096 ),
|
|
anonymous boolean default false not null,
|
|
deleted_at timestamptz default null null,
|
|
draft boolean default false not null,
|
|
last_version boolean default false not null,
|
|
unique (version_id, version_number)
|
|
);
|
|
|
|
create unique index last_version_constitution_idx on constitution (last_version, version_id) where last_version = true;
|
|
|
|
create trigger generate_version_number_trigger
|
|
before insert
|
|
on constitution
|
|
for each row
|
|
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
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid not null references citizen (id),
|
|
name text not null check ( name != '' ),
|
|
rank int not null check ( rank >= 0 ),
|
|
constitution_id uuid not null references constitution (id)
|
|
);
|
|
|
|
create table article_in_title
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid not null references citizen (id),
|
|
rank int not null check ( rank >= 0 ),
|
|
title_id uuid not null references title (id),
|
|
article_id uuid not null references article (id),
|
|
constitution_id uuid not null references constitution (id)
|
|
);
|
|
|
|
create or replace function set_constitution_link() returns trigger
|
|
language plpgsql as
|
|
$$
|
|
begin
|
|
new.constitution_id = (
|
|
select t.constitution_id
|
|
from title as t
|
|
where t.id = new.title_id
|
|
);
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
create trigger set_constitution_link_trigger
|
|
before insert
|
|
on article_in_title
|
|
for each row
|
|
execute procedure set_constitution_link();
|
|
|
|
create table article_relations
|
|
(
|
|
source_id uuid references article,
|
|
target_id uuid references article check ( source_id != target_id ),
|
|
created_at timestamptz default now(),
|
|
created_by_id uuid not null references citizen (id),
|
|
comment text null check ( comment != '' ),
|
|
primary key (source_id, target_id)
|
|
);
|
|
|
|
-- Extra resources
|
|
|
|
create table extra
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid not null references citizen (id),
|
|
target_id uuid not null,
|
|
target_reference regclass not null
|
|
);
|
|
|
|
create table follow
|
|
(
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (extra);
|
|
|
|
create table follow_article
|
|
(
|
|
target_reference regclass default 'article'::regclass not null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
foreign key (target_id) references article (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (follow);
|
|
|
|
create table follow_constitution
|
|
(
|
|
target_reference regclass default 'constitution'::regclass not null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
foreign key (target_id) references constitution (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (follow);
|
|
|
|
create table follow_citizen
|
|
(
|
|
target_reference regclass default 'citizen'::regclass not null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
foreign key (target_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (follow);
|
|
|
|
|
|
|
|
create table comment
|
|
(
|
|
updated_at timestamptz default now() not null check ( updated_at >= created_at ),
|
|
"content" text not null check ( content != '' and length(content) < 4096),
|
|
parent_id uuid references comment (id),
|
|
parents_ids uuid[],
|
|
deleted_at timestamptz null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id)
|
|
) inherits (extra);
|
|
|
|
create index comment_parents_ids_idx
|
|
on comment (parents_ids);
|
|
|
|
create or replace function set_comment_parents_ids() returns trigger
|
|
language plpgsql as
|
|
$$
|
|
begin
|
|
if (new.parent_id is not null) then
|
|
new.parents_ids = (
|
|
select com.parents_ids || com.id
|
|
from "comment" com
|
|
where com.id = new.parent_id
|
|
);
|
|
else
|
|
new.parents_ids = null;
|
|
end if;
|
|
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
create trigger set_comment_parents_ids_trigger
|
|
before insert
|
|
on comment
|
|
for each row
|
|
execute procedure set_comment_parents_ids();
|
|
|
|
create table comment_on_article
|
|
(
|
|
target_reference regclass default 'article'::regclass not null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
foreign key (target_id) references article (id),
|
|
foreign key (parent_id) references comment_on_article (id),
|
|
primary key (id)
|
|
) inherits (comment);
|
|
|
|
create index comment_on_article_parents_ids_idx
|
|
on comment_on_article (parents_ids);
|
|
|
|
create trigger set_comment_on_article_parents_ids_trigger
|
|
before insert
|
|
on comment_on_article
|
|
for each row
|
|
execute procedure set_comment_parents_ids();
|
|
|
|
create table comment_on_constitution
|
|
(
|
|
target_reference regclass default 'constitution'::regclass not null,
|
|
foreign key (created_by_id) references citizen (id),
|
|
foreign key (target_id) references constitution (id),
|
|
foreign key (parent_id) references comment_on_constitution (id),
|
|
primary key (id)
|
|
) inherits (comment);
|
|
|
|
create index comment_on_constitution_parents_ids_idx
|
|
on comment_on_constitution (parents_ids);
|
|
|
|
create trigger set_comment_on_constitution_parents_ids_trigger
|
|
before insert
|
|
on comment_on_constitution
|
|
for each row
|
|
execute procedure set_comment_parents_ids();
|
|
|
|
|
|
|
|
create table vote
|
|
(
|
|
anonymous boolean default true not null,
|
|
note int not null check ( note >= -1 and note <= 1 ),
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (extra);
|
|
|
|
create table vote_for_article
|
|
(
|
|
target_reference regclass default 'article'::regclass not null,
|
|
foreign key (target_id) references article (id),
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (vote);
|
|
|
|
create table vote_for_constitution
|
|
(
|
|
target_reference regclass default 'constitution'::regclass not null,
|
|
foreign key (target_id) references constitution (id),
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (vote);
|
|
|
|
create table vote_for_comment_on_article
|
|
(
|
|
target_reference regclass default 'comment_on_article'::regclass not null,
|
|
foreign key (target_id) references comment_on_article (id),
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (vote);
|
|
|
|
create table vote_for_comment_on_constitution
|
|
(
|
|
target_reference regclass default 'comment_on_constitution'::regclass not null,
|
|
foreign key (target_id) references comment_on_constitution (id),
|
|
foreign key (created_by_id) references citizen (id),
|
|
primary key (id),
|
|
unique (created_by_id, target_id)
|
|
) inherits (vote);
|
|
|
|
-- Stats
|
|
create table resource_view
|
|
(
|
|
id uuid default uuid_generate_v4() not null primary key,
|
|
type regclass not null,
|
|
created_at timestamptz default now() not null,
|
|
created_by_id uuid null references citizen (id),
|
|
ip cidr null
|
|
);
|
|
|
|
|
|
|
|
--------------
|
|
-- ZOMBO DB --
|
|
--------------
|
|
|
|
-- Filter
|
|
select zdb.define_filter('french_stop', '{
|
|
"type": "stop",
|
|
"stopwords": "_french_",
|
|
"ignore_case": true
|
|
}');
|
|
|
|
select zdb.define_filter('french_elision', '{
|
|
"type": "elision",
|
|
"articles": [
|
|
"à",
|
|
"ainsi",
|
|
"alors",
|
|
"assez",
|
|
"au",
|
|
"aussi",
|
|
"aux",
|
|
"c",
|
|
"ça",
|
|
"car",
|
|
"ce",
|
|
"cela",
|
|
"ces",
|
|
"ceux",
|
|
"ci",
|
|
"celle",
|
|
"celles",
|
|
"d",
|
|
"de",
|
|
"déjà",
|
|
"depuis",
|
|
"des",
|
|
"donc",
|
|
"du",
|
|
"et",
|
|
"ici",
|
|
"l",
|
|
"la",
|
|
"là",
|
|
"le",
|
|
"les",
|
|
"leur",
|
|
"leurs",
|
|
"ma",
|
|
"mais",
|
|
"même",
|
|
"mes",
|
|
"mon",
|
|
"ne",
|
|
"ni",
|
|
"notre",
|
|
"nous",
|
|
"ou",
|
|
"où",
|
|
"s",
|
|
"sa",
|
|
"ses",
|
|
"son",
|
|
"t",
|
|
"ta",
|
|
"tant",
|
|
"tantôt",
|
|
"tels",
|
|
"tes",
|
|
"ton",
|
|
"tôt",
|
|
"toujours",
|
|
"trop",
|
|
"un",
|
|
"une",
|
|
"votre",
|
|
"vos"
|
|
],
|
|
"ignore_case": true
|
|
}');
|
|
|
|
select zdb.define_filter('french_stemmer', '{
|
|
"type": "stemmer",
|
|
"language": "light_french"
|
|
}');
|
|
|
|
select zdb.define_filter('worddelimiter', '{
|
|
"type": "word_delimiter"
|
|
}');
|
|
|
|
-- Tokenizer
|
|
select zdb.define_tokenizer('ngram_tokenizer', '{
|
|
"type": "nGram",
|
|
"min_gram": 3,
|
|
"max_gram": 7,
|
|
"token_chars": [
|
|
"letter",
|
|
"digit"
|
|
]
|
|
}');
|
|
|
|
-- Analyzer
|
|
select zdb.define_analyzer('name_analyzer', '{
|
|
"type": "custom",
|
|
"tokenizer": "ngram_tokenizer",
|
|
"filter": [
|
|
"lowercase",
|
|
"asciifolding"
|
|
]
|
|
}');
|
|
|
|
select zdb.define_analyzer('fr_analyzer', '{
|
|
"tokenizer": "standard",
|
|
"filter": [
|
|
"french_elision",
|
|
"worddelimiter",
|
|
"asciifolding",
|
|
"lowercase",
|
|
"french_stop",
|
|
"french_stemmer"
|
|
]
|
|
}');
|
|
|
|
-- INDEX article table
|
|
select zdb.define_field_mapping('article', 'title', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
select zdb.define_field_mapping('article', 'content', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
select zdb.define_field_mapping('article', 'description', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
create index article_idx
|
|
on article
|
|
using zombodb ((article.*))
|
|
with (alias ='article_idx');
|
|
|
|
reindex index article_idx;
|
|
|
|
|
|
-- INDEX constitution table
|
|
select zdb.define_field_mapping('constitution', 'title', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
select zdb.define_field_mapping('constitution', 'intro', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
create index constitution_idx
|
|
on constitution
|
|
using zombodb ((constitution.*))
|
|
with (alias ='constitution_idx');
|
|
|
|
reindex index constitution_idx;
|
|
|
|
|
|
-- INDEX coment table
|
|
select zdb.define_field_mapping('comment', 'content', '{
|
|
"type": "text",
|
|
"analyzer": "fr_analyzer",
|
|
"search_analyzer": "fr_analyzer"
|
|
}');
|
|
|
|
create index comment_idx
|
|
on comment
|
|
using zombodb ((comment.*))
|
|
with (alias ='comment_idx');
|
|
|
|
reindex index comment_idx;
|
|
|
|
|
|
-- INDEX citizen table
|
|
select zdb.define_field_mapping('citizen', 'first_name', '{
|
|
"type": "text",
|
|
"analyzer": "name_analyzer",
|
|
"search_analyzer": "name_analyzer"
|
|
}');
|
|
|
|
select zdb.define_field_mapping('citizen', 'last_name', '{
|
|
"type": "text",
|
|
"analyzer": "name_analyzer",
|
|
"search_analyzer": "name_analyzer"
|
|
}');
|
|
|
|
create index citizen_idx
|
|
on citizen
|
|
using zombodb ((citizen.*))
|
|
with (alias ='citizen_idx');
|
|
|
|
reindex index citizen_idx; |