Optimize full text search with a CTE
authorDan Scott <dscott@laurentian.ca>
Wed, 16 May 2012 04:29:11 +0000 (00:29 -0400)
committerMike Rylander <mrylander@gmail.com>
Thu, 17 May 2012 15:26:31 +0000 (11:26 -0400)
The tsquery used directly in a JOIN was resulting in bad
plan with lots of nested loops and operations. Using a CTE
to define the query improves the plan.

Also, instead of ARRAY_AGG(DISTINCT foo), just return the
whole thing in an array. Not great for dupes, but we can
handle those inside search.query_parser_fts if we really
need to.

Signed-off-by: Dan Scott <dscott@laurentian.ca>
Signed-off-by: Mike Rylander <mrylander@gmail.com>

Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm

index efd83e0..a405c1f 100644 (file)
@@ -655,10 +655,20 @@ sub toSQL {
     } else {
         $flat_where = "AND $flat_where";
     }
+    my $with = $$flat_plan{with};
+
+    # Need an array for query parser db function; this gives a better plan
+    # than the ARRAY_AGG(DISTINCT m.source) option as of PostgreSQL 9.1
+    my $agg_records = 'ARRAY[m.source] AS records';
+    if ($key =~ /metarecord/) {
+        # metarecord searches still require the ARRAY_AGG approach
+        $agg_records = 'ARRAY_AGG(DISTINCT m.source) AS records';
+    }
 
     my $sql = <<SQL;
+$with
 SELECT  $key AS id,
-        ARRAY_ACCUM(DISTINCT m.source) AS records,
+        $agg_records,
         $rel AS rel,
         $rank AS rank, 
         FIRST(mrd.attrs->'date1') AS tie_break
@@ -712,6 +722,7 @@ sub flatten {
 
     my $from = shift || '';
     my $where = shift || '(';
+    my $with = '';
 
     my @rank_list;
     for my $node ( @{$self->query_nodes} ) {
@@ -730,11 +741,12 @@ sub flatten {
                 my $node_rank = 'COALESCE(' . $node->rank . " * ${talias}.weight, 0.0)";
 
                 my $core_limit = $self->QueryParser->core_limit || 25000;
-                $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight, x.tsq /* search */\n\t\t  FROM  $table AS fe";
+                $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight, xq.tsq /* search */\n\t\t  FROM  $table AS fe";
                 $from .= "\n\t\t\tJOIN config.metabib_field AS fe_weight ON (fe_weight.id = fe.field)";
 
                 if ($node->dummy_count < @{$node->only_atoms} ) {
-                    $from .= "\n\t\t\tJOIN (SELECT ". $node->tsquery ." AS tsq ) AS x ON (fe.index_vector @@ x.tsq)";
+                    $with.= "\n\t\t\tWITH xq AS (SELECT ". $node->tsquery ." AS tsq )";
+                    $from .= "\n\t\t\tJOIN xq ON (fe.index_vector @@ xq.tsq)";
                 } else {
                     $from .= "\n\t\t\t, (SELECT NULL::tsquery AS tsq ) AS x";
                 }
@@ -810,7 +822,7 @@ sub flatten {
         }
     }
 
-    return { rank_list => \@rank_list, from => $from, where => $where.')' };
+    return { rank_list => \@rank_list, from => $from, where => $where.')', with => $with };
 
 }