Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.41% |
73 / 79 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
DeletesCommand | |
92.41% |
73 / 79 |
|
50.00% |
2 / 4 |
17.13 | |
0.00% |
0 / 1 |
configure | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
1 | |||
getIdsFromFlatFile | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getIdsFromMarcFile | |
71.43% |
10 / 14 |
|
0.00% |
0 / 1 |
8.14 | |||
execute | |
93.75% |
30 / 32 |
|
0.00% |
0 / 1 |
6.01 |
1 | <?php |
2 | |
3 | /** |
4 | * Console command: delete from Solr |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2020. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Console |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development Wiki |
28 | */ |
29 | |
30 | namespace VuFindConsole\Command\Util; |
31 | |
32 | use Symfony\Component\Console\Attribute\AsCommand; |
33 | use Symfony\Component\Console\Formatter\OutputFormatter; |
34 | use Symfony\Component\Console\Input\InputArgument; |
35 | use Symfony\Component\Console\Input\InputInterface; |
36 | use Symfony\Component\Console\Input\InputOption; |
37 | use Symfony\Component\Console\Output\OutputInterface; |
38 | use VuFind\Marc\MarcCollectionFile; |
39 | |
40 | use function count; |
41 | use function strlen; |
42 | |
43 | /** |
44 | * Console command: delete from Solr |
45 | * |
46 | * @category VuFind |
47 | * @package Console |
48 | * @author Demian Katz <demian.katz@villanova.edu> |
49 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
50 | * @link https://vufind.org/wiki/development Wiki |
51 | */ |
52 | #[AsCommand( |
53 | name: 'util/deletes', |
54 | description: 'Tool for deleting Solr records' |
55 | )] |
56 | class DeletesCommand extends AbstractSolrCommand |
57 | { |
58 | /** |
59 | * Configure the command. |
60 | * |
61 | * @return void |
62 | */ |
63 | protected function configure() |
64 | { |
65 | $this |
66 | ->setHelp('Deletes a set of records from the Solr index.') |
67 | ->addArgument( |
68 | 'filename', |
69 | InputArgument::REQUIRED, |
70 | 'the file containing records to delete.' |
71 | )->addArgument( |
72 | 'format', |
73 | InputArgument::OPTIONAL, |
74 | "the format of the file -- it may be one of the following:\n" |
75 | . 'flat - flat text format ' |
76 | . "(deletes all IDs in newline-delimited file)\n" |
77 | . 'marc - MARC record in binary or MARCXML format (deletes all ' |
78 | . "record IDs from 001 fields)\n" |
79 | . "marcxml - DEPRECATED; use marc instead\n", |
80 | 'marc' |
81 | )->addArgument( |
82 | 'index', |
83 | InputArgument::OPTIONAL, |
84 | 'Name of Solr core/backend to update', |
85 | 'Solr' |
86 | )->addOption( |
87 | 'id-prefix', |
88 | null, |
89 | InputOption::VALUE_REQUIRED, |
90 | 'Prefix to prepend to all IDs', |
91 | '' |
92 | ); |
93 | } |
94 | |
95 | /** |
96 | * Load IDs from a flat file. |
97 | * |
98 | * @param string $filename Filename to load from |
99 | * |
100 | * @return array |
101 | */ |
102 | protected function getIdsFromFlatFile(string $filename): array |
103 | { |
104 | $ids = []; |
105 | foreach (array_map('trim', file($filename)) as $id) { |
106 | if (strlen($id)) { |
107 | $ids[] = $id; |
108 | } |
109 | } |
110 | return $ids; |
111 | } |
112 | |
113 | /** |
114 | * Load IDs from a MARC file |
115 | * |
116 | * @param string $filename MARC file |
117 | * @param OutputInterface $output Output object |
118 | * |
119 | * @return array |
120 | */ |
121 | protected function getIdsFromMarcFile( |
122 | string $filename, |
123 | OutputInterface $output |
124 | ): array { |
125 | $ids = []; |
126 | // MARC file mode: |
127 | $messageCallback = function (string $msg, int $level) use ($output) { |
128 | if ($output->isVerbose() || $level !== E_NOTICE) { |
129 | $output->writeln( |
130 | '<comment>' . OutputFormatter::escape($msg) . '</comment>' |
131 | ); |
132 | } |
133 | }; |
134 | $collection = new MarcCollectionFile($filename, $messageCallback); |
135 | |
136 | // Once the records are loaded, the rest of the logic is always the same: |
137 | $missingIdCount = 0; |
138 | foreach ($collection as $record) { |
139 | if ($id = $record->getField('001')) { |
140 | $ids[] = $id; |
141 | } else { |
142 | $missingIdCount++; |
143 | } |
144 | } |
145 | if ($output->isVerbose() && $missingIdCount) { |
146 | $output->writeln( |
147 | "Encountered $missingIdCount record(s) without IDs." |
148 | ); |
149 | } |
150 | return $ids; |
151 | } |
152 | |
153 | /** |
154 | * Run the command. |
155 | * |
156 | * @param InputInterface $input Input object |
157 | * @param OutputInterface $output Output object |
158 | * |
159 | * @return int 0 for success |
160 | */ |
161 | protected function execute(InputInterface $input, OutputInterface $output) |
162 | { |
163 | $filename = $input->getArgument('filename'); |
164 | $mode = $input->getArgument('format'); |
165 | $index = $input->getArgument('index'); |
166 | $prefix = $input->getOption('id-prefix'); |
167 | |
168 | // File doesn't exist? |
169 | if (!file_exists($filename)) { |
170 | $output->writeln("Cannot find file: {$filename}"); |
171 | return 1; |
172 | } |
173 | |
174 | $output->writeln( |
175 | "Loading IDs in {$mode} mode.", |
176 | OutputInterface::VERBOSITY_VERBOSE |
177 | ); |
178 | |
179 | // Build list of records to delete: |
180 | $ids = ($mode == 'flat') |
181 | ? $this->getIdsFromFlatFile($filename) |
182 | : $this->getIdsFromMarcFile($filename, $output); |
183 | |
184 | // Delete, Commit and Optimize if necessary: |
185 | if (!empty($ids)) { |
186 | $output->writeln( |
187 | 'Attempting to delete ' . count($ids) . ' record(s): ' |
188 | . implode(', ', $ids), |
189 | OutputInterface::VERBOSITY_VERBOSE |
190 | ); |
191 | if (!empty($prefix)) { |
192 | $callback = function ($id) use ($prefix) { |
193 | return $prefix . $id; |
194 | }; |
195 | $ids = array_map($callback, $ids); |
196 | } |
197 | $this->solr->deleteRecords($index, $ids); |
198 | $output->writeln( |
199 | 'Delete operation completed.', |
200 | OutputInterface::VERBOSITY_VERBOSE |
201 | ); |
202 | } elseif ($output->isVerbose()) { |
203 | $output->writeln('Nothing to delete.'); |
204 | } |
205 | |
206 | return 0; |
207 | } |
208 | } |