Merged database migrations.
This commit is contained in:
parent
85942c5d00
commit
dbaddfb291
|
@ -38,7 +38,7 @@ module.exports = {
|
||||||
login: true,
|
login: true,
|
||||||
signup: true,
|
signup: true,
|
||||||
usernameLength: [2, 24],
|
usernameLength: [2, 24],
|
||||||
usernamePattern: /^[a-zA-Z0-9_-]$/,
|
usernamePattern: /^[a-zA-Z0-9_-]+$/,
|
||||||
},
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
channels: [
|
channels: [
|
||||||
|
|
|
@ -64,6 +64,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.boolean('independent')
|
table.boolean('independent')
|
||||||
.defaultTo(false);
|
.defaultTo(false);
|
||||||
|
|
||||||
|
table.boolean('showcased');
|
||||||
table.boolean('visible')
|
table.boolean('visible')
|
||||||
.defaultTo(true);
|
.defaultTo(true);
|
||||||
|
|
||||||
|
@ -651,6 +652,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.integer('duration')
|
table.integer('duration')
|
||||||
.unsigned();
|
.unsigned();
|
||||||
|
|
||||||
|
table.specificType('qualities', 'text[]');
|
||||||
|
|
||||||
table.boolean('deep');
|
table.boolean('deep');
|
||||||
table.text('deep_url', 1000);
|
table.text('deep_url', 1000);
|
||||||
|
|
||||||
|
@ -804,6 +807,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.text('original_tag');
|
table.text('original_tag');
|
||||||
|
|
||||||
table.unique(['tag_id', 'release_id']);
|
table.unique(['tag_id', 'release_id']);
|
||||||
|
table.index('tag_id');
|
||||||
|
table.index('release_id');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('releases_search', (table) => {
|
.then(() => knex.schema.createTable('releases_search', (table) => {
|
||||||
table.integer('release_id', 16)
|
table.integer('release_id', 16)
|
||||||
|
@ -873,6 +878,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
|
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.index('scene_id');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('movies_covers', (table) => {
|
.then(() => knex.schema.createTable('movies_covers', (table) => {
|
||||||
table.integer('movie_id', 16)
|
table.integer('movie_id', 16)
|
||||||
|
@ -916,12 +923,151 @@ exports.up = (knex) => Promise.resolve()
|
||||||
|
|
||||||
table.unique('movie_id');
|
table.unique('movie_id');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('movies_photos', (table) => {
|
||||||
|
table.integer('movie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('movies')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['movie_id', 'media_id']);
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('movies_search', (table) => {
|
.then(() => knex.schema.createTable('movies_search', (table) => {
|
||||||
table.integer('movie_id', 16)
|
table.integer('movie_id', 16)
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('movies')
|
.inTable('movies')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series', (table) => {
|
||||||
|
table.increments('id', 16);
|
||||||
|
|
||||||
|
table.integer('entity_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.integer('studio_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.text('entry_id');
|
||||||
|
table.unique(['entity_id', 'entry_id']);
|
||||||
|
|
||||||
|
table.text('url', 1000);
|
||||||
|
table.text('title');
|
||||||
|
table.text('slug');
|
||||||
|
|
||||||
|
table.timestamp('date');
|
||||||
|
table.index('date');
|
||||||
|
|
||||||
|
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
||||||
|
.defaultTo('day');
|
||||||
|
|
||||||
|
table.text('description');
|
||||||
|
|
||||||
|
table.boolean('deep');
|
||||||
|
table.text('deep_url', 1000);
|
||||||
|
|
||||||
|
table.text('comment');
|
||||||
|
|
||||||
|
table.integer('created_batch_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('batches')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('updated_batch_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('batches')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_scenes', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('scene_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('releases')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'scene_id']);
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_trailers', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.unique()
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_posters', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique('serie_id');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_covers', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'media_id']);
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_photos', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('media_id', 21)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('media');
|
||||||
|
|
||||||
|
table.unique(['serie_id', 'media_id']);
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('series_search', (table) => {
|
||||||
|
table.integer('serie_id', 16)
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('chapters', (table) => {
|
.then(() => knex.schema.createTable('chapters', (table) => {
|
||||||
table.increments('id', 16);
|
table.increments('id', 16);
|
||||||
|
|
||||||
|
@ -1062,6 +1208,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.datetime('last_login');
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('stashes', (table) => {
|
.then(() => knex.schema.createTable('stashes', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
@ -1088,7 +1236,12 @@ exports.up = (knex) => Promise.resolve()
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.unique(['user_id', 'slug']);
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.raw(`
|
||||||
|
CREATE UNIQUE INDEX unique_primary ON stashes (user_id, "primary") WHERE ("primary" = TRUE);
|
||||||
|
`))
|
||||||
.then(() => knex.schema.createTable('stashes_scenes', (table) => {
|
.then(() => knex.schema.createTable('stashes_scenes', (table) => {
|
||||||
table.integer('stash_id')
|
table.integer('stash_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
|
@ -1152,6 +1305,27 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('stashes_series', (table) => {
|
||||||
|
table.integer('stash_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('stashes')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('serie_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('series')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique(['stash_id', 'serie_id']);
|
||||||
|
|
||||||
|
table.string('comment');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('alerts', (table) => {
|
.then(() => knex.schema.createTable('alerts', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
|
||||||
|
@ -1329,6 +1503,9 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.raw(`
|
||||||
|
ALTER TABLE banners ADD COLUMN ratio numeric GENERATED ALWAYS AS (ROUND(width::decimal/ height::decimal, 2)) STORED;
|
||||||
|
`))
|
||||||
.then(() => knex.schema.createTable('banners_tags', (table) => {
|
.then(() => knex.schema.createTable('banners_tags', (table) => {
|
||||||
table.increments('id');
|
table.increments('id');
|
||||||
|
|
||||||
|
@ -1372,12 +1549,32 @@ exports.up = (knex) => Promise.resolve()
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('random_campaign', (table) => {
|
||||||
|
table.text('banner_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('banners');
|
||||||
|
|
||||||
|
table.text('url');
|
||||||
|
|
||||||
|
table.integer('entity_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.string('affiliate_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('affiliates');
|
||||||
|
|
||||||
|
table.integer('parent_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
}))
|
||||||
// SEARCH AND SORT
|
// SEARCH AND SORT
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
// allow vim fold
|
// allow vim fold
|
||||||
return knex.raw(`
|
return knex.raw(`
|
||||||
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
||||||
ALTER TABLE movies_search ADD COLUMN document tsvector;
|
ALTER TABLE movies_search ADD COLUMN document tsvector;
|
||||||
|
ALTER TABLE series_search ADD COLUMN document tsvector;
|
||||||
|
|
||||||
/* allow scenes without dates to be mixed inbetween scenes with dates */
|
/* allow scenes without dates to be mixed inbetween scenes with dates */
|
||||||
ALTER TABLE releases
|
ALTER TABLE releases
|
||||||
|
@ -1401,6 +1598,8 @@ exports.up = (knex) => Promise.resolve()
|
||||||
CREATE UNIQUE INDEX movies_search_unique ON movies_search (movie_id);
|
CREATE UNIQUE INDEX movies_search_unique ON movies_search (movie_id);
|
||||||
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
||||||
CREATE INDEX movies_search_index ON movies_search USING GIN (document);
|
CREATE INDEX movies_search_index ON movies_search USING GIN (document);
|
||||||
|
CREATE UNIQUE INDEX series_search_unique ON series_search (serie_id);
|
||||||
|
CREATE INDEX series_search_index ON series_search USING GIN (document);
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
|
@ -1421,15 +1620,13 @@ exports.up = (knex) => Promise.resolve()
|
||||||
CREATE TABLE movies_search_results (movie_id integer, rank real, FOREIGN KEY (movie_id) REFERENCES movies (id));
|
CREATE TABLE movies_search_results (movie_id integer, rank real, FOREIGN KEY (movie_id) REFERENCES movies (id));
|
||||||
|
|
||||||
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
||||||
SELECT releases.id, ranks.rank FROM (
|
SELECT results.release_id, ts_rank(results.document::tsvector, curate_search_query(query)) as rank
|
||||||
SELECT
|
FROM (
|
||||||
releases_search.release_id,
|
SELECT releases_search.release_id, document
|
||||||
ts_rank(releases_search.document, to_tsquery('english', array_to_string(array(SELECT * FROM regexp_matches(query, '[A-Za-zÀ-ÖØ-öø-ÿ0-9]+', 'g')), '|'))) AS rank
|
|
||||||
FROM releases_search
|
FROM releases_search
|
||||||
) ranks
|
WHERE document::tsvector @@ curate_search_query(query)
|
||||||
LEFT JOIN releases ON releases.id = ranks.release_id
|
) AS results
|
||||||
WHERE ranks.rank > 0
|
ORDER BY rank DESC;
|
||||||
ORDER BY ranks.rank DESC;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION search_movies(query text) RETURNS SETOF movies_search_results AS $$
|
CREATE FUNCTION search_movies(query text) RETURNS SETOF movies_search_results AS $$
|
||||||
|
@ -1513,6 +1710,52 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ORDER BY actors.name;
|
ORDER BY actors.name;
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION entities_scenes(entity entities) RETURNS SETOF releases AS $$
|
||||||
|
WITH RECURSIVE children AS (
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
WHERE entities.id = entity.id
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
INNER JOIN children ON children.id = entities.parent_id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.entity_id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.studio_id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS bigint AS $$
|
||||||
|
SELECT COUNT(id)
|
||||||
|
FROM releases
|
||||||
|
WHERE releases.entity_id = entity.id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION entities_scene_tags(entity entities, selectable_tags text[]) RETURNS SETOF tags AS $$
|
||||||
|
SELECT tags.*
|
||||||
|
FROM releases
|
||||||
|
LEFT JOIN
|
||||||
|
releases_tags ON releases_tags.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
tags ON tags.id = releases_tags.tag_id
|
||||||
|
WHERE
|
||||||
|
releases.entity_id = entity.id
|
||||||
|
AND
|
||||||
|
CASE WHEN array_length(selectable_tags, 1) IS NOT NULL
|
||||||
|
THEN tags.slug = ANY(selectable_tags)
|
||||||
|
ELSE true
|
||||||
|
END
|
||||||
|
GROUP BY tags.id
|
||||||
|
ORDER BY tags.name;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
/* GraphQL/Postgraphile 'every' applies to the data, will only include scenes for which every assigned tag is selected,
|
/* GraphQL/Postgraphile 'every' applies to the data, will only include scenes for which every assigned tag is selected,
|
||||||
instead of what we want; scenes with every selected tag, but possibly also some others */
|
instead of what we want; scenes with every selected tag, but possibly also some others */
|
||||||
CREATE FUNCTION actors_scenes(actor actors, selected_tags text[], mode text DEFAULT 'all') RETURNS SETOF releases AS $$
|
CREATE FUNCTION actors_scenes(actor actors, selected_tags text[], mode text DEFAULT 'all') RETURNS SETOF releases AS $$
|
||||||
|
@ -1589,7 +1832,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ORDER BY tags.priority DESC
|
ORDER BY tags.priority DESC
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION movies_photos(movie movies) RETURNS SETOF media AS $$
|
CREATE FUNCTION movies_scenes_photos(movie movies) RETURNS SETOF media AS $$
|
||||||
SELECT media.*
|
SELECT media.*
|
||||||
FROM movies_scenes
|
FROM movies_scenes
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
@ -1607,15 +1850,111 @@ exports.up = (knex) => Promise.resolve()
|
||||||
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ LANGUAGE sql STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION banners_ratio(banner banners) RETURNS numeric AS $$
|
CREATE FUNCTION series_actors(serie series) RETURNS SETOF actors AS $$
|
||||||
SELECT ROUND(banner.width::decimal / banner.height::decimal, 2);
|
SELECT actors.*
|
||||||
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
LEFT JOIN
|
||||||
|
releases_actors ON releases_actors.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
actors ON actors.id = releases_actors.actor_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
AND actors.id IS NOT NULL
|
||||||
|
GROUP BY actors.id
|
||||||
|
ORDER BY actors.name, actors.gender
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION get_random_campaign() RETURNS SETOF campaigns AS $$
|
CREATE FUNCTION series_tags(serie series) RETURNS SETOF tags AS $$
|
||||||
SELECT * FROM campaigns
|
SELECT tags.*
|
||||||
ORDER BY random()
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
LEFT JOIN
|
||||||
|
releases_tags ON releases_tags.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
tags ON tags.id = releases_tags.tag_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
AND tags.id IS NOT NULL
|
||||||
|
GROUP BY tags.id
|
||||||
|
ORDER BY tags.priority DESC
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION series_scenes_photos(serie series) RETURNS SETOF media AS $$
|
||||||
|
SELECT media.*
|
||||||
|
FROM series_scenes
|
||||||
|
LEFT JOIN
|
||||||
|
releases ON releases.id = series_scenes.scene_id
|
||||||
|
INNER JOIN
|
||||||
|
releases_photos ON releases_photos.release_id = releases.id
|
||||||
|
LEFT JOIN
|
||||||
|
media ON media.id = releases_photos.media_id
|
||||||
|
WHERE series_scenes.serie_id = serie.id
|
||||||
|
GROUP BY media.id
|
||||||
|
ORDER BY media.index ASC
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
||||||
|
banner_id, url, entity_id, affiliate_id, parent_id
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
campaigns.*, entities.parent_id as parent_id
|
||||||
|
FROM campaigns
|
||||||
|
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
||||||
|
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
||||||
|
WHERE banner_id IS NOT NULL
|
||||||
|
AND ratio >= min_ratio
|
||||||
|
AND ratio <= max_ratio
|
||||||
|
ORDER BY RANDOM()
|
||||||
|
) random_campaigns
|
||||||
|
) random_banners
|
||||||
|
ORDER BY RANDOM()
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
`);
|
||||||
|
})
|
||||||
|
// VIEWS AND COMMENTS
|
||||||
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
|
// allow vim fold
|
||||||
|
return knex.raw(`
|
||||||
|
CREATE MATERIALIZED VIEW releases_not_showcased AS (
|
||||||
|
SELECT releases.id AS release_id FROM releases
|
||||||
|
LEFT JOIN entities AS channels ON channels.id = releases.entity_id
|
||||||
|
LEFT JOIN entities AS studios ON studios.id = releases.studio_id
|
||||||
|
LEFT JOIN entities AS networks ON networks.id = channels.parent_id
|
||||||
|
WHERE (studios.showcased = false)
|
||||||
|
OR (channels.showcased = false AND studios.showcased IS NOT true)
|
||||||
|
OR (networks.showcased = false AND channels.showcased IS NOT true AND studios.showcased IS NOT true)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX ON releases_not_showcased (release_id);
|
||||||
|
COMMENT ON MATERIALIZED VIEW releases_not_showcased IS E'@foreignKey (release_id) references releases (id)';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN users.password IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.email IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.email_verified IS E'@omit';
|
||||||
|
COMMENT ON COLUMN users.abilities IS E'@omit';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
||||||
|
COMMENT ON COLUMN actors.penis_girth IS E'@omit read,update,create,delete,all,many';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION entities_scenes IS E'@sortable';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION actors_tags IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_channels IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_entities IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_movies IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// POLICIES
|
// POLICIES
|
||||||
|
@ -1632,6 +1971,7 @@ exports.up = (knex) => Promise.resolve()
|
||||||
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_movies ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_movies ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE stashes_series ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
||||||
CREATE POLICY stashes_policy_update ON stashes FOR UPDATE USING (stashes.public OR stashes.user_id = current_user_id());
|
CREATE POLICY stashes_policy_update ON stashes FOR UPDATE USING (stashes.public OR stashes.user_id = current_user_id());
|
||||||
|
@ -1662,6 +2002,14 @@ exports.up = (knex) => Promise.resolve()
|
||||||
AND (stashes.user_id = current_user_id() OR stashes.public)
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
CREATE POLICY stashes_policy ON stashes_series
|
||||||
|
USING (EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM stashes
|
||||||
|
WHERE stashes.id = stashes_series.stash_id
|
||||||
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
|
));
|
||||||
|
|
||||||
ALTER TABLE alerts ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE alerts_tags ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts_tags ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE alerts_scenes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE alerts_scenes ENABLE ROW LEVEL SECURITY;
|
||||||
|
@ -1729,33 +2077,6 @@ exports.up = (knex) => Promise.resolve()
|
||||||
`, {
|
`, {
|
||||||
visitor: knex.raw(config.database.query.user),
|
visitor: knex.raw(config.database.query.user),
|
||||||
});
|
});
|
||||||
})
|
|
||||||
// VIEWS AND COMMENTS
|
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
|
||||||
// allow vim fold
|
|
||||||
return knex.raw(`
|
|
||||||
COMMENT ON COLUMN users.password IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.email IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.email_verified IS E'@omit';
|
|
||||||
COMMENT ON COLUMN users.abilities IS E'@omit';
|
|
||||||
|
|
||||||
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
|
||||||
COMMENT ON COLUMN actors.penis_girth IS E'@omit read,update,create,delete,all,many';
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION actors_tags IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_channels IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_entities IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_movies IS E'@sortable';
|
|
||||||
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
|
@ -1776,8 +2097,17 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_covers CASCADE;
|
DROP TABLE IF EXISTS movies_covers CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_posters CASCADE;
|
DROP TABLE IF EXISTS movies_posters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS movies_photos CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_trailers CASCADE;
|
DROP TABLE IF EXISTS movies_trailers CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS stashes_series CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_scenes CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_trailers CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_posters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_covers CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_photos CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series_search CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS clips_tags CASCADE;
|
DROP TABLE IF EXISTS clips_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS clips_posters CASCADE;
|
DROP TABLE IF EXISTS clips_posters CASCADE;
|
||||||
DROP TABLE IF EXISTS clips_photos CASCADE;
|
DROP TABLE IF EXISTS clips_photos CASCADE;
|
||||||
|
@ -1789,6 +2119,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS banners_tags CASCADE;
|
DROP TABLE IF EXISTS banners_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS banners CASCADE;
|
DROP TABLE IF EXISTS banners CASCADE;
|
||||||
DROP TABLE IF EXISTS campaigns CASCADE;
|
DROP TABLE IF EXISTS campaigns CASCADE;
|
||||||
|
DROP TABLE IF EXISTS random_campaign CASCADE;
|
||||||
DROP TABLE IF EXISTS affiliates CASCADE;
|
DROP TABLE IF EXISTS affiliates CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS batches CASCADE;
|
DROP TABLE IF EXISTS batches CASCADE;
|
||||||
|
@ -1812,6 +2143,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS movies CASCADE;
|
DROP TABLE IF EXISTS movies CASCADE;
|
||||||
DROP TABLE IF EXISTS clips CASCADE;
|
DROP TABLE IF EXISTS clips CASCADE;
|
||||||
DROP TABLE IF EXISTS chapters CASCADE;
|
DROP TABLE IF EXISTS chapters CASCADE;
|
||||||
|
DROP TABLE IF EXISTS series CASCADE;
|
||||||
DROP TABLE IF EXISTS releases CASCADE;
|
DROP TABLE IF EXISTS releases CASCADE;
|
||||||
DROP TABLE IF EXISTS actors CASCADE;
|
DROP TABLE IF EXISTS actors CASCADE;
|
||||||
DROP TABLE IF EXISTS tags CASCADE;
|
DROP TABLE IF EXISTS tags CASCADE;
|
||||||
|
@ -1853,6 +2185,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
DROP FUNCTION IF EXISTS get_random_sfw_media_id;
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS releases_is_new;
|
DROP FUNCTION IF EXISTS releases_is_new;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scenes;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scene_total;
|
||||||
|
DROP FUNCTION IF EXISTS entities_scene_tags;
|
||||||
DROP FUNCTION IF EXISTS actors_tags;
|
DROP FUNCTION IF EXISTS actors_tags;
|
||||||
DROP FUNCTION IF EXISTS actors_channels;
|
DROP FUNCTION IF EXISTS actors_channels;
|
||||||
DROP FUNCTION IF EXISTS actors_actors;
|
DROP FUNCTION IF EXISTS actors_actors;
|
||||||
|
@ -1860,7 +2195,11 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS movies_actors;
|
DROP FUNCTION IF EXISTS movies_actors;
|
||||||
DROP FUNCTION IF EXISTS movies_tags;
|
DROP FUNCTION IF EXISTS movies_tags;
|
||||||
DROP FUNCTION IF EXISTS movies_photos;
|
DROP FUNCTION IF EXISTS movies_scenes_photos;
|
||||||
|
|
||||||
|
DROP FUNCTION IF EXISTS series_actors;
|
||||||
|
DROP FUNCTION IF EXISTS series_tags;
|
||||||
|
DROP FUNCTION IF EXISTS series_scenes_photos;
|
||||||
|
|
||||||
DROP POLICY IF EXISTS stashes_policy ON stashes;
|
DROP POLICY IF EXISTS stashes_policy ON stashes;
|
||||||
DROP POLICY IF EXISTS stashes_policy ON stashes_scenes;
|
DROP POLICY IF EXISTS stashes_policy ON stashes_scenes;
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE FUNCTION entities_scenes(entity entities) RETURNS SETOF releases AS $$
|
|
||||||
WITH RECURSIVE children AS (
|
|
||||||
SELECT entities.id
|
|
||||||
FROM entities
|
|
||||||
WHERE entities.id = entity.id
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT entities.id
|
|
||||||
FROM entities
|
|
||||||
INNER JOIN children ON children.id = entities.parent_id
|
|
||||||
)
|
|
||||||
|
|
||||||
SELECT releases FROM releases
|
|
||||||
INNER JOIN children ON children.id = releases.entity_id
|
|
||||||
|
|
||||||
UNION
|
|
||||||
|
|
||||||
SELECT releases FROM releases
|
|
||||||
INNER JOIN children ON children.id = releases.studio_id;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION entities_scenes IS E'@sortable';
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scenes;
|
|
||||||
`);
|
|
|
@ -1,15 +0,0 @@
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
|
||||||
table.index('release_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
|
||||||
table.index('scene_id');
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
|
||||||
table.dropIndex('release_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
|
||||||
table.dropIndex('scene_id');
|
|
||||||
}));
|
|
|
@ -1,11 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS bigint AS $$
|
|
||||||
SELECT COUNT(id)
|
|
||||||
FROM releases
|
|
||||||
WHERE releases.entity_id = entity.id;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scene_total;
|
|
||||||
`);
|
|
|
@ -1,23 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE FUNCTION entities_scene_tags(entity entities, selectable_tags text[]) RETURNS SETOF tags AS $$
|
|
||||||
SELECT tags.*
|
|
||||||
FROM releases
|
|
||||||
LEFT JOIN
|
|
||||||
releases_tags ON releases_tags.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
tags ON tags.id = releases_tags.tag_id
|
|
||||||
WHERE
|
|
||||||
releases.entity_id = entity.id
|
|
||||||
AND
|
|
||||||
CASE WHEN array_length(selectable_tags, 1) IS NOT NULL
|
|
||||||
THEN tags.slug = ANY(selectable_tags)
|
|
||||||
ELSE true
|
|
||||||
END
|
|
||||||
GROUP BY tags.id
|
|
||||||
ORDER BY tags.name;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS entities_scene_tags;
|
|
||||||
`);
|
|
|
@ -1,215 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.createTable('series', (table) => {
|
|
||||||
table.increments('id', 16);
|
|
||||||
|
|
||||||
table.integer('entity_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities')
|
|
||||||
.notNullable();
|
|
||||||
|
|
||||||
table.integer('studio_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
|
|
||||||
table.text('entry_id');
|
|
||||||
table.unique(['entity_id', 'entry_id']);
|
|
||||||
|
|
||||||
table.text('url', 1000);
|
|
||||||
table.text('title');
|
|
||||||
table.text('slug');
|
|
||||||
|
|
||||||
table.timestamp('date');
|
|
||||||
table.index('date');
|
|
||||||
|
|
||||||
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
|
||||||
.defaultTo('day');
|
|
||||||
|
|
||||||
table.text('description');
|
|
||||||
|
|
||||||
table.boolean('deep');
|
|
||||||
table.text('deep_url', 1000);
|
|
||||||
|
|
||||||
table.text('comment');
|
|
||||||
|
|
||||||
table.integer('created_batch_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('batches')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('updated_batch_id', 12)
|
|
||||||
.references('id')
|
|
||||||
.inTable('batches')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_scenes', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('scene_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('releases')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'scene_id']);
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_trailers', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.unique()
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_posters', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique('serie_id');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_covers', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_search', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('stashes_series', (table) => {
|
|
||||||
table.integer('stash_id')
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('stashes')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.integer('serie_id')
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.unique(['stash_id', 'serie_id']);
|
|
||||||
|
|
||||||
table.string('comment');
|
|
||||||
|
|
||||||
table.datetime('created_at')
|
|
||||||
.notNullable()
|
|
||||||
.defaultTo(knex.fn.now());
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER TABLE series_search ADD COLUMN document tsvector;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX series_search_unique ON series_search (serie_id);
|
|
||||||
CREATE INDEX series_search_index ON series_search USING GIN (document);
|
|
||||||
|
|
||||||
CREATE FUNCTION series_actors(serie series) RETURNS SETOF actors AS $$
|
|
||||||
SELECT actors.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
LEFT JOIN
|
|
||||||
releases_actors ON releases_actors.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
actors ON actors.id = releases_actors.actor_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
AND actors.id IS NOT NULL
|
|
||||||
GROUP BY actors.id
|
|
||||||
ORDER BY actors.name, actors.gender
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
CREATE FUNCTION series_tags(serie series) RETURNS SETOF tags AS $$
|
|
||||||
SELECT tags.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
LEFT JOIN
|
|
||||||
releases_tags ON releases_tags.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
tags ON tags.id = releases_tags.tag_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
AND tags.id IS NOT NULL
|
|
||||||
GROUP BY tags.id
|
|
||||||
ORDER BY tags.priority DESC
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
CREATE FUNCTION series_photos(serie series) RETURNS SETOF media AS $$
|
|
||||||
SELECT media.*
|
|
||||||
FROM series_scenes
|
|
||||||
LEFT JOIN
|
|
||||||
releases ON releases.id = series_scenes.scene_id
|
|
||||||
INNER JOIN
|
|
||||||
releases_photos ON releases_photos.release_id = releases.id
|
|
||||||
LEFT JOIN
|
|
||||||
media ON media.id = releases_photos.media_id
|
|
||||||
WHERE series_scenes.serie_id = serie.id
|
|
||||||
GROUP BY media.id
|
|
||||||
ORDER BY media.index ASC
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
|
|
||||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
|
|
||||||
|
|
||||||
ALTER TABLE stashes_series ENABLE ROW LEVEL SECURITY;
|
|
||||||
|
|
||||||
CREATE POLICY stashes_policy ON stashes_series
|
|
||||||
USING (EXISTS (
|
|
||||||
SELECT *
|
|
||||||
FROM stashes
|
|
||||||
WHERE stashes.id = stashes_series.stash_id
|
|
||||||
AND (stashes.user_id = current_user_id() OR stashes.public)
|
|
||||||
));
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS series_actors;
|
|
||||||
DROP FUNCTION IF EXISTS series_tags;
|
|
||||||
DROP FUNCTION IF EXISTS series_photos;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS stashes_series CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_scenes CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_trailers CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_posters CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_covers CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_search CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series CASCADE;
|
|
||||||
`));
|
|
|
@ -1,49 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER FUNCTION movies_photos(movie movies) RENAME TO movies_scenes_photos;
|
|
||||||
ALTER FUNCTION series_photos(serie series) RENAME TO series_scenes_photos;
|
|
||||||
`))
|
|
||||||
.then(() => knex.schema.createTable('movies_photos', (table) => {
|
|
||||||
table.integer('movie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('movies')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['movie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.schema.createTable('series_photos', (table) => {
|
|
||||||
table.integer('serie_id', 16)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('series')
|
|
||||||
.onDelete('cascade');
|
|
||||||
|
|
||||||
table.text('media_id', 21)
|
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
|
||||||
.inTable('media');
|
|
||||||
|
|
||||||
table.unique(['serie_id', 'media_id']);
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO :visitor;
|
|
||||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :visitor;
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
}));
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP TABLE IF EXISTS movies_photos CASCADE;
|
|
||||||
DROP TABLE IF EXISTS series_photos CASCADE;
|
|
||||||
|
|
||||||
ALTER FUNCTION movies_scenes_photos(movie movies) RENAME TO movies_photos;
|
|
||||||
ALTER FUNCTION series_scenes_photos(serie series) RENAME TO series_photos;
|
|
||||||
`);
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('releases', (table) => {
|
|
||||||
table.specificType('qualities', 'text[]');
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('releases', (table) => {
|
|
||||||
table.dropColumn('qualities');
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('users', (table) => {
|
|
||||||
table.datetime('last_login');
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('users', (table) => {
|
|
||||||
table.dropColumn('last_login');
|
|
||||||
});
|
|
|
@ -1,58 +0,0 @@
|
||||||
exports.up = async (knex) => Promise.resolve()
|
|
||||||
.then(() => knex.schema.createTable('random_campaign', (table) => {
|
|
||||||
table.text('banner_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('banners');
|
|
||||||
|
|
||||||
table.text('url');
|
|
||||||
|
|
||||||
table.integer('entity_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
|
|
||||||
table.string('affiliate_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('affiliates');
|
|
||||||
|
|
||||||
table.integer('parent_id')
|
|
||||||
.references('id')
|
|
||||||
.inTable('entities');
|
|
||||||
}))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
ALTER TABLE banners ADD COLUMN ratio numeric GENERATED ALWAYS AS (ROUND(width::decimal/ height::decimal, 2)) STORED;
|
|
||||||
`))
|
|
||||||
.then(() => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS get_random_campaign;
|
|
||||||
DROP FUNCTION IF EXISTS banners_ratio;
|
|
||||||
|
|
||||||
CREATE FUNCTION get_random_campaign(min_ratio decimal default 0, max_ratio decimal default 1000.0) RETURNS random_campaign AS $$
|
|
||||||
SELECT * FROM (
|
|
||||||
SELECT DISTINCT ON (CASE WHEN parent_id IS NOT NULL THEN parent_id ELSE entity_id END)
|
|
||||||
banner_id, url, entity_id, affiliate_id, parent_id
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
campaigns.*, entities.parent_id as parent_id
|
|
||||||
FROM campaigns
|
|
||||||
LEFT JOIN entities ON entities.id = campaigns.entity_id
|
|
||||||
LEFT JOIN banners ON banners.id = campaigns.banner_id
|
|
||||||
WHERE banner_id IS NOT NULL
|
|
||||||
AND ratio >= min_ratio
|
|
||||||
AND ratio <= max_ratio
|
|
||||||
ORDER BY RANDOM()
|
|
||||||
) random_campaigns
|
|
||||||
) random_banners
|
|
||||||
ORDER BY RANDOM()
|
|
||||||
LIMIT 1;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`));
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP FUNCTION IF EXISTS get_random_campaign;
|
|
||||||
DROP TABLE IF EXISTS random_campaign;
|
|
||||||
|
|
||||||
ALTER TABLE banners DROP COLUMN ratio;
|
|
||||||
|
|
||||||
CREATE FUNCTION banners_ratio(banner banners) RETURNS numeric AS $$
|
|
||||||
SELECT ROUND(banner.width::decimal / banner.height::decimal, 2);
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
`);
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => knex.schema.alterTable('entities', (table) => {
|
|
||||||
table.boolean('showcased');
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.schema.alterTable('entities', (table) => {
|
|
||||||
table.dropColumn('showcased');
|
|
||||||
});
|
|
|
@ -1,22 +0,0 @@
|
||||||
const config = require('config');
|
|
||||||
|
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE VIEW releases_not_showcased AS (
|
|
||||||
SELECT releases.id AS release_id FROM releases
|
|
||||||
LEFT JOIN entities AS channels ON channels.id = releases.entity_id
|
|
||||||
LEFT JOIN entities AS studios ON studios.id = releases.studio_id
|
|
||||||
LEFT JOIN entities AS networks ON networks .id = entities.parent_id
|
|
||||||
WHERE (studios.showcased = false)
|
|
||||||
OR (channel.showcased = false AND studios.showcased IS NOT true)
|
|
||||||
OR (network.showcased = false AND channel.showcased IS NOT true AND studios.showcased IS NOT true)
|
|
||||||
);
|
|
||||||
|
|
||||||
COMMENT ON VIEW releases_not_showcased IS E'@foreignKey (release_id) references releases (id)';
|
|
||||||
GRANT SELECT ON releases_not_showcased TO :visitor;
|
|
||||||
`, {
|
|
||||||
visitor: knex.raw(config.database.query.user),
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP VIEW IF EXISTS releases_not_showcased;
|
|
||||||
`);
|
|
|
@ -1,19 +0,0 @@
|
||||||
exports.up = async (knex) => {
|
|
||||||
await knex.schema.alterTable('stashes', (table) => {
|
|
||||||
table.unique(['user_id', 'slug']);
|
|
||||||
});
|
|
||||||
|
|
||||||
await knex.raw(`
|
|
||||||
CREATE UNIQUE INDEX unique_primary ON stashes (user_id, "primary") WHERE ("primary" = TRUE);
|
|
||||||
`);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async (knex) => {
|
|
||||||
await knex.schema.alterTable('stashes', (table) => {
|
|
||||||
table.dropUnique(['user_id', 'slug']);
|
|
||||||
});
|
|
||||||
|
|
||||||
await knex.raw(`
|
|
||||||
DROP INDEX unique_primary;
|
|
||||||
`);
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
exports.up = async (knex) => {
|
|
||||||
await knex.raw('CREATE INDEX releases_tag_index ON releases_tags(tag_id);');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async (knex) => {
|
|
||||||
await knex.raw('DROP INDEX releases_tag_index;');
|
|
||||||
};
|
|
|
@ -1,38 +0,0 @@
|
||||||
exports.up = async (knex) => {
|
|
||||||
await knex.raw(`
|
|
||||||
CREATE OR REPLACE FUNCTION curate_search_query(query text) RETURNS tsquery AS $$
|
|
||||||
SELECT to_tsquery('english', array_to_string(array(SELECT * FROM regexp_matches(query, '[A-Za-zÀ-ÖØ-öø-ÿ0-9]+', 'g')), '|'));
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
|
||||||
SELECT results.release_id, ts_rank(results.document::tsvector, curate_search_query(query)) as rank
|
|
||||||
FROM (
|
|
||||||
SELECT releases_search.release_id, document
|
|
||||||
FROM releases_search
|
|
||||||
WHERE document::tsvector @@ curate_search_query(query)
|
|
||||||
) AS results
|
|
||||||
ORDER BY rank DESC;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
|
||||||
`);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async (knex) => {
|
|
||||||
await knex.raw(`
|
|
||||||
CREATE OR REPLACE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
|
||||||
SELECT releases.id, ranks.rank FROM (
|
|
||||||
SELECT
|
|
||||||
releases_search.release_id,
|
|
||||||
ts_rank(releases_search.document, to_tsquery('english', array_to_string(array(SELECT * FROM regexp_matches(query, '[A-Za-zÀ-ÖØ-öø-ÿ0-9]+', 'g')), '|'))) AS rank
|
|
||||||
FROM releases_search
|
|
||||||
) ranks
|
|
||||||
LEFT JOIN releases ON releases.id = ranks.release_id
|
|
||||||
WHERE ranks.rank > 0
|
|
||||||
ORDER BY ranks.rank DESC;
|
|
||||||
$$ LANGUAGE SQL STABLE;
|
|
||||||
|
|
||||||
DROP FUNCTION curate_search_query;
|
|
||||||
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
|
||||||
`);
|
|
||||||
};
|
|
|
@ -1,25 +0,0 @@
|
||||||
exports.up = async (knex) => knex.raw(`
|
|
||||||
CREATE MATERIALIZED VIEW entities_stats
|
|
||||||
AS
|
|
||||||
WITH RECURSIVE relations AS (
|
|
||||||
SELECT entities.id, entities.parent_id, count(releases.id) AS releases_count, count(releases.id) AS total_count
|
|
||||||
FROM entities
|
|
||||||
LEFT JOIN releases ON releases.entity_id = entities.id
|
|
||||||
GROUP BY entities.id
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
|
|
||||||
SELECT entities.id AS entity_id, count(releases.id) AS releases_count, count(releases.id) + relations.total_count AS total_count
|
|
||||||
FROM entities
|
|
||||||
INNER JOIN relations ON relations.id = entities.parent_id
|
|
||||||
LEFT JOIN releases ON releases.entity_id = entities.id
|
|
||||||
GROUP BY entities.id
|
|
||||||
)
|
|
||||||
|
|
||||||
SELECT relations.id AS entity_id, relations.releases_count
|
|
||||||
FROM relations;
|
|
||||||
`);
|
|
||||||
|
|
||||||
exports.down = async (knex) => knex.raw(`
|
|
||||||
DROP MATERIALIZED VIEW entities_stats;
|
|
||||||
`);
|
|
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
|
@ -269,6 +269,8 @@ async function updateSceneSearch(releaseIds) {
|
||||||
if (documents.rows?.length > 0) {
|
if (documents.rows?.length > 0) {
|
||||||
await bulkInsert('releases_search', documents.rows, ['release_id']);
|
await bulkInsert('releases_search', documents.rows, ['release_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await knex.raw('REFRESH MATERIALIZED VIEW CONCURRENTLY releases_not_showcased;');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storeChapters(releases) {
|
async function storeChapters(releases) {
|
||||||
|
|
Loading…
Reference in New Issue