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