193 lines
5.7 KiB
Perl
193 lines
5.7 KiB
Perl
#!/usr/bin/perl
|
|
|
|
# For info: https://dev.yorhel.nl/doc/sqlobject
|
|
# Date: 2019-08-13
|
|
# License: MIT
|
|
|
|
use v5.12;
|
|
use warnings;
|
|
use DBIx::Simple;
|
|
use SQL::Interp 'sql';
|
|
use Data::Dumper 'Dumper';
|
|
|
|
my $db = DBIx::Simple->connect('dbi:Pg:dbname=test', undef, undef, { RaiseError => 1 });
|
|
|
|
$db->query($_) for (split /;/, <<_);
|
|
DROP SCHEMA IF EXISTS sqlobject_test CASCADE;
|
|
CREATE SCHEMA sqlobject_test;
|
|
SET search_path TO sqlobject_test;
|
|
|
|
CREATE TABLE games (
|
|
game_id integer PRIMARY KEY,
|
|
title text
|
|
);
|
|
|
|
CREATE TABLE releases (
|
|
release_id integer PRIMARY KEY,
|
|
game_id integer REFERENCES games,
|
|
release_date date
|
|
);
|
|
|
|
CREATE TABLE releases_platforms (
|
|
release_id integer REFERENCES releases,
|
|
platform text
|
|
);
|
|
|
|
INSERT INTO games VALUES (7, 'Tsukihime');
|
|
INSERT INTO releases VALUES (339, 7, '2000-12-29');
|
|
INSERT INTO releases VALUES (341, 7, '2006-12-28');
|
|
INSERT INTO releases_platforms VALUES (339, 'Linux');
|
|
INSERT INTO releases_platforms VALUES (339, 'Windows');
|
|
INSERT INTO releases_platforms VALUES (341, 'Windows')
|
|
_
|
|
|
|
|
|
{
|
|
my @games = $db->query('SELECT game_id, title FROM games')->hashes;
|
|
|
|
for my $game (@games) {
|
|
$game->{releases} = [
|
|
$db->query(
|
|
'SELECT release_id, release_date FROM releases WHERE game_id = ?',
|
|
$game->{game_id}
|
|
)->hashes
|
|
];
|
|
}
|
|
print Dumper \@games;
|
|
}
|
|
|
|
|
|
{
|
|
# Our abstraction to fetch game entries.
|
|
my sub fetch_games {
|
|
my $game_ids = shift;
|
|
$db->query("SELECT * FROM games WHERE game_id IN($game_ids)")->hashes;
|
|
# We could be doing some transformations here.
|
|
# Sorting, pagination, fetching extra information, etc.
|
|
}
|
|
|
|
# We can use that function to fetch games that have already been released.
|
|
my $ids = "SELECT game_id FROM releases WHERE release_date <= CURRENT_DATE";
|
|
my @released_games = fetch_games($ids);
|
|
print Dumper \@released_games;
|
|
}
|
|
|
|
|
|
{
|
|
|
|
my sub fetch_games {
|
|
my $game_ids = shift;
|
|
# Note the use of `iquery` instead of `query`,
|
|
# this is a DBIx::Simple wrapper around `sql_interp()`.
|
|
$db->iquery("SELECT * FROM games WHERE game_id IN(", $game_ids, ")")->hashes;
|
|
}
|
|
|
|
my $latest_date = '2019-01-20'; # Our user input
|
|
|
|
# This is our "SQL + bind parameters" in a single variable.
|
|
my $ids = sql("SELECT game_id FROM releases WHERE release_date <=", \$latest_date);
|
|
|
|
# ...which can be passed to fetch_games():
|
|
my @games_with_release_before_latest_date = fetch_games($ids);
|
|
print Dumper \@games_with_release_before_latest_date;
|
|
}
|
|
|
|
|
|
{
|
|
my @games = $db->query('SELECT game_id, title FROM games')->hashes;
|
|
|
|
# List of game_ids, which we can use for our IN() clause.
|
|
my @game_ids = map $_->{game_id}, @games;
|
|
|
|
# Fetch all the releases related to @game_ids.
|
|
my @releases = $db->iquery(
|
|
'SELECT game_id, release_id, release_date FROM releases WHERE game_id IN',
|
|
\@game_ids
|
|
)->hashes;
|
|
|
|
# Create a 'game_id' => [releases] lookup table for quick access
|
|
my %releases;
|
|
for my $release (@releases) {
|
|
# Add this release to %releases and remove the 'game_id' column,
|
|
# which was only needed for this merging step.
|
|
push @{$releases{ delete $release->{game_id} }}, $release;
|
|
}
|
|
|
|
# Now merge the release information back into @games
|
|
for my $game (@games) {
|
|
$game->{releases} = $releases{ $game->{game_id} } || [];
|
|
}
|
|
print Dumper \@games;
|
|
}
|
|
|
|
|
|
sub enrich {
|
|
my($field_name, $merge_field, $sql, @objects) = @_;
|
|
my %ids = map +($_->{$merge_field}, []), @objects;
|
|
return if !keys %ids;
|
|
my @result = $db->iquery( $sql->([keys %ids]) )->hashes;
|
|
push @{$ids{ delete $_->{$merge_field} }}, $_ for (@result);
|
|
$_->{ $field_name } = $ids{ $_->{$merge_field} } for (@objects);
|
|
}
|
|
|
|
|
|
sub enrich_flatten {
|
|
# This is the same as the original enrich()
|
|
my($field_name, $merge_field, $sql, @objects) = @_;
|
|
my %ids = map +($_->{$merge_field}, []), @objects;
|
|
return if !keys %ids;
|
|
my @result = $db->iquery( $sql->([keys %ids]) )->hashes;
|
|
|
|
# This is the actual merge strategy, which we'll modify slightly:
|
|
push @{$ids{ delete $_->{$merge_field} }}, values %$_ for (@result);
|
|
$_->{ $field_name } = $ids{ $_->{$merge_field} } for (@objects);
|
|
}
|
|
|
|
|
|
{
|
|
my @games = $db->query('SELECT game_id, title FROM games')->hashes;
|
|
|
|
enrich 'releases', 'game_id', sub {
|
|
sql 'SELECT game_id, release_id, release_date FROM releases WHERE game_id IN', $_[0]
|
|
}, @games;
|
|
|
|
# More nesting
|
|
enrich 'platforms', 'release_id', sub {
|
|
sql 'SELECT release_id, platform FROM releases_platforms WHERE release_id IN', $_[0]
|
|
}, map @{$_->{releases}}, @games;
|
|
|
|
# Flattened platforms
|
|
enrich_flatten 'platforms_flat', 'release_id', sub {
|
|
sql 'SELECT release_id, platform FROM releases_platforms WHERE release_id IN', $_[0]
|
|
}, map @{$_->{releases}}, @games;
|
|
|
|
print Dumper \@games;
|
|
}
|
|
|
|
|
|
{
|
|
my @games = $db->query('SELECT game_id, title FROM games')->hashes;
|
|
|
|
# This is the per-game platforms list.
|
|
enrich_flatten 'platforms', 'game_id', sub {
|
|
sql 'SELECT DISTINCT game_id, platform
|
|
FROM releases
|
|
JOIN releases_platforms USING (release_id)
|
|
WHERE game_id IN', $_[0]
|
|
}, @games;
|
|
|
|
my $latest_date = '2004-01-20'; # Our user input
|
|
|
|
# This is the releases list.
|
|
enrich 'releases', 'game_id', sub {
|
|
sql 'SELECT game_id, release_id, release_date,
|
|
(SELECT COUNT(*) FROM releases_platforms rp
|
|
WHERE rp.release_id = r.release_id) AS platform_count
|
|
FROM releases r
|
|
WHERE game_id IN', $_[0], '
|
|
AND release_date <=', \$latest_date, '
|
|
ORDER BY release_date'
|
|
}, @games;
|
|
|
|
print Dumper \@games;
|
|
}
|