diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 696d7639..8762dd60 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -17,6 +17,184 @@ exports.up = knex => Promise.resolve() table.integer('priority', 2) .defaultTo(0); })) + .then(() => knex.schema.createTable('media', (table) => { + table.increments('id', 16); + + table.string('path'); + table.string('thumbnail'); + table.integer('index'); + table.string('mime'); + + table.string('type'); + table.string('quality', 6); + + table.string('hash'); + table.text('comment'); + table.string('source', 1000); + + table.unique('hash'); + table.unique('source'); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('tags_groups', (table) => { + table.increments('id', 12); + + table.string('name', 32); + table.text('description'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('tags', (table) => { + table.increments('id', 12); + table.string('name'); + + table.text('description'); + + table.integer('priority', 2) + .defaultTo(0); + + table.integer('group_id', 12) + .references('id') + .inTable('tags_groups'); + + table.integer('alias_for', 12) + .references('id') + .inTable('tags'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('tags_posters', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('tag_id'); + })) + .then(() => knex.schema.createTable('tags_photos', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['tag_id', 'media_id']); + })) + .then(() => knex.schema.createTable('networks', (table) => { + table.increments('id', 12); + + table.string('name'); + table.string('url'); + table.text('description'); + table.string('parameters'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('networks_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.unique(['url', 'network_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('sites', (table) => { + table.increments('id', 12); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.string('name'); + table.string('url'); + table.text('description'); + table.string('parameters'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('sites_tags', (table) => { + table.integer('tag_id', 12) + .notNullable() + .references('id') + .inTable('tags'); + + table.integer('site_id', 12) + .notNullable() + .references('id') + .inTable('sites'); + + table.unique(['tag_id', 'site_id']); + })) + .then(() => knex.schema.createTable('sites_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('site_id', 12) + .notNullable() + .references('id') + .inTable('sites'); + + table.unique(['url', 'site_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) + .then(() => knex.schema.createTable('studios', (table) => { + table.increments('id', 12); + + table.integer('network_id', 12) + .notNullable() + .references('id') + .inTable('networks'); + + table.string('name'); + table.string('url'); + table.text('description'); + + table.string('slug', 32) + .unique(); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) .then(() => knex.schema.createTable('actors', (table) => { table.increments('id', 12); @@ -70,6 +248,48 @@ exports.up = knex => Promise.resolve() table.datetime('scraped_at'); table.boolean('scrape_success'); })) + .then(() => knex.schema.createTable('actors_avatars', (table) => { + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('actor_id'); + })) + .then(() => knex.schema.createTable('actors_photos', (table) => { + table.integer('actor_id', 12) + .notNullable() + .references('id') + .inTable('actors'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['actor_id', 'media_id']); + })) + .then(() => knex.schema.createTable('actors_social', (table) => { + table.increments('id', 16); + + table.string('url'); + table.string('platform'); + + table.integer('actor_id', 8) + .notNullable() + .references('id') + .inTable('actors'); + + table.unique(['url', 'actor_id']); + + table.datetime('created_at') + .defaultTo(knex.fn.now()); + })) .then(() => knex.schema.createTable('directors', (table) => { table.increments('id', 12); @@ -84,92 +304,6 @@ exports.up = knex => Promise.resolve() table.datetime('created_at') .defaultTo(knex.fn.now()); })) - .then(() => knex.schema.createTable('tags_groups', (table) => { - table.increments('id', 12); - - table.string('name', 32); - table.text('description'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('tags', (table) => { - table.increments('id', 12); - table.string('name'); - - table.text('description'); - - table.integer('priority', 2) - .defaultTo(0); - - table.integer('group_id', 12) - .references('id') - .inTable('tags_groups'); - - table.integer('alias_for', 12) - .references('id') - .inTable('tags'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('networks', (table) => { - table.increments('id', 12); - - table.string('name'); - table.string('url'); - table.text('description'); - table.string('parameters'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('sites', (table) => { - table.increments('id', 12); - - table.integer('network_id', 12) - .notNullable() - .references('id') - .inTable('networks'); - - table.string('name'); - table.string('url'); - table.text('description'); - table.string('parameters'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('studios', (table) => { - table.increments('id', 12); - - table.integer('network_id', 12) - .notNullable() - .references('id') - .inTable('networks'); - - table.string('name'); - table.string('url'); - table.text('description'); - - table.string('slug', 32) - .unique(); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) .then(() => knex.schema.createTable('releases', (table) => { table.increments('id', 16); @@ -193,10 +327,6 @@ exports.up = knex => Promise.resolve() table.date('date'); table.text('description'); - table.integer('director', 12) - .references('id') - .inTable('directors'); - table.integer('duration') .unsigned(); @@ -209,48 +339,7 @@ exports.up = knex => Promise.resolve() table.datetime('created_at') .defaultTo(knex.fn.now()); })) - .then(() => knex.schema.createTable('media', (table) => { - table.increments('id', 16); - - table.string('path'); - table.string('thumbnail'); - table.integer('index'); - table.string('mime'); - - table.string('domain'); - table.integer('target_id', 16); - - table.json('target'); - - table.string('role'); - table.string('quality', 6); - - table.string('hash'); - table.text('comment'); - table.string('source', 1000); - - table.unique(['domain', 'target_id', 'role', 'hash']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('social', (table) => { - table.increments('id', 16); - - table.string('url'); - table.string('platform'); - - table.string('domain'); - table.integer('target_id', 16); - - table.unique(['url', 'domain', 'target_id']); - - table.datetime('created_at') - .defaultTo(knex.fn.now()); - })) - .then(() => knex.schema.createTable('actors_associated', (table) => { - // table.increments('id', 16); - + .then(() => knex.schema.createTable('releases_actors', (table) => { table.integer('release_id', 16) .notNullable() .references('id') @@ -263,9 +352,7 @@ exports.up = knex => Promise.resolve() table.unique(['release_id', 'actor_id']); })) - .then(() => knex.schema.createTable('directors_associated', (table) => { - table.increments('id', 16); - + .then(() => knex.schema.createTable('releases_directors', (table) => { table.integer('release_id', 16) .notNullable() .references('id') @@ -278,65 +365,85 @@ exports.up = knex => Promise.resolve() table.unique(['release_id', 'director_id']); })) - .then(() => knex.schema.createTable('tags_associated', (table) => { + .then(() => knex.schema.createTable('releases_posters', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique('release_id'); + })) + .then(() => knex.schema.createTable('releases_photos', (table) => { + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); + + table.integer('media_id', 16) + .notNullable() + .references('id') + .inTable('media'); + + table.unique(['release_id', 'media_id']); + })) + .then(() => knex.schema.createTable('releases_tags', (table) => { table.integer('tag_id', 12) .notNullable() .references('id') .inTable('tags'); - table.string('domain'); - table.integer('target_id', 16); + table.integer('release_id', 16) + .notNullable() + .references('id') + .inTable('releases'); - table.unique(['domain', 'tag_id', 'target_id']); + table.unique(['tag_id', 'release_id']); })) .then(() => knex.raw(` - CREATE VIEW releases_media AS SELECT * FROM media WHERE domain = 'releases'; - CREATE VIEW actors_media AS SELECT * FROM media WHERE domain = 'actors'; - CREATE VIEW tags_media AS SELECT * FROM media WHERE domain = 'tags'; - - CREATE VIEW releases_tags AS SELECT tags_associated.*,tags.slug FROM tags_associated LEFT JOIN tags ON tags_associated.tag_id = tags.id WHERE domain = 'releases'; - /* used for sorting release actors and tags */ - CREATE VIEW releases_actors AS SELECT actors_associated.*,actors.gender,actors.birthdate FROM actors_associated LEFT JOIN actors ON actors_associated.actor_id = actors.id; - - CREATE VIEW actors_social AS SELECT * FROM social WHERE domain = 'actors'; - - COMMENT ON VIEW releases_media IS E'@foreignKey (target_id) references releases (id)|@fieldName releaseMedia'; - COMMENT ON VIEW actors_media IS E'@foreignKey (target_id) references actors (id)|@fieldName actorMedia'; - COMMENT ON VIEW tags_media IS E'@foreignKey (target_id) references tags (id)|@fieldName tagMedia'; - - COMMENT ON VIEW actors_social IS E'@foreignKey (target_id) references actors (id)|@fieldName actorSocial'; - - COMMENT ON VIEW releases_tags IS E'@foreignKey (target_id) references releases (id)|@fieldName tagRelease\n@foreignKey (tag_id) references tags (id)|@fieldName releaseTag'; - - /* restore foreign keys in view, used for sorting release actors */ - COMMENT ON VIEW releases_actors IS E'@foreignKey (release_id) references releases (id)|@fieldName actorRelease\n@foreignKey (actor_id) references actors (id)|@fieldName releaseActor'; - + /* allow conversion resolver to be added for height and weight */ 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'; + + /* + create function releases_by_tag_slugs(slugs text[]) returns setof releases as $$ + select distinct on (releases.id) releases.* from releases + join releases_tags on (releases_tags.release_id = releases.id) + join tags on (releases_tags.tag_id = tags.id) + where tags.slug = ANY($1); + $$ language sql stable + */ `)); -exports.down = knex => Promise.resolve() - .then(() => knex.raw(` - DROP VIEW releases_media; - DROP VIEW actors_media; - DROP VIEW tags_media; +exports.down = knex => knex.raw(` + DROP FUNCTION IF EXISTS releases_by_tag_slugs; - DROP VIEW releases_tags; - DROP VIEW releases_actors; - - DROP VIEW actors_social; - `)) - .then(() => knex.schema.dropTable('tags_associated')) - .then(() => knex.schema.dropTable('directors_associated')) - .then(() => knex.schema.dropTable('actors_associated')) - .then(() => knex.schema.dropTable('tags')) - .then(() => knex.schema.dropTable('tags_groups')) - .then(() => knex.schema.dropTable('media')) - .then(() => knex.schema.dropTable('social')) - .then(() => knex.schema.dropTable('actors')) - .then(() => knex.schema.dropTable('releases')) - .then(() => knex.schema.dropTable('sites')) - .then(() => knex.schema.dropTable('studios')) - .then(() => knex.schema.dropTable('directors')) - .then(() => knex.schema.dropTable('networks')) - .then(() => knex.schema.dropTable('countries')); + DROP TABLE IF EXISTS releases_actors CASCADE; + DROP TABLE IF EXISTS releases_directors CASCADE; + DROP TABLE IF EXISTS releases_posters CASCADE; + DROP TABLE IF EXISTS releases_photos CASCADE; + DROP TABLE IF EXISTS releases_tags CASCADE; + DROP TABLE IF EXISTS actors_avatars CASCADE; + DROP TABLE IF EXISTS actors_photos CASCADE; + DROP TABLE IF EXISTS actors_social CASCADE; + DROP TABLE IF EXISTS sites_tags CASCADE; + DROP TABLE IF EXISTS sites_social CASCADE; + DROP TABLE IF EXISTS networks_social CASCADE; + DROP TABLE IF EXISTS tags_posters CASCADE; + DROP TABLE IF EXISTS tags_photos CASCADE; + DROP TABLE IF EXISTS releases CASCADE; + DROP TABLE IF EXISTS actors CASCADE; + DROP TABLE IF EXISTS directors CASCADE; + DROP TABLE IF EXISTS tags CASCADE; + DROP TABLE IF EXISTS tags_groups CASCADE; + DROP TABLE IF EXISTS social CASCADE; + DROP TABLE IF EXISTS sites CASCADE; + DROP TABLE IF EXISTS studios CASCADE; + DROP TABLE IF EXISTS media CASCADE; + DROP TABLE IF EXISTS countries CASCADE; + DROP TABLE IF EXISTS networks CASCADE; +`); diff --git a/seeds/00_networks.js b/seeds/00_networks.js index e737f7e7..f09bc3c2 100644 --- a/seeds/00_networks.js +++ b/seeds/00_networks.js @@ -125,10 +125,4 @@ const networks = [ ]; exports.seed = knex => Promise.resolve() - .then(async () => { - // find network IDs - const duplicates = await knex('networks').select('*'); - const duplicatesBySlug = duplicates.reduce((acc, network) => ({ ...acc, [network.slug]: network }), {}); - - return upsert('networks', networks, duplicatesBySlug, 'slug', knex); - }); + .then(async () => upsert('networks', networks, 'slug', knex)); diff --git a/seeds/01_sites.js b/seeds/01_sites.js index 39747a5f..1302df53 100644 --- a/seeds/01_sites.js +++ b/seeds/01_sites.js @@ -2428,15 +2428,10 @@ function getSites(networksMap) { /* eslint-disable max-len */ exports.seed = knex => Promise.resolve() .then(async () => { - const [duplicates, networks] = await Promise.all([ - knex('sites').select('*'), - knex('networks').select('*'), - ]); - - const duplicatesBySlug = duplicates.reduce((acc, site) => ({ ...acc, [site.slug]: site }), {}); + const networks = await knex('networks').select('*'); const networksMap = networks.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); const sites = getSites(networksMap); - return upsert('sites', sites, duplicatesBySlug, 'slug', knex); + return upsert('sites', sites, 'slug', knex); }); diff --git a/seeds/02_studios.js b/seeds/02_studios.js index 516080a0..c421f12e 100644 --- a/seeds/02_studios.js +++ b/seeds/02_studios.js @@ -1,5 +1,3 @@ -'use strict'; - const upsert = require('../src/utils/upsert'); function getStudios(networksMap) { @@ -9,133 +7,133 @@ function getStudios(networksMap) { slug: 'gonzocom', name: 'Gonzo.com', url: 'https://www.legalporno.com/studios/gonzo_com', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'giorgiograndi', name: 'Giorgio Grandi', url: 'https://www.legalporno.com/studios/giorgio-grandi', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'hardpornworld', name: 'Hard Porn World', url: 'https://www.legalporno.com/studios/hard-porn-world', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'interracialvision', name: 'Interracial Vision', url: 'https://www.legalporno.com/studios/interracial-vision', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'giorgioslab', name: 'Giorgio\'s Lab', url: 'https://www.legalporno.com/studios/giorgio--s-lab', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'americananal', name: 'American Anal', url: 'https://www.legalporno.com/studios/american-anal', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'assablanca', name: 'Assablanca', url: 'https://www.legalporno.com/studios/assablanca', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'focus', name: 'Focus', url: 'https://www.legalporno.com/studios/focus', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'analforever', name: 'Anal Forever', url: 'https://www.legalporno.com/studios/anal-forever', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'gonzoinbrazil', name: 'Gonzo in Brazil', url: 'https://www.legalporno.com/studios/gonzo-in-brazil', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'mranal', name: 'Mr Anal', url: 'https://www.legalporno.com/studios/mr-anal', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'tarrawhite', name: 'Tarra White', url: 'https://www.legalporno.com/studios/tarra-white', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'sineplexsos', name: 'Sineplex SOS', url: 'https://www.legalporno.com/studios/sineplex-sos', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'fmodels', name: 'F Models', url: 'https://www.legalporno.com/studios/f-models', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'sineplexcz', name: 'Sineplex CZ', url: 'https://www.legalporno.com/studios/sineplex-cz', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'gg', name: 'GG', url: 'https://www.legalporno.com/studios/gg', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'firstgape', name: 'First Gape', url: 'https://www.legalporno.com/studios/first-gape', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'omargalantiproductions', name: 'Omar Galanti Productions', url: 'https://www.legalporno.com/studios/omar-galanti-productions', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'norestfortheass', name: 'No Rest For The Ass', url: 'https://www.legalporno.com/studios/no-rest-for-the-ass', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'hairygonzo', name: 'Hairy Gonzo', url: 'https://www.legalporno.com/studios/hairy-gonzo', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'sineplexclassic', name: 'Sineplex Classic', url: 'https://www.legalporno.com/studios/sineplex-classic', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, { slug: 'sinemale', name: 'Sinemale', url: 'https://www.legalporno.com/studios/sinemale', - network_id: networksMap['legalporno'], + network_id: networksMap.legalporno, }, ]; } @@ -143,15 +141,10 @@ function getStudios(networksMap) { /* eslint-disable max-len */ exports.seed = knex => Promise.resolve() .then(async () => { - const [duplicates, networks] = await Promise.all([ - knex('studios').select('*'), - knex('networks').select('*'), - ]); - - const duplicatesBySlug = duplicates.reduce((acc, studio) => ({ ...acc, [studio.slug]: studio }), {}); + const networks = await knex('networks').select('*'); const networksMap = networks.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); const studios = getStudios(networksMap); - return upsert('studios', studios, duplicatesBySlug, 'slug', knex); + return upsert('studios', studios, 'slug', knex); }); diff --git a/seeds/03_tags.js b/seeds/03_tags.js index ffff0f83..d0b91e43 100644 --- a/seeds/03_tags.js +++ b/seeds/03_tags.js @@ -69,7 +69,7 @@ function getTags(groupsMap) { name: 'airtight', slug: 'airtight', alias_for: null, - description: 'Stuffing one cock in her ass, one in her pussy, and one in her mouth, filling all of her penetrable holes and sealing her airtight like a figurative balloon. In other words, simultaneously getting [double penetrated](/tag/double-penetration), and giving a [blowjob](/tag/blowjob) or getting [facefucked](/tag/facefuck). Being airtight implies being [gangbanged](/tag/gangbang).', + description: 'Stuffing one cock in her ass, one in her pussy, and one in her mouth, filling all of her penetrable holes and sealing her airtight like a figurative balloon. In other words, simultaneously getting [double penetrated](/tag/double-penetration), and giving a [blowjob](/tag/blowjob) or getting [facefucked](/tag/facefuck). Being airtight implies being [gangbanged](/tag/gangbang).', /* eslint-disable-line max-len */ priority: 9, group_id: groupsMap.penetration, }, @@ -429,7 +429,7 @@ function getTags(groupsMap) { { name: 'gangbang', slug: 'gangbang', - description: 'A group of three or more guys fucking a woman, at least two at the same time, often but not necessarily involving a [blowbang](/tag/blowbang), [double penetration](/tag/airtight) and [airtight](/tag/airtight). If she only gets fucked by one guy at a time, it might be considered a [trainbang](/tag/trainbang) instead. In a reverse gangbang, multiple women fuck one man.', + description: 'A group of three or more guys fucking a woman, at least two at the same time, often but not necessarily involving a [blowbang](/tag/blowbang), [double penetration](/tag/airtight) and [airtight](/tag/airtight). If she only gets fucked by one guy at a time, it might be considered a [trainbang](/tag/trainbang) instead. In a reverse gangbang, multiple women fuck one man.', /* eslint-disable-line max-len */ alias_for: null, priority: 9, group_id: groupsMap.group, @@ -1542,35 +1542,20 @@ function getTagAliases(tagsMap) { } exports.seed = knex => Promise.resolve() + .then(async () => upsert('tags_groups', groups, 'slug', knex)) .then(async () => { - const duplicates = await knex('tags_groups').select('*'); - const duplicatesBySlug = duplicates.reduce((acc, group) => ({ ...acc, [group.slug]: group }), {}); - - return upsert('tags_groups', groups, duplicatesBySlug, 'slug', knex); - }) - .then(async () => { - const [duplicates, groupEntries] = await Promise.all([ - knex('tags').select('*'), - knex('tags_groups').select('*'), - ]); - - const duplicatesBySlug = duplicates.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag }), {}); + const groupEntries = knex('tags').select('*'); const groupsMap = groupEntries.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); const tags = getTags(groupsMap); - return upsert('tags', tags, duplicatesBySlug, 'slug', knex); + return upsert('tags', tags, 'slug', knex); }) .then(async () => { - const [duplicates, tags] = await Promise.all([ - knex('tags').select('*').whereNotNull('alias_for'), - knex('tags').select('*').where({ alias_for: null }), - ]); - - const duplicatesByName = duplicates.reduce((acc, tag) => ({ ...acc, [tag.name]: tag }), {}); + const tags = await knex('tags').select('*').where({ alias_for: null }); const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); const tagAliases = getTagAliases(tagsMap); - return upsert('tags', tagAliases, duplicatesByName, 'name', knex); + return upsert('tags', tagAliases, 'name', knex); }); diff --git a/seeds/04_media.js b/seeds/04_media.js index 9868dc9e..d82549f7 100644 --- a/seeds/04_media.js +++ b/seeds/04_media.js @@ -1,257 +1,241 @@ const upsert = require('../src/utils/upsert'); -function getMedia(tagsMap) { - return [ - { - path: 'tags/airtight/poster.jpeg', - target_id: tagsMap.airtight, - role: 'poster', - comment: 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan', - }, - { - path: 'tags/airtight/2.jpeg', - target_id: tagsMap.airtight, - comment: 'Dakota Skye in "Dakota Goes Nuts" for ArchAngel', - }, - { - path: 'tags/airtight/1.jpeg', - target_id: tagsMap.airtight, - comment: 'Chloe Amour in "DP Masters 4" for Jules Jordan', - }, - { - path: 'tags/airtight/0.jpeg', - domain: 'tags', - target_id: tagsMap.airtight, - comment: 'Sheena Shaw in "Ass Worship 14" for Jules Jordan', - }, - { - path: 'tags/anal/poster.jpeg', - target_id: tagsMap.anal, - role: 'poster', - comment: '', - }, - { - path: 'tags/double-penetration/poster.jpeg', - target_id: tagsMap['double-penetration'], - role: 'poster', - comment: '', - }, - { - path: 'tags/double-anal/poster.jpeg', - target_id: tagsMap['double-anal'], - role: 'poster', - comment: '', - }, - { - path: 'tags/double-vaginal/poster.jpeg', - target_id: tagsMap['double-vaginal'], - role: 'poster', - comment: '', - }, - { - path: 'tags/da-tp/0.jpeg', - target_id: tagsMap['da-tp'], - role: 'poster', - comment: 'Natasha Teen in LegalPorno SZ2164', - }, - { - path: 'tags/da-tp/3.jpeg', - target_id: tagsMap['da-tp'], - role: 'photo', - comment: 'Evelina Darling in GIO294', - }, - { - path: 'tags/da-tp/1.jpeg', - target_id: tagsMap['da-tp'], - role: 'photo', - comment: 'Francys Belle in SZ1702 for LegalPorno', - }, - { - path: 'tags/da-tp/2.jpeg', - target_id: tagsMap['da-tp'], - role: 'photo', - comment: 'Angel Smalls in GIO408 for LegalPorno', - }, - { - path: 'tags/da-tp/4.jpeg', - target_id: tagsMap['da-tp'], - role: 'photo', - comment: 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno', - }, - { - path: 'tags/dv-tp/poster.jpeg', - target_id: tagsMap['dv-tp'], - role: 'poster', - comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel', - }, - { - path: 'tags/dv-tp/0.jpeg', - target_id: tagsMap['dv-tp'], - role: 'photo', - comment: 'Luna Rival in LegalPorno SZ1490', - }, - { - path: 'tags/tattoo/poster.jpeg', - target_id: tagsMap.tattoo, - role: 'poster', - comment: 'Kali Roses in "Goes All In For Anal" for Hussie Pass', - }, - { - path: 'tags/triple-anal/poster.jpeg', - target_id: tagsMap['triple-anal'], - role: 'poster', - comment: 'Kristy Black in SZ1986 for LegalPorno', - }, - { - path: 'tags/triple-anal/1.jpeg', - target_id: tagsMap['triple-anal'], - role: 'photo', - comment: 'Natasha Teen in SZ2098 for LegalPorno', - }, - { - path: 'tags/triple-anal/2.jpeg', - target_id: tagsMap['triple-anal'], - role: 'photo', - comment: 'Kira Thorn in GIO1018 for LegalPorno', - }, - { - path: 'tags/blowbang/poster.jpeg', - target_id: tagsMap.blowbang, - role: 'poster', - comment: '', - }, - { - path: 'tags/gangbang/poster.jpeg', - target_id: tagsMap.gangbang, - role: 'poster', - comment: '', - }, - { - path: 'tags/gangbang/1.jpeg', - target_id: tagsMap.gangbang, - role: 'photo', - comment: 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall for Puritan No. 10, 1984. This photo pushed the boundaries of pornography at the time, as depicting a woman \'fully occupied\' was unheard of.', - }, - { - path: 'tags/gangbang/2.jpeg', - target_id: tagsMap.gangbang, - role: 'photo', - comment: 'Riley Reid\'s double anal in "The Gangbang of Riley Reid" for Jules Jordan', - }, - { - path: 'tags/gangbang/3.jpeg', - target_id: tagsMap.gangbang, - role: 'photo', - comment: 'Kelsi Monroe in "Brazzers House 2, Day 2" for Brazzers', - }, - { - path: 'tags/mff/poster.jpeg', - target_id: tagsMap.mff, - role: 'poster', - comment: '', - }, - { - path: 'tags/mfm/poster.jpeg', - target_id: tagsMap.mfm, - role: 'poster', - comment: '', - }, - { - path: 'tags/orgy/poster.jpeg', - target_id: tagsMap.orgy, - role: 'poster', - comment: '', - }, - { - path: 'tags/asian/poster.jpeg', - target_id: tagsMap.asian, - role: 'poster', - comment: '', - }, - { - path: 'tags/caucasian/poster.jpeg', - target_id: tagsMap.caucasian, - role: 'poster', - comment: '', - }, - { - path: 'tags/ebony/poster.jpeg', - target_id: tagsMap.ebony, - role: 'poster', - comment: '', - }, - { - path: 'tags/latina/poster.jpeg', - target_id: tagsMap.latina, - role: 'poster', - comment: '', - }, - { - path: 'tags/interracial/poster.jpeg', - target_id: tagsMap.interracial, - role: 'poster', - comment: '', - }, - { - path: 'tags/facial/poster.jpeg', - target_id: tagsMap.facial, - role: 'poster', - comment: '', - }, - { - path: 'tags/bukkake/poster.jpeg', - target_id: tagsMap.bukkake, - role: 'poster', - comment: '', - }, - { - path: 'tags/swallowing/poster.jpeg', - target_id: tagsMap.swallowing, - role: 'poster', - comment: '', - }, - { - path: 'tags/creampie/poster.jpeg', - target_id: tagsMap.creampie, - role: 'poster', - comment: '', - }, - { - path: 'tags/anal-creampie/poster.jpeg', - target_id: tagsMap['anal-creampie'], - role: 'poster', - comment: '', - }, - { - path: 'tags/oral-creampie/poster.jpeg', - target_id: tagsMap['oral-creampie'], - role: 'poster', - comment: '', - }, - ] - .map((file, index) => ({ - ...file, - thumbnail: file.thumbnail || file.path.replace('.jpeg', '_thumb.jpeg'), - mime: 'image/jpeg', - index, - domain: file.domain || 'tags', - target: { [file.domain || 'tags']: file.target_id }, - role: file.role || 'photo', - })); -} +const tagPosters = [ + { + path: 'tags/airtight/poster.jpeg', + tagSlug: 'airtight', + comment: 'Jynx Maze in "Pump My Ass Full of Cum 3" for Jules Jordan', + }, + { + path: 'tags/anal/poster.jpeg', + tagSlug: 'anal', + comment: '', + }, + { + path: 'tags/double-penetration/poster.jpeg', + tagSlug: 'double-penetration', + comment: '', + }, + { + path: 'tags/double-anal/poster.jpeg', + tagSlug: 'double-anal', + comment: '', + }, + { + path: 'tags/double-vaginal/poster.jpeg', + tagSlug: 'double-vaginal', + comment: '', + }, + { + path: 'tags/tattoo/poster.jpeg', + tagSlug: 'tattoo', + comment: 'Kali Roses in "Goes All In For Anal" for Hussie Pass', + }, + { + path: 'tags/triple-anal/poster.jpeg', + tagSlug: 'triple-anal', + comment: 'Kristy Black in SZ1986 for LegalPorno', + }, + { + path: 'tags/blowbang/poster.jpeg', + tagSlug: 'blowbang', + comment: '', + }, + { + path: 'tags/gangbang/poster.jpeg', + tagSlug: 'gangbang', + comment: '', + }, + { + path: 'tags/mff/poster.jpeg', + tagSlug: 'mff', + comment: '', + }, + { + path: 'tags/mfm/poster.jpeg', + tagSlug: 'mfm', + comment: '', + }, + { + path: 'tags/orgy/poster.jpeg', + tagSlug: 'orgy', + comment: '', + }, + { + path: 'tags/asian/poster.jpeg', + tagSlug: 'asian', + comment: '', + }, + { + path: 'tags/caucasian/poster.jpeg', + tagSlug: 'caucasian', + comment: '', + }, + { + path: 'tags/ebony/poster.jpeg', + tagSlug: 'ebony', + comment: '', + }, + { + path: 'tags/latina/poster.jpeg', + tagSlug: 'latina', + comment: '', + }, + { + path: 'tags/interracial/poster.jpeg', + tagSlug: 'interracial', + comment: '', + }, + { + path: 'tags/facial/poster.jpeg', + tagSlug: 'facial', + comment: '', + }, + { + path: 'tags/bukkake/poster.jpeg', + tagSlug: 'bukkake', + comment: '', + }, + { + path: 'tags/swallowing/poster.jpeg', + tagSlug: 'swallowing', + comment: '', + }, + { + path: 'tags/creampie/poster.jpeg', + tagSlug: 'creampie', + comment: '', + }, + { + path: 'tags/anal-creampie/poster.jpeg', + tagSlug: 'anal-creampie', + comment: '', + }, + { + path: 'tags/oral-creampie/poster.jpeg', + tagSlug: 'oral-creampie', + comment: '', + }, +] + .map((file, index) => ({ + ...file, + thumbnail: file.thumbnail || file.path.replace('.jpeg', '_thumb.jpeg'), + mime: 'image/jpeg', + index, + })); + +const tagPhotos = [ + { + path: 'tags/airtight/2.jpeg', + tagSlug: 'airtight', + comment: 'Dakota Skye in "Dakota Goes Nuts" for ArchAngel', + }, + { + path: 'tags/airtight/1.jpeg', + tagSlug: 'airtight', + comment: 'Chloe Amour in "DP Masters 4" for Jules Jordan', + }, + { + path: 'tags/airtight/0.jpeg', + domain: 'tags', + tagSlug: 'airtight', + comment: 'Sheena Shaw in "Ass Worship 14" for Jules Jordan', + }, + { + path: 'tags/da-tp/0.jpeg', + tagSlug: 'da-tp', + comment: 'Natasha Teen in LegalPorno SZ2164', + }, + { + path: 'tags/da-tp/3.jpeg', + tagSlug: 'da-tp', + comment: 'Evelina Darling in GIO294', + }, + { + path: 'tags/da-tp/1.jpeg', + tagSlug: 'da-tp', + comment: 'Francys Belle in SZ1702 for LegalPorno', + }, + { + path: 'tags/da-tp/2.jpeg', + tagSlug: 'da-tp', + comment: 'Angel Smalls in GIO408 for LegalPorno', + }, + { + path: 'tags/da-tp/4.jpeg', + tagSlug: 'da-tp', + comment: 'Ninel Mojado aka Mira Cuckold in GIO063 for LegalPorno', + }, + { + path: 'tags/dv-tp/poster.jpeg', + tagSlug: 'dv-tp', + comment: 'Juelz Ventura in "Gangbanged 5" for Elegant Angel', + }, + { + path: 'tags/dv-tp/0.jpeg', + tagSlug: 'dv-tp', + comment: 'Luna Rival in LegalPorno SZ1490', + }, + { + path: 'tags/triple-anal/1.jpeg', + tagSlug: 'triple-anal', + comment: 'Natasha Teen in SZ2098 for LegalPorno', + }, + { + path: 'tags/triple-anal/2.jpeg', + tagSlug: 'triple-anal', + comment: 'Kira Thorn in GIO1018 for LegalPorno', + }, + { + path: 'tags/gangbang/1.jpeg', + tagSlug: 'gangbang', + comment: 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall for Puritan No. 10, 1984. This photo pushed the boundaries of pornography at the time, as depicting a woman \'fully occupied\' was unheard of.', + }, + { + path: 'tags/gangbang/2.jpeg', + tagSlug: 'gangbang', + comment: 'Riley Reid\'s double anal in "The Gangbang of Riley Reid" for Jules Jordan', + }, + { + path: 'tags/gangbang/3.jpeg', + tagSlug: 'gangbang', + comment: 'Kelsi Monroe in "Brazzers House 2, Day 2" for Brazzers', + }, +] + .map((file, index) => ({ + ...file, + thumbnail: file.thumbnail || file.path.replace('.jpeg', '_thumb.jpeg'), + mime: 'image/jpeg', + index, + })); /* eslint-disable max-len */ exports.seed = knex => Promise.resolve() .then(async () => { - const [duplicates, tags] = await Promise.all([ - knex('media').where('domain', 'tags'), - knex('tags').where('alias_for', null), + const tagMedia = tagPosters.concat(tagPhotos); + + const tags = await knex('tags').whereIn('slug', tagMedia.map(item => item.tagSlug)); + const { inserted, updated } = await upsert('media', tagMedia.map(({ + path, thumbnail, mime, index, comment, + }) => ({ + path, thumbnail, mime, index, comment, + })), 'path', knex); + + const tagIdsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag.id }), {}); + const mediaIdsByPath = inserted.concat(updated).reduce((acc, item) => ({ ...acc, [item.path]: item.id }), {}); + + const tagPosterEntries = tagPosters.map(poster => ({ + tag_id: tagIdsBySlug[poster.tagSlug], + media_id: mediaIdsByPath[poster.path], + })); + + const tagPhotoEntries = tagPhotos.map(photo => ({ + tag_id: tagIdsBySlug[photo.tagSlug], + media_id: mediaIdsByPath[photo.path], + })); + + return Promise.all([ + upsert('tags_posters', tagPosterEntries, 'tag_id', knex), + upsert('tags_photos', tagPhotoEntries, 'tag_id', knex), ]); - - const duplicatesByPath = duplicates.reduce((acc, file) => ({ ...acc, [file.path]: file }), {}); - const tagsMap = tags.reduce((acc, { id, slug }) => ({ ...acc, [slug]: id }), {}); - - const media = getMedia(tagsMap); - - return upsert('media', media, duplicatesByPath, 'path', knex); }); diff --git a/seeds/05_countries.js b/seeds/05_countries.js index 02a6dc13..30828120 100644 --- a/seeds/05_countries.js +++ b/seeds/05_countries.js @@ -1755,9 +1755,4 @@ const countries = [ ]; exports.seed = knex => knex('countries') - .then(async () => { - const duplicates = await knex('countries').select('*'); - const duplicatesByAlpha2 = duplicates.reduce((acc, country) => ({ ...acc, [country.alpha2]: country }), {}); - - return upsert('countries', countries, duplicatesByAlpha2, 'alpha2', knex); - }); + .then(async () => upsert('countries', countries, 'alpha2', knex)); diff --git a/src/utils/upsert.js b/src/utils/upsert.js index 93643a27..ea139174 100644 --- a/src/utils/upsert.js +++ b/src/utils/upsert.js @@ -1,29 +1,18 @@ 'use strict'; -async function upsert(table, items, duplicatesById, identifier = 'id', knex) { +async function upsert(table, items, identifier = 'id', knex) { + const duplicates = await knex(table).whereIn(identifier, items.map(item => item[identifier])); + const duplicatesByIdentifier = duplicates.reduce((acc, item) => ({ ...acc, [item[identifier]]: item }), {}); + const { insert, update } = items.reduce((acc, item) => { - if (duplicatesById[item[identifier]]) { - return { - ...acc, - update: [ - ...acc.update, - { - ...duplicatesById[item[identifier]], - ...item, - }, - ], - }; + if (duplicatesByIdentifier[item[identifier]]) { + acc.update.push(item); + return acc; } - return { - ...acc, - insert: [ - ...acc.insert, - item, - ], - }; - }, - { + acc.insert.push(item); + return acc; + }, { insert: [], update: [], }); @@ -32,13 +21,19 @@ async function upsert(table, items, duplicatesById, identifier = 'id', knex) { console.log(`${table}: Inserting ${insert.length}`); console.log(`${table}: Updating ${update.length}`); - return Promise.all([ - knex(table).insert(insert), + const [inserted, updated] = await Promise.all([ + knex(table).returning('*').insert(insert), knex.transaction(async trx => Promise.all(update.map(item => trx .where({ [identifier]: item[identifier] }) .update(item) - .into(table)))), + .into(table) + .returning('*')))), ]); + + return { + inserted: Array.isArray(inserted) ? inserted : [], + updated: updated.reduce((acc, updatedItems) => acc.concat(updatedItems), []), + }; } return { insert, update };