Introduce object mechanism to VCs in vc conversion tool
Change-Id: If5d06a0b9b08b9c39700a0eb471a0edfb0d5daf5
diff --git a/lib/KorAP/VirtualCorpus.pm b/lib/KorAP/VirtualCorpus.pm
new file mode 100644
index 0000000..58adc6b
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus.pm
@@ -0,0 +1,104 @@
+package KorAP::VirtualCorpus;
+use strict;
+use warnings;
+
+
+# Get or set name of the VC
+sub name {
+ my $self = shift;
+ unless (@_) {
+ return $self->{name};
+ };
+ $self->{name} = shift;
+ return $self;
+};
+
+
+# Comment
+sub comment {
+ my $self = shift;
+ unless (@_) {
+ return $self->{comment};
+ };
+ $self->{comment} //= [];
+
+ push @{$self->{comment}}, @_;
+ return $self;
+};
+
+
+# Flatten the object - can be overwritten
+sub flatten {
+ shift;
+};
+
+
+# Serialize to koral object - can be overwritten
+sub to_koral {
+ shift;
+};
+
+
+# Quote utility function
+sub quote {
+ shift;
+ my $str = shift;
+ $str =~ s/(["\\])/\\$1/g;
+ return qq{"$str"};
+};
+
+
+# Escaped quote utility function
+sub equote {
+ shift;
+ my $str = shift;
+ $str =~ s/(["\\])/\\$1/g;
+ $str =~ s/(["\\])/\\$1/g;
+ return '\\"' . $str . '\\"';
+};
+
+
+sub _commentparam_to_string {
+ my $self = shift;
+ my $comment = $self->_comment_to_string;
+ if ($comment) {
+ return qq!,"comment":"$comment"!;
+ };
+ return '';
+};
+
+
+sub _comment_to_string {
+ my $self = shift;
+ if (!$self->name && !$self->comment) {
+ return '';
+ };
+
+ my $json = '';
+ $json .= 'name:' . $self->equote($self->name) if $self->name;
+ if ($self->name && $self->comment) {
+ $json .= ','
+ };
+ $json .= join(',', @{$self->{comment}}) if $self->{comment};
+
+ return $json;
+};
+
+
+# Stringify collection
+sub to_string {
+ my $self = shift;
+ ## Create collection object
+
+ my $obj = $self->to_koral;
+
+ my $json = '{';
+ $json .= '"@context":"http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",';
+ $json .= '"collection":';
+ $json .= $obj->_to_fragment;
+ # Set at the end, when all comments are done
+ $json .= $obj->_commentparam_to_string;
+ return $json .= '}';
+};
+
+1;
diff --git a/lib/KorAP/VirtualCorpus/And.pm b/lib/KorAP/VirtualCorpus/And.pm
new file mode 100644
index 0000000..613710b
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus/And.pm
@@ -0,0 +1,69 @@
+package KorAP::VirtualCorpus::And;
+use strict;
+use warnings;
+use base 'KorAP::VirtualCorpus';
+
+
+# TODO:
+# Support comments!
+
+
+# Constructor
+sub new {
+ my $class = shift;
+ bless { ops => [@_] }, $class;
+};
+
+
+# Get koral type
+sub koral_type {
+ return 'And';
+};
+
+
+# Get operands
+sub operands {
+ shift->{ops};
+};
+
+
+# Flatten group
+sub flatten {
+ my $self = shift;
+
+ my @ops;
+
+ foreach (@{$self->{ops}}) {
+ if ($_->koral_type eq 'And') {
+ push @ops, @{$_->operands};
+ }
+
+ else {
+ push @ops, $_->flatten;
+ };
+ };
+
+ $self->{ops} = \@ops;
+
+ return $self;
+};
+
+
+# Serialize fragment
+sub _to_fragment {
+ my $self = shift;
+ my $json = '{';
+ $json .= '"@type":"koral:docGroup",';
+ $json .= '"operation":"operation:and",';
+ $json .= '"operands":[';
+ $json .= join(',', map { $_->_to_fragment } @{$self->{ops}});
+ $json .= ']';
+
+ # Set at the end, when all comments are done
+ $json .= $self->_commentparam_to_string;
+ $json .= '}';
+ return $json;
+};
+
+
+1;
diff --git a/lib/KorAP/VirtualCorpus/Doc.pm b/lib/KorAP/VirtualCorpus/Doc.pm
new file mode 100644
index 0000000..e635d3d
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus/Doc.pm
@@ -0,0 +1,96 @@
+package KorAP::VirtualCorpus::Doc;
+use strict;
+use warnings;
+use base 'KorAP::VirtualCorpus';
+
+# Constructor
+sub new {
+ my $class = shift;
+ bless {
+ key => shift,
+ match => 'eq',
+ type => 'string',
+ value => ''
+ }, $class;
+};
+
+
+# Clone document VC
+sub clone {
+ my $self = shift;
+ bless {
+ key => $self->{key},
+ match => $self->{match},
+ type => $self->{type},
+ value => $self->{value},
+ }, __PACKAGE__;
+};
+
+
+# Return object type
+sub koral_type {
+ return 'Doc';
+};
+
+
+# Get or set type
+sub type {
+ my $self = shift;
+ if (@_) {
+ $self->{type} = shift;
+ return $self;
+ };
+ return $self->{type};
+};
+
+
+# Get or set match
+sub match {
+ my $self = shift;
+ if (@_) {
+ $self->{match} = shift;
+ return $self;
+ };
+ return $self->{match};
+};
+
+
+# Get or set key
+sub key {
+ my $self = shift;
+ if (@_) {
+ $self->{key} = shift;
+ return $self;
+ };
+ return $self->{key};
+};
+
+
+# Get or set value
+sub value {
+ my $self = shift;
+ if (@_) {
+ $self->{value} = shift;
+ return $self;
+ };
+ return $self->{value};
+};
+
+
+# Stringify fragment
+sub _to_fragment {
+ my $self = shift;
+ my $json = '{';
+ $json .= '"@type":"koral:doc",';
+ $json .= '"type":"type:' . $self->type . '",';
+ $json .= '"match":"match:' . $self->match . '",';
+ $json .= '"key":"' . $self->key . '",';
+ $json .= '"value":' . $self->quote($self->value);
+
+ # Set at the end, when all comments are done
+ $json .= $self->_commentparam_to_string;
+ return $json . '}';
+};
+
+
+1;
diff --git a/lib/KorAP/VirtualCorpus/DocVec.pm b/lib/KorAP/VirtualCorpus/DocVec.pm
new file mode 100644
index 0000000..49fefb8
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus/DocVec.pm
@@ -0,0 +1,48 @@
+package KorAP::VirtualCorpus::DocVec;
+use strict;
+use warnings;
+use base 'KorAP::VirtualCorpus::Doc';
+
+# Constructor
+sub new {
+ my $class = shift;
+ bless {
+ key => shift,
+ match => 'eq',
+ type => 'string',
+ value => []
+ }, $class;
+};
+
+# Return object type
+sub koral_type {
+ return 'DocVec';
+};
+
+# Get or set value
+sub value {
+ my $self = shift;
+ if (@_) {
+ $self->{value} = [@_];
+ return $self;
+ };
+ return $self->{value};
+};
+
+# Stringify fragment
+sub _to_fragment {
+ my $self = shift;
+ my $json = '{';
+ $json .= '"@type":"koral:doc",';
+ $json .= '"type":"type:' . $self->type . '",';
+ $json .= '"match":"match:' . $self->match . '",';
+ $json .= '"key":"' . $self->key . '",';
+ $json .= '"value":[' . join(',', map { $self->quote($_) } @{$self->value}) . ']';
+
+ # Set at the end, when all comments are done
+ $json .= $self->_commentparam_to_string;
+ return $json . '}';
+};
+
+
+1;
diff --git a/lib/KorAP/VirtualCorpus/Group.pm b/lib/KorAP/VirtualCorpus/Group.pm
new file mode 100644
index 0000000..4062cf9
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus/Group.pm
@@ -0,0 +1,159 @@
+package KorAP::VirtualCorpus::Group;
+use strict;
+use warnings;
+use base 'KorAP::VirtualCorpus';
+use KorAP::VirtualCorpus::Doc;
+use KorAP::VirtualCorpus::And;
+use KorAP::VirtualCorpus::Or;
+
+# Abstract KoralQuery object that normalize to And or Or.
+
+# Construct a new VC group
+sub new {
+ my $class = shift;
+ bless {
+ # New try
+ ops => undef,
+ type => undef
+ }, $class;
+};
+
+
+# Clone object
+sub clone {
+ my $self = shift;
+ my $clone = {};
+ $clone->{ops} = [@{$self->{ops}}] if $self->{ops};
+ $clone->{type} = $self->{type};
+ $clone->{name} = $self->{name};
+ bless $clone, __PACKAGE__;
+};
+
+
+# Add operand
+sub add {
+ my ($self, $type, $op) = @_;
+
+ if (!$self->{ops}) {
+ push @{$self->{ops}}, $op;
+ return $self;
+ };
+
+ if (!$self->{type}) {
+ push @{$self->{ops}}, $op;
+ $self->{type} = $type;
+ return $self;
+ };
+
+ if ($self->{type} eq $type) {
+ push @{$self->{ops}}, $op;
+ return $self;
+ };
+
+ if ($self->{type} eq 'union') {
+ my $vc = KorAP::VirtualCorpus::Or->new(
+ @{$self->{ops}}
+ );
+
+ $self->{type} = 'joint';
+ $self->{ops} = [$vc, $op];
+ return $self;
+ };
+
+ my $vc = KorAP::VirtualCorpus::And->new(
+ @{$self->{ops}}
+ );
+
+ $self->{type} = 'union';
+ $self->{ops} = [$vc, $op];
+ return $self;
+};
+
+
+# Serialize to koral
+sub to_koral {
+ my $self = shift;
+
+ # Single object
+ if (@{$self->{ops}} == 1) {
+ return $self->{ops}->[0]->name($self->name)->flatten;
+ };
+
+ # Union group
+ if ($self->{type} eq 'union') {
+ return KorAP::VirtualCorpus::Or->new(
+ @{$self->{ops}}
+ )->name($self->name)->flatten;
+ }
+
+ # Joint group
+ elsif ($self->{type} eq 'joint') {
+ return KorAP::VirtualCorpus::And->new(
+ @{$self->{ops}}
+ )->name($self->name)->flatten;
+ };
+};
+
+
+# Define an operand to be "or"ed
+sub union {
+ my $self = shift;
+ $self->add('union', shift)
+};
+
+
+# Define a field that should be "or"ed
+sub union_field {
+ my $self = shift;
+ my $field = shift;
+ my $value = shift;
+ $self->union(
+ KorAP::VirtualCorpus::Doc->new($field)->value($value)
+ );
+};
+
+# Define an operand to be "and"ed
+sub joint {
+ my $self = shift;
+ $self->add('joint', shift)
+};
+
+
+# Define a field that should be "and"ed
+sub joint_field {
+ my $self = shift;
+ my $field = shift;
+ my $value = shift;
+ $self->joint(
+ KorAP::VirtualCorpus::Doc->new($field)->value($value)
+ );
+};
+
+
+# Restrict VC by date
+sub from {
+ my $self = shift;
+ my ($year, $month) = @_;
+ my $doc = KorAP::VirtualCorpus::Doc->new('createDate')
+ ->type('date')
+ ->match('geq')
+ ->value($year . '-' . $month);
+ $self->joint($doc);
+};
+
+
+# Restrict VC by date
+sub to {
+ my $self = shift;
+ my ($year, $month) = @_;
+ my $doc = KorAP::VirtualCorpus::Doc->new('createDate')
+ ->type('date')
+ ->match('leq')
+ ->value($year . '-' . $month);
+ $self->joint($doc);
+};
+
+
+1;
+
+__END__
diff --git a/lib/KorAP/VirtualCorpus/Or.pm b/lib/KorAP/VirtualCorpus/Or.pm
new file mode 100644
index 0000000..a795ed3
--- /dev/null
+++ b/lib/KorAP/VirtualCorpus/Or.pm
@@ -0,0 +1,86 @@
+package KorAP::VirtualCorpus::Or;
+use strict;
+use warnings;
+use KorAP::VirtualCorpus::DocVec;
+use base 'KorAP::VirtualCorpus';
+
+
+# TODO:
+# Support comments!
+
+
+# Constructor
+sub new {
+ my $class = shift;
+ bless { ops => [@_] }, $class;
+};
+
+
+# Get koral type
+sub koral_type {
+ return 'Or';
+};
+
+
+# Get operands
+sub operands {
+ shift->{ops};
+};
+
+
+# Flatten the group
+sub flatten {
+ my $self = shift;
+ my %fields = ();
+ my @ops;
+
+ foreach (@{$self->{ops}}) {
+ if ($_->koral_type eq 'Doc') {
+ if ($_->type eq 'string' && $_->match eq 'eq') {
+ $fields{$_->key} //= [];
+ push @{$fields{$_->key}}, $_->value;
+ };
+ }
+ elsif ($_->koral_type eq 'DocVec') {
+ $fields{$_->key} //= [];
+ push @{$fields{$_->key}}, @{$_->value};
+ }
+ elsif ($_->koral_type eq 'Or') {
+ push @ops, @{$_->operands}
+ }
+ else {
+ push @ops, $_->flatten;
+ }
+ };
+
+ # Vectorize fields
+ foreach (sort keys %fields) {
+ push @ops, KorAP::VirtualCorpus::DocVec->new($_)->value(@{$fields{$_}});
+ };
+
+ if (@ops == 1) {
+ return $ops[0]->name($self->name);
+ };
+
+ $self->{ops} = \@ops;
+ return $self;
+};
+
+
+# Serialize to fragment
+sub _to_fragment {
+ my $self = shift;
+ my $json = '{';
+ $json .= '"@type":"koral:docGroup",';
+ $json .= '"operation":"operation:or",';
+ $json .= '"operands":[';
+ $json .= join(',', map { $_->_to_fragment } @{$self->{ops}});
+ $json .= ']';
+ # Set at the end, when all comments are done
+ $json .= $self->_commentparam_to_string;
+ $json .= '}';
+ return $json;
+};
+
+
+1;
diff --git a/list2vc.pl b/list2vc.pl
index 35a205a..3e31e50 100755
--- a/list2vc.pl
+++ b/list2vc.pl
@@ -1,251 +1,13 @@
#!/usr/bin/env perl
-package KorAP::VirtualCorpus;
use strict;
use warnings;
-
-
-# Get or set name of the VC
-sub name {
- my $self = shift;
- unless (@_) {
- return $self->{name};
- };
- $self->{name} = shift;
- return $self;
-};
-
-
-# Comment
-sub comment {
- my $self = shift;
- unless (@_) {
- return $self->{comment};
- };
- $self->{comment} //= [];
-
- push @{$self->{comment}}, shift;
- return $self;
-};
-
-
-# Quote utility function
-sub quote {
- shift;
- my $str = shift;
- $str =~ s/(["\\])/\\$1/g;
- return qq{"$str"};
-};
-
-
-# Escaped quote utility function
-sub equote {
- shift;
- my $str = shift;
- $str =~ s/(["\\])/\\$1/g;
- $str =~ s/(["\\])/\\$1/g;
- return '\\"' . $str . '\\"';
-};
-
-
-sub _commentparam_to_string {
- my $self = shift;
- my $comment = $self->_comment_to_string;
- if ($comment) {
- return qq!,"comment":"$comment"!;
- };
- return '';
-};
-
-
-sub _comment_to_string {
- my $self = shift;
- if (!$self->name && !$self->comment) {
- return '';
- };
-
- my $json = '';
- $json .= 'name:' . $self->equote($self->name) if $self->name;
- if ($self->name && $self->comment) {
- $json .= ','
- };
- $json .= join(',', @{$self->{comment}}) if $self->{comment};
-
- return $json;
-};
-
-
-# Stringify globally
-sub to_string {
- my $self = shift;
- ## Create collection object
-
- my $json = '{';
- $json .= '"@context":"http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",';
- $json .= '"collection":';
- $json .= $self->_to_fragment;
- # Set at the end, when all comments are done
- $json .= $self->_commentparam_to_string;
- return $json .= '}';
-};
-
-
-package KorAP::VirtualCorpus::Group;
-use strict;
-use warnings;
-use base 'KorAP::VirtualCorpus';
-
-
-# Construct a new VC group
-sub new {
- my $class = shift;
- bless {
- with => [],
- with_fields => {},
- without => [],
- without_fields => {},
- }, $class;
-};
-
-
-# Define an operand to be "or"ed
-sub with {
- my $self = shift;
- push @{$self->{with}}, shift;
-};
-
-
-# Define a field that should be "or"ed
-sub with_field {
- my $self = shift;
- my $field = shift;
- push @{$self->{with_fields}->{$field}}, shift;
-};
-
-# Define an operand to be "and"ed
-sub without {
- my $self = shift;
- push @{$self->{without}}, shift;
-};
-
-
-# Define a field that should be "and"ed
-sub without_field {
- my $self = shift;
- my $field = shift;
- push @{$self->{without_fields}->{$field}}, shift;
-};
-
-
-# VC contains only with fields
-sub only_with_fields {
- my $self = shift;
-
- if (keys %{$self->{without_fields}} || @{$self->{with}} || @{$self->{without}}) {
- return 0;
- };
-
- return 1;
-};
-
-
-# Create a document vector field
-sub _doc_vec {
- my $field = shift;
- my $vec = shift;
- my $json = '{';
- $json .= '"@type":"koral:doc",';
- $json .= '"key":"' . $field . '",';
- $json .= '"match":"match:eq",';
- $json .= '"value":[';
- $json .= join ',', map { '"' . $_ . '"' } @$vec;
- $json .= ']';
- $json .= '},';
- return $json;
-}
-
-
-# Stringify fragment
-sub _to_fragment {
- my $self = shift;
-
- my $json = '{';
- $json .= '"@type":"koral:docGroup",';
-
- # Make the outer group "and"
- if (keys %{$self->{without_fields}}) {
- $json .= '"operation":"operation:and",';
- $json .= '"operands":[';
-
- foreach my $field (sort keys %{$self->{without_fields}}) {
- unless (@{$self->{without_fields}->{$field}}) {
- next;
- };
- $json .= _doc_vec($field, $self->{without_fields}->{$field});
- };
-
- # Remove the last comma
- chop $json;
-
- $json .= ']';
- }
-
- elsif (keys %{$self->{with_fields}} || @{$self->{with}}) {
- $json .= '"operation":"operation:or",';
-
- $json .= '"operands":[';
-
- # Flatten embedded "or"-VCs
- foreach my $op (@{$self->{with}}) {
-
- # The embedded VC has only extending fields
- if ($op->only_with_fields) {
-
- $self->comment('embed:[' . $op->_comment_to_string . ']');
-
- foreach my $k (keys %{$op->{with_fields}}) {
- foreach my $v (@{$op->{with_fields}->{$k}}) {
- $self->with_field($k, $v);
- };
- };
- }
-
- # Embed complex VC
- else {
- $json .= $op->_to_fragment . ',';
- };
- };
-
- foreach my $field (sort keys %{$self->{with_fields}}) {
- unless (@{$self->{with_fields}->{$field}}) {
- next;
- };
- $json .= _doc_vec($field, $self->{with_fields}->{$field});
- };
-
- # Remove the last comma
- chop $json;
-
- $json .= ']';
- }
-
- # No operands in the group
- else {
- # Remove the last comma after the comment
- chop $json;
- };
-
- # Set at the end, when all comments are done
- $json .= $self->_commentparam_to_string;
- return $json . '}';
-};
-
-
-package main;
-use strict;
-use warnings;
+use lib 'lib';
+use KorAP::VirtualCorpus::Group;
# 2020-05-20
# Preliminary support for C2 def-files.
+# 2020-05-29
+# Introduce optimizable object system.
our @ARGV;
@@ -352,24 +114,24 @@
# Convert C2 sigle to KorAP form
$value =~ s!^([^/]+?/[^\.]+?)\.(.+?)$!$1\/$2!;
- ${$vc}->with_field(textSigle => $value);
+ ${$vc}->union_field(textSigle => $value);
}
# Add doc field
elsif ($key eq 'doc') {
- ${$vc}->with_field(docSigle => $value);
+ ${$vc}->union_field(docSigle => $value);
}
# Add corpus field
elsif ($key eq 'corpus') {
- ${$vc}->with_field(corpusSigle => $value);
+ ${$vc}->union_field(corpusSigle => $value);
}
# Add corpus field
elsif ($key eq 'cn') {
# Korpussigle, z.B. 'F97 Frankfurter Allgemeine 1997'
if ($value =~ m!^([^\/\s]+)(?:\s.+?)?$!) {
- ${$vc}->with_field(corpusSigle => $1);
+ ${$vc}->union_field(corpusSigle => $1);
};
}
@@ -430,19 +192,108 @@
next;
};
- $$vc->with($all_vcs{$value});
+ $$vc->union($all_vcs{$value}->clone->to_koral);
}
+ # AND definition
+ elsif ($key eq 'and') {
+ unless (defined $all_vcs{$value}) {
+ # warn 'VC ' . $value . ' not defined';
+ # exit(1);
+ next;
+ };
+
+ $$vc->joint($all_vcs{$value}->clone->to_koral);
+ }
+
+ # Source of the corpus
+ elsif ($key eq 'ql') {
+ # Quellenname, z.B. "Neue Zürcher Zeitung"
+ $$vc->union_field(corpusTitle => $value);
+ }
+
+ elsif ($key eq 'sub') {
+ # "Sub" is the difference - it is the "and not" operation.
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'co') {
+ # Country, z.B. DE für Text in Deutschland erschienen
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'tl') {
+ # Textlength, Bereich von Texten der angegebenen Länge [in Anz. Wörtern]
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'ts') {
+ # Textsorte, z.B. "Bericht"
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'th') {
+ # Thema, z.B. "Sport - Fußball"
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'red') {
+ # Reduktionsfaktor
+ # Wert zw. 1-99%: virt. Korpus wird auf diesen Wert
+ # reduziert. Modus: feste Reduzierung, nicht variabel.
+ warn $key . ' is not yet supported';
+ }
+
+ elsif ($key eq 'thprob') {
+ # ThemaProbability
+ # Wert, der für <th>Thema verwendet wird um zu bestimmen, ab welchem
+ # Zuverläßigkeitswert ein Thema übernommen wird
+ }
+
+
# Add reduction value as a comment
elsif ($key eq 'redabs') {
# "red. Anz. Texte
# absoluter Wert der durch Reduktion zu erzielende Anzahl Texte"
$$vc->comment('redabs:' . $value);
+ warn $key . ' is not yet supported';
+ }
+
+ # Add reduction value as a comment
+ elsif ($key eq 'date') {
+ # Supports two pattern schemes:
+ # m1=Year1/Month1 bis Year2/Month2
+ # Datumsbereich Schema 1: z.B. "2000/01 bis 2010/12"
+
+ # Schema 1
+ if ($value =~ m!^(?:m1\s*=\s*)?\s*(\d+)\/(\d+) bis (\d+)\/(\d+)\s*$!s) {
+ my ($y1, $m1, $y2, $m2) = ($1, $2, $3, $4);
+ if ($m1 < 10) {
+ $m1 = '0' . (0+$m1);
+ };
+ if ($m2 < 10) {
+ $m2 = '0' . (0+$m2);
+ };
+ $$vc->from($y1, $m1);
+ $$vc->to($y2, $m2);
+ }
+
+ # Scheme 2
+ elsif ($value =~ m!^\s*\d{4}-\d{4}\s+und\s+\d{1,2}-\d{1,2}\s*$!) {
+ # m2=Year1-Year2 und Month1-Month2
+ # Datumsbereich Schema 2: z.B. "1990-2000 und 06-06"
+
+ warn 'Second date scheme not yet supported!'
+ }
+
+ else {
+ warn 'Unknown date scheme ' . $value;
+ };
}
# Unknown
else {
- # warn $key . ' is an unknown field';
+ warn $key . ' is an unknown field';
};
};
diff --git a/t/list2vc-def.t b/t/list2vc-def.t
index baaefda..a3b60e7 100644
--- a/t/list2vc-def.t
+++ b/t/list2vc-def.t
@@ -44,38 +44,41 @@
my $list3 = catfile(dirname(__FILE__), 'data', 'list3.def');
+
# Check JSON
# Only return extended area
$json = decode_json(join('', `$script $list3`));
-is($json->{'collection'}->{'@type'}, 'koral:docGroup', 'type');
-is($json->{'collection'}->{'operation'}, 'operation:or', 'operation');
+is($json->{'collection'}->{'@type'}, 'koral:doc', 'type');
+
+
is($json->{'collection'}->{'comment'}, 'name:"VAS-N91 (Stand \"2013\", korr. 2017)"', 'type');
-$op1 = $json->{'collection'}->{'operands'}->[0];
+$op1 = $json->{'collection'};
is($op1->{'@type'}, 'koral:doc', 'type');
is($op1->{'key'}, 'textSigle', 'key');
is($op1->{'match'}, 'match:eq', 'match');
is($op1->{'value'}->[0], "A00/APR/23232", 'value');
is($op1->{'value'}->[1], "A00/APR/23233", 'value');
-
my $list4 = catfile(dirname(__FILE__), 'data', 'list4.def');
# Only contains intended area
$json = decode_json(join('', `$script $list4`));
is($json->{'collection'}->{'@type'}, 'koral:docGroup', 'type');
-is($json->{'collection'}->{'operation'}, 'operation:or', 'operation');
+is($json->{'collection'}->{'comment'}, 'name:"VAS N91"', 'name');
like($json->{'collection'}->{'comment'}, qr!^name:"VAS N91"!, 'name');
-like($json->{'collection'}->{'comment'}, qr!embed:\[name:"Berliner Zeitung",redabs:143237\]!, 'embed');
-like($json->{'collection'}->{'comment'}, qr!embed:\[name:"Frankfurter Allgemeine",redabs:301166\]!, 'embed');
-$op1 = $json->{'collection'}->{'operands'}->[0];
-is($op1->{'@type'}, 'koral:doc', 'type');
-is($op1->{'key'}, 'corpusSigle', 'key');
-is($op1->{'match'}, 'match:eq', 'match');
-is($op1->{'value'}->[0], "F97", 'value');
-is($op1->{'value'}->[1], "F99", 'value');
+
+my $bz = $json->{'collection'}->{operands}->[0]->{operands}->[0];
+is($bz->{operation}, 'operation:and', 'Intersection');
+is(scalar @{$bz->{operands}}, 3, 'Flatten operands');
+
+my $faz = $json->{'collection'}->{operands}->[0]->{operands}->[1];
+is($faz->{'@type'}, 'koral:doc', 'DocVec');
+is($faz->{value}->[0], 'F97', 'Value');
+is($faz->{value}->[1], 'F99', 'Value');
done_testing;
+__END__
diff --git a/t/list2vc-obj.t b/t/list2vc-obj.t
new file mode 100644
index 0000000..d4bbd80
--- /dev/null
+++ b/t/list2vc-obj.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+use Data::Dumper;
+use Mojo::JSON 'decode_json';
+
+require_ok 'KorAP::VirtualCorpus::Group';
+
+my $vc = KorAP::VirtualCorpus::Group->new;
+$vc->union_field('author', 'Goethe');
+$vc->union_field('author', 'Schiller');
+$vc->joint_field('author', 'Fontane');
+
+
+my $json = decode_json $vc->to_koral->to_string;
+
+is($json->{collection}->{operation}, 'operation:and');
+is($json->{collection}->{operands}->[0]->{'@type'}, 'koral:doc');
+is($json->{collection}->{operands}->[0]->{'value'}->[0], 'Goethe');
+is($json->{collection}->{operands}->[0]->{'value'}->[1], 'Schiller');
+
+done_testing;