|
Lines 1-6
Link Here
|
| 1 |
package Koha::OAI::Server::ListBase; |
1 |
package Koha::OAI::Server::ListBase; |
| 2 |
|
2 |
|
| 3 |
# Copyright The National Library of Finland, University of Helsinki 2016-2017 |
3 |
# Copyright The National Library of Finland, University of Helsinki 2016-2018 |
| 4 |
# |
4 |
# |
| 5 |
# This file is part of Koha. |
5 |
# This file is part of Koha. |
| 6 |
# |
6 |
# |
|
Lines 46-181
sub GetRecords {
Link Here
|
| 46 |
$set = GetOAISetBySpec($token->{'set'}); |
46 |
$set = GetOAISetBySpec($token->{'set'}); |
| 47 |
} |
47 |
} |
| 48 |
my $offset = $token->{offset}; |
48 |
my $offset = $token->{offset}; |
| 49 |
my $deleted = defined $token->{deleted} ? $token->{deleted} : 0; |
|
|
| 50 |
my $deleted_count = defined $token->{deleted_count} ? $token->{deleted_count} : 0; |
| 51 |
my $max = $repository->{koha_max_count}; |
49 |
my $max = $repository->{koha_max_count}; |
| 52 |
my $count = 0; |
50 |
my $count = 0; |
| 53 |
my $format = $args{metadataPrefix} || $token->{metadata_prefix}; |
51 |
my $format = $args{metadataPrefix} || $token->{metadata_prefix}; |
| 54 |
my $include_items = $repository->items_included( $format ); |
52 |
my $include_items = $repository->items_included( $format ); |
| 55 |
|
53 |
|
| 56 |
# Since creating a union of normal and deleted record tables would be a heavy |
54 |
my $sql = " |
| 57 |
# operation in a large database, build results in two stages: |
55 |
SELECT biblionumber, deleted_at |
| 58 |
# first deleted records ($deleted == 1), then normal records ($deleted == 0) |
56 |
FROM biblio_metadata |
| 59 |
STAGELOOP: |
57 |
WHERE (timestamp >= ? AND timestamp <= ?) |
| 60 |
for ( ; $deleted >= 0; $deleted-- ) { |
58 |
"; |
| 61 |
my $table = $deleted ? 'deletedbiblio_metadata' : 'biblio_metadata'; |
59 |
my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'}); |
| 62 |
my $sql = " |
|
|
| 63 |
SELECT biblionumber |
| 64 |
FROM $table |
| 65 |
WHERE (timestamp >= ? AND timestamp <= ?) |
| 66 |
"; |
| 67 |
my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'}); |
| 68 |
|
| 69 |
if ($include_items) { |
| 70 |
$sql .= " |
| 71 |
OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ? AND deleted_at IS NOT NULL) |
| 72 |
"; |
| 73 |
push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'}); |
| 74 |
if (!$deleted) { |
| 75 |
$sql .= " |
| 76 |
OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ? AND deleted_at IS NULL) |
| 77 |
"; |
| 78 |
push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'}); |
| 79 |
} |
| 80 |
} |
| 81 |
|
60 |
|
|
|
61 |
if ($include_items) { |
| 82 |
$sql .= " |
62 |
$sql .= " |
| 83 |
ORDER BY biblionumber |
63 |
OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ?) |
| 84 |
"; |
64 |
"; |
|
|
65 |
push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'}); |
| 66 |
} |
| 85 |
|
67 |
|
| 86 |
# Use a subquery for sets since it allows us to use an index in |
68 |
$sql .= " |
| 87 |
# biblioitems table and is quite a bit faster than a join. |
69 |
ORDER BY biblionumber |
| 88 |
if (defined $set) { |
70 |
"; |
| 89 |
$sql = " |
|
|
| 90 |
SELECT bi.* FROM ($sql) bi |
| 91 |
WHERE bi.biblionumber in (SELECT osb.biblionumber FROM oai_sets_biblios osb WHERE osb.set_id = ?) |
| 92 |
"; |
| 93 |
push @bind_params, $set->{'id'}; |
| 94 |
} |
| 95 |
|
71 |
|
| 96 |
$sql .= " |
72 |
# Use a subquery for sets since it allows us to use an index in |
| 97 |
LIMIT " . ($max + 1) . " |
73 |
# biblioitems table and is quite a bit faster than a join. |
| 98 |
OFFSET " . ($offset - $deleted_count); |
74 |
if (defined $set) { |
| 99 |
|
75 |
$sql = " |
| 100 |
my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr ); |
76 |
SELECT bi.* FROM ($sql) bi |
| 101 |
|
77 |
WHERE bi.biblionumber in (SELECT osb.biblionumber FROM oai_sets_biblios osb WHERE osb.set_id = ?) |
| 102 |
if ( $deleted ) { |
78 |
"; |
| 103 |
$sql = " |
79 |
push @bind_params, $set->{'id'}; |
| 104 |
SELECT MAX(timestamp) |
80 |
} |
| 105 |
FROM ( |
81 |
|
| 106 |
SELECT timestamp FROM deletedbiblio_metadata WHERE biblionumber = ? |
82 |
$sql .= " |
| 107 |
UNION |
83 |
LIMIT " . ($max + 1) . " |
| 108 |
SELECT timestamp FROM items WHERE biblionumber = ? AND deleted_at IS NOT NULL |
84 |
OFFSET $offset"; |
| 109 |
) bis |
85 |
|
| 110 |
"; |
86 |
my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr ); |
| 111 |
} else { |
87 |
|
| 112 |
$sql = " |
88 |
$sql = " |
| 113 |
SELECT MAX(timestamp) |
89 |
SELECT MAX(timestamp) |
| 114 |
FROM ( |
90 |
FROM ( |
| 115 |
SELECT timestamp FROM biblio_metadata WHERE biblionumber = ? |
91 |
SELECT timestamp FROM biblio_metadata WHERE biblionumber = ? |
| 116 |
UNION |
92 |
UNION |
| 117 |
SELECT timestamp FROM items WHERE biblionumber = ? AND deleted_at IS NULL |
93 |
SELECT timestamp FROM items WHERE biblionumber = ? |
| 118 |
UNION |
94 |
) bi |
| 119 |
SELECT timestamp FROM items WHERE biblionumber = ? AND deleted_at IS NOT NULL |
95 |
"; |
| 120 |
) bi |
96 |
my $record_sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr ); |
| 121 |
"; |
97 |
|
|
|
98 |
$sth->execute( @bind_params ) || die( 'Could not execute statement: ' . $sth->errstr ); |
| 99 |
while ( my ($biblionumber, $deleted_at) = $sth->fetchrow ) { |
| 100 |
$count++; |
| 101 |
if ( $count > $max ) { |
| 102 |
$self->resumptionToken( |
| 103 |
new Koha::OAI::Server::ResumptionToken( |
| 104 |
metadataPrefix => $token->{metadata_prefix}, |
| 105 |
from => $token->{from}, |
| 106 |
until => $token->{until}, |
| 107 |
offset => $token->{offset} + $max, |
| 108 |
set => $token->{set}, |
| 109 |
) |
| 110 |
); |
| 111 |
last; |
| 122 |
} |
112 |
} |
| 123 |
my $record_sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr ); |
113 |
$record_sth->execute( ( $biblionumber, $biblionumber ) ) || die( 'Could not execute statement: ' . $sth->errstr ); |
| 124 |
|
|
|
| 125 |
$sth->execute( @bind_params ) || die( 'Could not execute statement: ' . $sth->errstr ); |
| 126 |
while ( my ($biblionumber) = $sth->fetchrow ) { |
| 127 |
$count++; |
| 128 |
if ( $count > $max ) { |
| 129 |
$self->resumptionToken( |
| 130 |
new Koha::OAI::Server::ResumptionToken( |
| 131 |
metadataPrefix => $token->{metadata_prefix}, |
| 132 |
from => $token->{from}, |
| 133 |
until => $token->{until}, |
| 134 |
offset => $token->{offset} + $max, |
| 135 |
set => $token->{set}, |
| 136 |
deleted => $deleted, |
| 137 |
deleted_count => $deleted_count |
| 138 |
) |
| 139 |
); |
| 140 |
last STAGELOOP; |
| 141 |
} |
| 142 |
my @params = $deleted ? ( $biblionumber, $biblionumber ) : ( $biblionumber, $biblionumber, $biblionumber ); |
| 143 |
$record_sth->execute( @params ) || die( 'Could not execute statement: ' . $sth->errstr ); |
| 144 |
|
114 |
|
| 145 |
my ($timestamp) = $record_sth->fetchrow; |
115 |
my ($timestamp) = $record_sth->fetchrow; |
| 146 |
|
116 |
|
| 147 |
my $oai_sets = GetOAISetsBiblio($biblionumber); |
117 |
my $oai_sets = GetOAISetsBiblio($biblionumber); |
| 148 |
my @setSpecs; |
118 |
my @setSpecs; |
| 149 |
foreach ( @$oai_sets ) { |
119 |
foreach ( @$oai_sets ) { |
| 150 |
push @setSpecs, $_->{spec}; |
120 |
push @setSpecs, $_->{spec}; |
| 151 |
} |
121 |
} |
| 152 |
if ( $metadata ) { |
122 |
if ( $metadata ) { |
| 153 |
my $marcxml = !$deleted ? $repository->get_biblio_marcxml($biblionumber, $format) : undef; |
123 |
my $marcxml = !$deleted_at ? $repository->get_biblio_marcxml($biblionumber, $format) : undef; |
| 154 |
if ( $marcxml ) { |
124 |
if ( $marcxml ) { |
| 155 |
$self->record( Koha::OAI::Server::Record->new( |
125 |
$self->record( Koha::OAI::Server::Record->new( |
| 156 |
$repository, $marcxml, $timestamp, \@setSpecs, |
126 |
$repository, $marcxml, $timestamp, \@setSpecs, |
| 157 |
identifier => $repository->{ koha_identifier } . ':' . $biblionumber, |
127 |
identifier => $repository->{ koha_identifier } . ':' . $biblionumber, |
| 158 |
metadataPrefix => $token->{metadata_prefix} |
128 |
metadataPrefix => $token->{metadata_prefix} |
| 159 |
) ); |
129 |
) ); |
| 160 |
} else { |
|
|
| 161 |
$self->record( Koha::OAI::Server::DeletedRecord->new( |
| 162 |
$timestamp, \@setSpecs, identifier => $repository->{ koha_identifier } . ':' . $biblionumber |
| 163 |
) ); |
| 164 |
} |
| 165 |
} else { |
130 |
} else { |
| 166 |
$timestamp =~ s/ /T/; |
131 |
$self->record( Koha::OAI::Server::DeletedRecord->new( |
| 167 |
$timestamp .= 'Z'; |
132 |
$timestamp, \@setSpecs, identifier => $repository->{ koha_identifier } . ':' . $biblionumber |
| 168 |
$self->identifier( new HTTP::OAI::Header( |
|
|
| 169 |
identifier => $repository->{ koha_identifier} . ':' . $biblionumber, |
| 170 |
datestamp => $timestamp, |
| 171 |
status => $deleted ? 'deleted' : undef |
| 172 |
) ); |
133 |
) ); |
| 173 |
} |
134 |
} |
|
|
135 |
} else { |
| 136 |
$timestamp =~ s/ /T/; |
| 137 |
$timestamp .= 'Z'; |
| 138 |
$self->identifier( new HTTP::OAI::Header( |
| 139 |
identifier => $repository->{ koha_identifier} . ':' . $biblionumber, |
| 140 |
datestamp => $timestamp, |
| 141 |
status => $deleted_at ? 'deleted' : undef |
| 142 |
) ); |
| 174 |
} |
143 |
} |
| 175 |
# Store offset and deleted record count |
|
|
| 176 |
$offset += $count; |
| 177 |
$deleted_count = $offset if ($deleted); |
| 178 |
} |
144 |
} |
|
|
145 |
# Store offset |
| 146 |
$offset += $count; |
| 179 |
return $count; |
147 |
return $count; |
| 180 |
} |
148 |
} |
| 181 |
|
149 |
|