Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explode command #694

Merged
merged 10 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [completions](./referenz/kommandos/completions.md)
- [convert](./referenz/kommandos/convert.md)
- [count](./referenz/kommandos/count.md)
- [explode](./referenz/kommandos/explode.md)
- [filter](./referenz/kommandos/filter.md)
- [frequency](./referenz/kommandos/frequency.md)
- [hash](./referenz/kommandos/hash.md)
Expand Down
Binary file not shown.
180 changes: 180 additions & 0 deletions docs/book/src/referenz/kommandos/explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# `explode`

![stability-badge](https://img.shields.io/badge/stability-unstable-red?style=flat-square)

Mithilfe des `explode`-Kommandos lassen sich Datensätze in Lokal- bzw.
Exemplardatensätze aufteilen.

> **Hinweis:** Das `explode`-Kommando befindet sich in der aktiven
> Entwicklung. Funktionalitäten können unvollständig oder fehlerhaft
> sein. Änderungen am _command-line interface_ (CLI) sind nicht
> ausgeschlossen.

## Beschreibung

<!-- TODO: Seite über den Aufbau eines PICA+-Datensatzes erstellen -->

Die Verarbeitung und Analyse von Datensätzen auf Lokal- bzw.
Exemplarebene ist mitunter nur unzureichend möglich, da Filterausdrücke
die Grenzen von untergeordneten Ebenen nicht respektiert. Abhilfe kann
das `explode`-Kommando schaffen, das einen Datensatz in einzelne Lokal-
bzw. Exemplardatensätze aufteilen kann. Dabei werden alle Felder der
übergeordneten Ebenen mit in die Datensätze übernommen.

Das Aufteilen der Datensätze erfolgt durch die Angabe der Ebene
(_level_) an der der Datensatz geteilt werden soll. Es können folgende
Werte ausgewählt werden:

* `main` (Aufteilen auf Ebene der Titeldaten),
* `local` (Aufteilen auf Ebene der Lokaldaten),
* sowie `copy` (Aufteilen auf Ebene der Exemplardaten).

Sollen ein Datensatz in alle Lokaldatensätze aufgeteilt werden, muss die
Ebene `local` ausgewählt werden. Die neu erstellten Datensätze enthalten
alle Titeldaten (Felder der Ebene 0), den Identifikator des
Lokaldatensatzes (Feld `[email protected]`) sowie alle Exemplare, die diesem
Lokaldatensatz zugeordnet werden.

Soll darüber hinaus für jedes Exemplar ein eigenständiger Datensatz
erzeugt werden, muss die Ebene `copy` angegeben werden. Jeder erzeugte
Datensatz enthält die Titeldaten (Felder der Ebene 0), den Identifikator
des Lokaldatensatzes (Feld `[email protected]`) und nur die Felder, die zum
jeweiligen Exemplar gehören.

Schließlich kann ein Datensatz auch auf Ebene der Titeldaten (`main`)
aufgeteilt werden. Diese Aufwahl verändert nichts am Datensatz und gibt
den vollständigen Datensatz mit allen Feldern aus.

Als Beispiel soll folgender (reduzierter) Datensatz dienen:

```console
$ pica print COPY.dat.gz
003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/01 $0 0123456789
203@/02 $0 1234567890
101@ $a 2
203@/01 $0 345678901


```

Dieser Datensatz lässt sich in zwei Datensätze auf Ebene der Lokaldaten
aufteilen:

```console
$ pica explode -s local COPY.dat.gz -o local.dat
$ pica print local.dat
003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/01 $0 0123456789
203@/02 $0 1234567890

003@ $0 123456789
002@ $0 Abvz
101@ $a 2
203@/01 $0 345678901


```

Soll jedes Exemplar ein eigenständiger Datensatz werden, wird dies durch
Angabe von `copy` erzielt:

```console
$ pica explode -s copy COPY.dat.gz -o copy.dat
$ pica print copy.dat
003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/01 $0 0123456789

003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/02 $0 1234567890

003@ $0 123456789
002@ $0 Abvz
101@ $a 2
203@/01 $0 345678901


```


## Optionen

* `-s`, `--skip-invalid` — Überspringt jene Zeilen aus der Eingabe, die
nicht dekodiert werden konnten.
* `-i`, `--ignore-case` — Groß- und Kleinschreibung wird bei Vergleichen
ignoriert.
* `--strsim-threshold <value>` — Festlegen des Schwellenwerts beim
Ähnlichkeitsvergleich von Zeichenketten mittels `=*`.
* `--where` `<filter>` — Angabe eines Filters, der auf die erzeugten
Datensätze angewandt wird.
* `--and` `<expr>` — Hinzufügen eines zusätzlichen Filters mittels der
booleschen `&&`-Verknüpfung. Der ursprüngliche Filterausdruck
`<filter>` wird zum Ausdruck `<filter> && <expr>`.
* `--or` `<expr>` — Hinzufügen eines zusätzlichen Filters mittels der
booleschen `||`-Verknüpfung. Der ursprüngliche Filterausdruck
`<filter>` wird zum Ausdruck `<filter> || <expr>`.
* `--not` `<expr>` — Hinzufügen eines zusätzlichen Filters. Der
ursprüngliche Filterausdruck `<filter>` wird zum Ausdruck `<filter> &&
!(<expr>)`.
* `-g`, `--gzip` — Komprimieren der Ausgabe im [Gzip]-Format.
* `-p`, `--progress` — Anzeige des Fortschritts, der die Anzahl der
eingelesenen gültigen sowie invaliden Datensätze anzeigt. Das
Aktivieren der Option erfordert das Schreiben der Datensätze in eine
Datei mittels `-o` bzw. `--output`.
* `-o`, `--output` — Angabe, in welche Datei die Ausgabe geschrieben
werden soll. Standardmäßig wird die Ausgabe in die Standardausgabe
`stdout` geschrieben. Endet der Dateiname mit dem Suffix `.gz`, wird
die Ausgabe automatisch im gzip-Format komprimiert.


## Konfiguration

<!-- TODO: Link zum allgemeinen Kapitel über die Konfigurationsdatei -->

Einige Kommandozeilen-Optionen lassen sich per Konfigurationsdatei
(`Pica.toml`) einstellen:

```toml
[explode]
skip-invalid = true
gzip = true
```

## Beispiele

### Eingrenzen der Datensätze

Ist nur eine Teilmenge der erzeugten Datensätze von Interesse, lässt
sich die Ergebnismenge durch Hinzufügen eines Filterausdrucks
eingrenzen.

Werden bspw. nur die Exemplare mit dem Identifikator `[email protected] == "1"`
benötigt, kann die Eingrenzung durch Angabe der `--where`-Option
eingegrenzt werden:

```console
$ pica explode -s copy --where '[email protected] == "1"' COPY.dat.gz -o copy.dat
$ pica print copy.dat
003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/01 $0 0123456789

003@ $0 123456789
002@ $0 Abvz
101@ $a 1
203@/02 $0 1234567890


```


[Gzip]: https://de.wikipedia.org/wiki/Gzip
2 changes: 2 additions & 0 deletions docs/book/src/referenz/kommandos/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
* [completions](./completions.md) — Erzeugung von Shell-Skripten zur
Autovervollständigung
* [count](./count.md) — Zählen von Datensätzen, Feldern und Unterfeldern
* [explode](./explode.md) — Teilt Datensätzen in Lokal- oder
Exemplardatensätze auf
* [filter](./filter.md) — Filtert Datensätze anhand eines Kriteriums
* [frequency](./frequency.md) — Ermitteln einer Häufigkeitsverteilung
über ein oder mehrere Unterfelder
Expand Down
7 changes: 6 additions & 1 deletion pica-record/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::occurrence::parse_occurrence;
use crate::parser::{ParseResult, RS, SP};
use crate::subfield::parse_subfield;
use crate::tag::parse_tag;
use crate::{Occurrence, ParsePicaError, Subfield, Tag};
use crate::{Level, Occurrence, ParsePicaError, Subfield, Tag};

/// A PICA+ field.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -229,6 +229,11 @@ impl<'a, T: AsRef<[u8]> + From<&'a BStr> + Display> Field<T> {
}
write!(out, "\x1e")
}

#[inline]
pub fn level(&self) -> Level {
self.tag.level()
}
}

impl<'a, T: AsRef<[u8]>> IntoIterator for &'a Field<T> {
Expand Down
11 changes: 10 additions & 1 deletion pica-record/src/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use nom::sequence::tuple;
use nom::Finish;

use crate::parser::ParseResult;
use crate::ParsePicaError;
use crate::{Level, ParsePicaError};

/// A PICA+ tag.
#[derive(Eq, Debug, Clone)]
Expand Down Expand Up @@ -76,6 +76,15 @@ impl<'a, T: AsRef<[u8]> + From<&'a BStr> + Display> Tag<T> {
pub fn from_unchecked(value: impl Into<T>) -> Self {
Self(value.into())
}

pub fn level(&self) -> Level {
match self.as_ref().first().expect("valid tag") {
b'0' => Level::Main,
b'1' => Level::Local,
b'2' => Level::Copy,
_ => unreachable!(),
}
}
}

/// Parse a PICA+ tag.
Expand Down
Loading
Loading