Fixed sorting for unranked fields
Change-Id: I3ed499ec3407749d10324d978071f09563701dc9
diff --git a/lib/Krawfish/Compile.pm b/lib/Krawfish/Compile.pm
index 31cecfa..30460e6 100644
--- a/lib/Krawfish/Compile.pm
+++ b/lib/Krawfish/Compile.pm
@@ -28,7 +28,7 @@
# queries on the root level instead of the intermediate
# compile level
-use constant DEBUG => 1;
+use constant DEBUG => 0;
# Return match object
@@ -128,7 +128,7 @@
my $self = shift;
if (DEBUG) {
- print_log('compile', 'Compile aggregation');
+ print_log('compile', 'Compile aggregation with ' . ref($self));
};
# Get result object
@@ -165,10 +165,13 @@
my $query = $self->{query};
if (DEBUG) {
- print_log('compile', 'Check if ' . $query . ' does compiling');
+ print_log('compile', 'Check if ' . ref($query) . ' does compiling');
};
if (Role::Tiny::does_role($query, 'Krawfish::Compile')) {
+ if (DEBUG) {
+ print_log('compile', 'Add result from ' . ref($query));
+ };
$query->result($result)->compile;
};
diff --git a/lib/Krawfish/Compile/Cluster/Limit.pm b/lib/Krawfish/Compile/Cluster/Limit.pm
index d7aa71a..da6af96 100644
--- a/lib/Krawfish/Compile/Cluster/Limit.pm
+++ b/lib/Krawfish/Compile/Cluster/Limit.pm
@@ -6,6 +6,8 @@
with 'Krawfish::Compile';
+# TODO:
+# Rename to offset!
use constant DEBUG => 0;
diff --git a/lib/Krawfish/Compile/Segment/Bundle.pm b/lib/Krawfish/Compile/Segment/Bundle.pm
index 061bebb..b5eea07 100644
--- a/lib/Krawfish/Compile/Segment/Bundle.pm
+++ b/lib/Krawfish/Compile/Segment/Bundle.pm
@@ -12,7 +12,7 @@
# (or bundles of bundles of postings) sorted by a certain criterion.
-use constant DEBUG => 0;
+use constant DEBUG => 1;
# Bundle the current match
diff --git a/lib/Krawfish/Compile/Segment/BundleDocs.pm b/lib/Krawfish/Compile/Segment/BundleDocs.pm
index b61ec9d..ca223df 100644
--- a/lib/Krawfish/Compile/Segment/BundleDocs.pm
+++ b/lib/Krawfish/Compile/Segment/BundleDocs.pm
@@ -19,7 +19,7 @@
# is relevant.
# That's why next_doc is identical to next_bundle-
-use constant DEBUG => 0;
+use constant DEBUG => 1;
sub new {
my $class = shift;
diff --git a/lib/Krawfish/Compile/Segment/Sort.pm b/lib/Krawfish/Compile/Segment/Sort.pm
index bc84760..e7f4174 100644
--- a/lib/Krawfish/Compile/Segment/Sort.pm
+++ b/lib/Krawfish/Compile/Segment/Sort.pm
@@ -1,5 +1,6 @@
package Krawfish::Compile::Segment::Sort;
use Krawfish::Util::String qw/squote/;
+use Krawfish::Util::Constants qw/MAX_TOP_K/;
use Krawfish::Util::PriorityQueue::PerDoc;
use Krawfish::Koral::Result::Match;
use Krawfish::Posting::Bundle;
@@ -54,7 +55,7 @@
use constant {
- DEBUG => 0,
+ DEBUG => 1,
RANK => 0,
SAME => 1,
VALUE => 2,
@@ -74,7 +75,7 @@
# TODO:
# Check for mandatory parameters
#
- my $query = $param{query};
+ my $query = $param{query};
unless (Role::Tiny::does_role($query, 'Krawfish::Compile::Segment::Bundle')) {
warn 'The query is no bundled query';
@@ -90,7 +91,7 @@
# Set top_k if not yet set
# - to be honest, heap sort is probably not the
# best approach for a full search
- $top_k //= $segment->max_rank;
+ $top_k //= MAX_TOP_K;
# The maximum ranking value may be used
# by outside filters to know in advance,
@@ -120,6 +121,7 @@
query => $query,
queue => $queue,
max_rank_ref => $max_rank_ref,
+ max_rank => $segment->max_rank,
# TODO:
# Rename to criterion
@@ -201,6 +203,13 @@
print_log('sort', "Check rank $rank against max rank " . $$max_rank_ref);
};
+ # Rank is not given, because the field is not defined
+ # Always sort this to be at the end, no matter what the order is
+ # TODO:
+ # Alternatively the rank could be max_rank from rank_for!
+# if ($rank == 0) {
+# $rank = $self->{max_rank};
+# };
# Precheck if the match is relevant
if ($rank > $$max_rank_ref) {
@@ -236,6 +245,10 @@
print_log('sort', 'Get list ranking of ' . Dumper($array));
};
+ if (DEBUG) {
+ print_log('sort', 'First pass sorting finished','----------------');
+ };
+
# Get the rank reference (new);
$self->{buffer} = $array;
};
@@ -279,14 +292,14 @@
my $top_bundle = $self->{buffer}->[$pos];
if (DEBUG) {
- print_log('sort_after', "Move to next bundle at $pos, which is " .
+ print_log('sort', "Move to next bundle at $pos, which is " .
$top_bundle->[VALUE]);
};
my $rank = $top_bundle->[RANK];
if (DEBUG) {
- print_log('sort_after', "Create new bundle from prio at $pos starting with " .
+ print_log('sort', "Create new bundle from prio at $pos starting with " .
$top_bundle->[VALUE]->to_string);
};
@@ -335,7 +348,9 @@
my $self = shift;
my $str = 'sort(';
$str .= $self->{sort}->to_string;
- $str .= ',0-' . $self->{top_k} if $self->{top_k};
+ if ($self->{top_k} != MAX_TOP_K) {
+ $str .= ',0-' . $self->{top_k};
+ };
$str .= ':' . $self->{query}->to_string;
return $str . ')';
};
diff --git a/lib/Krawfish/Compile/Segment/Sort/Field.pm b/lib/Krawfish/Compile/Segment/Sort/Field.pm
index ac441e4..fa366ac 100644
--- a/lib/Krawfish/Compile/Segment/Sort/Field.pm
+++ b/lib/Krawfish/Compile/Segment/Sort/Field.pm
@@ -18,6 +18,10 @@
# TODO:
# Return max rank for unknown fields!
+# TODO:
+# This may need to be an inflatable
+
+
sub new {
my $class = shift;
@@ -59,7 +63,7 @@
sub rank_for {
my ($self, $doc_id) = @_;
- return $self->{rank}->rank_for($doc_id);
+ return $self->{rank}->rank_for($doc_id) || ($self->max_rank + 1);
# Get rank if rank is littler than value
# my $value = shift;
@@ -80,11 +84,11 @@
$_[0]->{field_id};
};
-# TODO:
-# This may need to be an inflatable
+
+# Stringification
sub to_string {
my $self = shift;
- return 'field=#' . $self->{field_id} . ($_->{desc} ? '>' : '<')
+ return 'field=#' . $self->{field_id} . ($self->{desc} ? '>' : '<')
};
1;
diff --git a/lib/Krawfish/Compile/Segment/SortAfter.pm b/lib/Krawfish/Compile/Segment/SortAfter.pm
index ee964b6..567fe0f 100644
--- a/lib/Krawfish/Compile/Segment/SortAfter.pm
+++ b/lib/Krawfish/Compile/Segment/SortAfter.pm
@@ -1,9 +1,10 @@
package Krawfish::Compile::Segment::SortAfter;
use Data::Dumper;
use Krawfish::Log;
+use Krawfish::Util::Constants qw/MAX_TOP_K/;
use strict;
use warnings;
-use Role::Tiny;
+use Role::Tiny::With;
with 'Krawfish::Compile::Segment::Sort';
@@ -25,7 +26,7 @@
use constant {
- DEBUG => 0,
+ DEBUG => 1,
RANK => 0,
SAME => 1,
VALUE => 2,
@@ -45,7 +46,7 @@
# This is the sort criterion
my $sort = $param{sort};
- $top_k //= $segment->max_rank;
+ $top_k //= MAX_TOP_K;
if (DEBUG) {
print_log('sort_after', 'Initiate follow up sort');
@@ -71,7 +72,7 @@
print_log('sort_after', 'Move to next bundle');
};
- $_[0]->{current_bundle} = undef;
+ $self->{current_bundle} = undef;
# Already served enough
if ($self->{pos} > $self->{top_k}) {
@@ -148,6 +149,10 @@
# Get stored rank
$rank = $sort->rank_for($posting->doc_id);
+ if (DEBUG) {
+ print_log('sort_after', 'Rank for doc id ' . $posting->doc_id . " is $rank");
+ };
+
# Checking for $$max_rank_ref is not useful here,
# as the bundles are already bundled and skipping bundles
# using next_doc() and preview_doc_id() is not beneficial.
@@ -158,9 +163,11 @@
# Get the sorted array (which has still the ranking structure etc.)
my $array = $queue->reverse_array;
- print_log('sort_after', 'Get list ranking of ' . Dumper($array)) if DEBUG;
+ if (DEBUG && 0) {
+ print_log('sort_after', 'Get list ranking of ' . Dumper($array));
+ };
- if (DEBUG) {
+ if (DEBUG && $self->{current_bundle}) {
print_log(
'sort_after',
'New current bundle is ' . $self->{current_bundle}->to_string
@@ -190,15 +197,4 @@
);
};
-
-# Stringification
-sub to_string {
- my $self = shift;
- my $str = 'sort(';
- $str .= $self->{sort}->to_string;
- $str .= ',0-' . $self->{top_k} if $self->{top_k};
- $str .= ':' . $self->{query}->to_string;
- return $str . ')';
-};
-
1;
diff --git a/lib/Krawfish/Index/Fields/Direction.pm b/lib/Krawfish/Index/Fields/Direction.pm
index a7196b5..9e61730 100644
--- a/lib/Krawfish/Index/Fields/Direction.pm
+++ b/lib/Krawfish/Index/Fields/Direction.pm
@@ -44,7 +44,7 @@
);
};
- return $self->[$doc_id] // 0;
+ return $self->[$doc_id];
};
diff --git a/lib/Krawfish/Koral/Compile/Node/Sort.pm b/lib/Krawfish/Koral/Compile/Node/Sort.pm
index a68a4ea..f8e4f86 100644
--- a/lib/Krawfish/Koral/Compile/Node/Sort.pm
+++ b/lib/Krawfish/Koral/Compile/Node/Sort.pm
@@ -8,7 +8,7 @@
use warnings;
use constant {
- DEBUG => 0,
+ DEBUG => 1,
UNIQUE => 'id'
};
@@ -65,8 +65,18 @@
sub optimize {
my ($self, $segment) = @_;
+ if (DEBUG) {
+ print_log('kq_n_sort', 'Optimize query ' . ref($self->{query}) . '=' .
+ $self->{query}->to_string);
+ };
+
my $query = $self->{query}->optimize($segment);
+ if (DEBUG) {
+ print_log('kq_n_sort', 'Optimized query is now ' . ref($query) . '=' .
+ $query->to_string);
+ };
+
if ($query->max_freq == 0) {
return Krawfish::Compile::Segment::Nowhere->new;
};
@@ -91,6 +101,9 @@
my $sort = $self->{sort}->optimize($segment);
unless ($sort) {
+ print_log('kq_n_sort', 'Sort is not optimizable: ' . $self->{sort}->to_string);
+
+ warn 'Do not sort on a non-sortable field!';
# TODO:
# This needs to be checked in identify!
@@ -108,6 +121,10 @@
# the sorting criterion may very well be important, so a NoSort-query
# also needs to add the query criterion!
return $self->{query};
+ }
+
+ elsif (DEBUG) {
+ print_log('kq_n_sort', 'Optimize sort criterion: ' . $sort->to_string);
};
# TODO:
diff --git a/lib/Krawfish/Koral/Compile/Sort/Field.pm b/lib/Krawfish/Koral/Compile/Sort/Field.pm
index ff95e89..52a8f2d 100644
--- a/lib/Krawfish/Koral/Compile/Sort/Field.pm
+++ b/lib/Krawfish/Koral/Compile/Sort/Field.pm
@@ -30,8 +30,8 @@
return Krawfish::Compile::Segment::Sort::Field->new(
$segment,
- $_[0]->{field}->term_id,
- $_[0]->{desc}
+ $self->{field}->term_id,
+ $self->{desc}
);
};
diff --git a/lib/Krawfish/Koral/Query/Filter.pm b/lib/Krawfish/Koral/Query/Filter.pm
index 4ba42f4..c852aff 100644
--- a/lib/Krawfish/Koral/Query/Filter.pm
+++ b/lib/Krawfish/Koral/Query/Filter.pm
@@ -22,7 +22,7 @@
#
# next([Der],previous(filter(author=goethe,[Mann]),[alte&ADJ]))
-use constant DEBUG => 0;
+use constant DEBUG => 1;
sub new {
my $class = shift;
@@ -102,6 +102,10 @@
sub optimize {
my ($self, $segment) = @_;
+ if (DEBUG) {
+ print_log('kq_filter', 'Optimize filter ' . $self->to_string);
+ };
+
# Optimize corpus
my $corpus = $self->corpus->optimize($segment);
@@ -117,13 +121,28 @@
# Optimize span
my $span = $self->operand->optimize($segment);
+ if (DEBUG) {
+ print_log('kq_filter', 'Operands are now ' .
+ ref($span) . ':' . $span->to_string . ' and ' .
+ ref($corpus) . ':' . $corpus->to_string);
+ };
+
# Filter would rule out everything
if ($span->max_freq == 0) {
return Krawfish::Query::Nowhere->new;
};
# Filter the span with the corpus
- return $span->filter_by($corpus);
+ my $filter = $span->filter_by($corpus);
+
+ if (DEBUG) {
+ print_log(
+ 'kq_filter',
+ 'Optimized filter query is ' . ref($filter) . ':' . $filter->to_string
+ );
+ };
+
+ return $filter;
};
diff --git a/lib/Krawfish/Util/Constants.pm b/lib/Krawfish/Util/Constants.pm
index 2714767..31d6f2b 100644
--- a/lib/Krawfish/Util/Constants.pm
+++ b/lib/Krawfish/Util/Constants.pm
@@ -15,7 +15,8 @@
REL_L_PREF => '>',
REL_R_PREF => '<',
PTI_CLASS => 0, # Payload identifier for classes
- NOMOREDOCS => 4_294_967_295 # (maximum value for 32 bit)
+ NOMOREDOCS => 4_294_967_295, # (maximum value for 32 bit)
+ MAX_TOP_K => 4_294_967_295
};
our $ANNO_PREFIX_RE = qr/(?:\:|\-|\@|\>|\<)/;
@@ -32,7 +33,8 @@
REL_R_PREF
PTI_CLASS
$ANNO_PREFIX_RE
- NOMOREDOCS/);
+ NOMOREDOCS
+ MAX_TOP_K/);
our %EXPORT_TAGS = (
PREFIX => [qw/KEY_PREF
diff --git a/t/compile/limit.t b/t/compile/cluster/limit.t
similarity index 100%
rename from t/compile/limit.t
rename to t/compile/cluster/limit.t
diff --git a/t/compile/segment/group_fields.t b/t/compile/segment/group_fields.t
index 5097007..0473236 100644
--- a/t/compile/segment/group_fields.t
+++ b/t/compile/segment/group_fields.t
@@ -264,6 +264,9 @@
"gFields('unknown','category','genre':filter(aa|bb,[1]))",
'Stringification');
+diag 'Respect unknown field';
+# as it may only be unknown to this node!
+
ok($query = $query->identify($index->dict)->optimize($index->segment), 'Optimize');
is($query->to_string,
'gFields(#5,#7:filter(or(#12,#14),[1]))',
diff --git a/t/compile/segment/sort_field.t b/t/compile/segment/sort_field.t
index 581d32c..c758b55 100644
--- a/t/compile/segment/sort_field.t
+++ b/t/compile/segment/sort_field.t
@@ -12,6 +12,8 @@
ok($index->introduce_field('id', 'NUM'), 'Introduce field as sortable');
ok($index->introduce_field('author', 'DE'), 'Introduce field as sortable');
+ok($index->introduce_field('genre', 'DE'), 'Introduce field as sortable');
+ok($index->introduce_field('title', 'DE'), 'Introduce field as sortable');
ok_index($index, {
id => 2,
@@ -42,6 +44,7 @@
id => 5,
author => 'Michael',
genre => 'newsletter',
+ title => 'Your new way to success!',
age => 7
} => [qw/aa bb/], 'Add complex document');
@@ -50,7 +53,7 @@
my $koral = Krawfish::Koral->new;
my $qb = $koral->query_builder;
my $mb = $koral->compilation_builder;
-my $query;
+my ($query, $result, $clone);
$koral->query(
@@ -62,7 +65,7 @@
ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'optimize');
-is($query->to_string, 'constr(pos=2:#10,filter(#12,[1]))', 'Stringification');
+is($query->to_string, 'constr(pos=2:#11,filter(#13,[1]))', 'Stringification');
# Check normal search
matches($query, ['[0:0-2]','[2:0-2]','[3:0-2]','[4:0-2]'], 'Query');
@@ -79,7 +82,7 @@
ok($query = $query->identify($index->dict)->optimize($index->segment), 'optimize');
is($query->to_string,
- 'sort(field=#1<,0-5:bundleDocs(constr(pos=2:#10,filter(#12,[1]))))',
+ 'sort(field=#1<:bundleDocs(constr(pos=2:#11,filter(#13,[1]))))',
'Stringification');
@@ -124,13 +127,13 @@
ok($query = $query->identify($index->dict), 'Identify');
is($query->to_string(1),
- 'sort(field=#1<:sort(field=#2<:filter(#10#12,[1])))',
+ 'sort(field=#1<:sort(field=#2<:filter(#11#13,[1])))',
'Stringification');
ok($query = $query->optimize($index->segment), 'Optimize');
is($query->to_string,
- 'sort(field=#1<,0-5:sort(field=#2<,0-5:bundleDocs(constr(pos=2:#10,filter(#12,[1])))))',
+ 'sort(field=#1<:sort(field=#2<:bundleDocs(constr(pos=2:#11,filter(#13,[1])))))',
'Stringification');
# 0:Peter, 1:Julian!, 2:Abraham, 3:Fritz, 4:Michael
@@ -146,16 +149,18 @@
is($query->current_bundle->to_string, '[[[0:0-2]]]', 'Stringification');
ok(!$query->next_bundle, 'No more next bundle');
-
# Add to more documents
+# 5
ok_index($index, {
id => 8,
author => 'Fritz'
} => [qw/aa bb/], 'Add complex document');
+# 6
ok_index($index, {
id => 9,
author => 'Michael'
} => [qw/aa bb/], 'Add complex document');
+# 7
ok_index($index, {
id => 7,
author => 'Michael'
@@ -163,7 +168,6 @@
ok($index->commit, 'Commit data');
-
# New query - sort by author
$koral = Krawfish::Koral->new;
$qb = $koral->query_builder;
@@ -186,7 +190,7 @@
ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'Optimize');
is($query->to_string,
- 'sort(field=#1<,0-8:sort(field=#2<,0-8:bundleDocs(constr(pos=2:#10,filter(#12,[1])))))',
+ 'sort(field=#1<:sort(field=#2<:bundleDocs(constr(pos=2:#11,filter(#13,[1])))))',
'Stringification');
@@ -208,6 +212,7 @@
is($query->current_bundle->to_string, '[[[0:0-2]]]', 'Stringification');
ok(!$query->next_bundle, 'No more next bundles');
+$koral = Krawfish::Koral->new;
$koral->query($qb->seq($qb->token('aa'),$qb->token('bb')));
$koral->compilation($mb->sort_by($mb->s_field('author')));
@@ -216,9 +221,9 @@
# Run query
ok($query = $query->identify($index->dict)->optimize($index->segment), 'Optimize');
-ok(my $clone = $query->clone, 'Clone query');
+ok($clone = $query->clone, 'Clone query');
-# 2, [3, 5], [4,7,6], 0
+# 2, [3, 5], [4,7,7,6], 0
ok($query->next, 'Move to next bundle');
is($query->current->to_string, '[2:0-2]', 'Stringification');
ok($query->next, 'Move to next bundle');
@@ -239,13 +244,44 @@
# Run clone
-ok(my $result = $clone->compile->inflate($index->dict), 'Run clone');
+ok($result = $clone->compile->inflate($index->dict), 'Run clone');
is($result->to_string,
'[matches=[2:0-2][3:0-2][5:0-2][4:0-2][7:0-2][7:2-4][6:0-2][0:0-2]]',
'Stringification');
-diag 'Deal with non-ranked fields';
+
+# Check with non-ranked sorting field (title)
+$koral = Krawfish::Koral->new;
+$koral->query(
+ $qb->bool_or(
+ $qb->token('aa'),
+ $qb->token('bb')
+ )
+);
+$koral->compilation(
+ $mb->sort_by($mb->s_field('genre')),
+ $mb->sort_by($mb->s_field('title'))
+);
+
+ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'Optimize');
+is($query->to_string,
+ 'sort(field=#1<:sort(field=#4<:sort(field=#3<:bundleDocs(filter(or(#13,#11),[1])))))',
+ 'Stringification');
+
+# genre,title,id
+# newsletter: 2=3=4 -> title: 4,2,3 -> id: irrelevant
+# novel: 0,1 -> title: - -> id: 0,1
+# -: 5=6=7 -> title: 5=6=7 -> id: 5,6,7
+# Finally:
+# [4,2,3,0,1,7,5,6]
+
+ok($result = $query->compile->inflate($index->dict), 'Run clone');
+is($result->to_string,
+ '[matches=[4:0-1][4:1-2][2:0-1][2:1-2][2:2-3][3:0-1][3:1-2][0:0-1][0:1-2][1:0-1][7:0-1][7:1-2][7:2-3][7:3-4][7:4-5][5:0-1][5:1-2][6:0-1][6:1-2]]',
+ 'Stringification');
+
+
diag 'Add sorting criteria in unbundling phase';
done_testing;
diff --git a/t/compile/segment/sort_field_empty.t b/t/compile/segment/sort_field_empty.t
new file mode 100644
index 0000000..c2d43ab
--- /dev/null
+++ b/t/compile/segment/sort_field_empty.t
@@ -0,0 +1,114 @@
+use Test::More;
+use Test::Krawfish;
+use Data::Dumper;
+use strict;
+use warnings;
+
+use_ok('Krawfish::Index');
+use_ok('Krawfish::Koral');
+
+# Create some documents
+my $index = Krawfish::Index->new;
+
+ok($index->introduce_field('id', 'NUM'), 'Introduce field as sortable');
+ok($index->introduce_field('author', 'DE'), 'Introduce field as sortable');
+ok($index->introduce_field('age', 'DE'), 'Introduce field as sortable');
+
+ok_index($index, {
+ id => 2,
+ age => 4,
+ author => 'Peter',
+} => [qw/aa/], 'Add complex document');
+ok_index($index, {
+ id => 3,
+ age => 4,
+ author => 'Julian',
+} => [qw/aa/], 'Add complex document');
+ok_index($index, {
+ age => 4,
+ id => 1,
+} => [qw/aa/], 'Add complex document');
+
+ok($index->commit, 'Commit data');
+
+my $koral = Krawfish::Koral->new;
+my $qb = $koral->query_builder;
+my $mb = $koral->compilation_builder;
+my ($query, $result, $clone);
+
+# Sort with an empty field
+$koral = Krawfish::Koral->new;
+$koral->query($qb->token('aa'));
+$koral->compilation(
+ $mb->sort_by($mb->s_field('author')),
+);
+ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'Optimize');
+is($query->to_string,
+ 'sort(field=#1<:sort(field=#2<:bundleDocs(filter(#8,[1]))))',
+ 'Stringification');
+ok($result = $query->compile->inflate($index->dict), 'Run clone');
+is($result->to_string,
+ '[matches=[1:0-1][0:0-1][2:0-1]]',
+ 'Stringification');
+
+# Sort in reverse order with an empty field
+$koral = Krawfish::Koral->new;
+$koral->query($qb->token('aa'));
+$koral->compilation(
+ $mb->sort_by($mb->s_field('author', 1)),
+);
+ok($query = $koral->to_query, 'To query');
+is($query->to_string,
+ "sort(field='id'<:sort(field='author'>:filter(aa,[1])))",
+ 'Stringification');
+ok($query = $query->identify($index->dict), 'Identify');
+is($query->to_string(1),
+ 'sort(field=#1<:sort(field=#2>:filter(#8,[1])))',
+ 'Stringification');
+ok($query = $query->optimize($index->segment), 'Optimize');
+is($query->to_string(1),
+ 'sort(field=#1<:sort(field=#2>:bundleDocs(filter(#8,[1]))))',
+ 'Stringification');
+ok($result = $query->compile->inflate($index->dict), 'Run clone');
+is($result->to_string,
+ '[matches=[0:0-1][1:0-1][2:0-1]]',
+ 'Stringification');
+
+
+# Sort after with an empty field
+$koral = Krawfish::Koral->new;
+$koral->query($qb->token('aa'));
+$koral->compilation(
+ $mb->sort_by($mb->s_field('age')),
+ $mb->sort_by($mb->s_field('author'))
+);
+ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'Optimize');
+is($query->to_string,
+ 'sort(field=#1<:sort(field=#2<:sort(field=#3<:bundleDocs(filter(#8,[1])))))',
+ 'Stringification');
+ok($result = $query->compile->inflate($index->dict), 'Run clone');
+is($result->to_string,
+ '[matches=[1:0-1][0:0-1][2:0-1]]',
+ 'Stringification');
+
+
+# Sort after in reverse order with an empty field
+$koral = Krawfish::Koral->new;
+$koral->query($qb->token('aa'));
+$koral->compilation(
+ $mb->sort_by($mb->s_field('age')),
+ $mb->sort_by($mb->s_field('author', 1))
+);
+ok($query = $koral->to_query->identify($index->dict)->optimize($index->segment), 'Optimize');
+is($query->to_string,
+ 'sort(field=#1<:sort(field=#2>:sort(field=#3<:bundleDocs(filter(#8,[1])))))',
+ 'Stringification');
+ok($result = $query->compile->inflate($index->dict), 'Run clone');
+is($result->to_string,
+ '[matches=[0:0-1][1:0-1][2:0-1]]',
+ 'Stringification');
+
+
+
+done_testing;
+__END__
diff --git a/t/koral/compile.t b/t/koral/compile/compile.t
similarity index 100%
rename from t/koral/compile.t
rename to t/koral/compile/compile.t