xref: /sqlite-3.40.0/test/fts3auto.test (revision 3fd65192)
199ebad90Sdan# 2011 June 10
299ebad90Sdan#
399ebad90Sdan#    May you do good and not evil.
499ebad90Sdan#    May you find forgiveness for yourself and forgive others.
599ebad90Sdan#    May you share freely, never taking more than you give.
699ebad90Sdan#
799ebad90Sdan#***********************************************************************
899ebad90Sdan#
999ebad90Sdan
1099ebad90Sdanset testdir [file dirname $argv0]
1199ebad90Sdansource $testdir/tester.tcl
1299ebad90Sdan
1399ebad90Sdan# If this build does not include FTS3, skip the tests in this file.
1499ebad90Sdan#
1599ebad90Sdanifcapable !fts3 { finish_test ; return }
1699ebad90Sdansource $testdir/fts3_common.tcl
1799ebad90Sdansource $testdir/malloc_common.tcl
1899ebad90Sdan
19*3fd65192Sdanset testprefix fts3auto
20*3fd65192Sdanset sfep $sqlite_fts3_enable_parentheses
21*3fd65192Sdanset sqlite_fts3_enable_parentheses 1
2299ebad90Sdan
23*3fd65192Sdan#--------------------------------------------------------------------------
24*3fd65192Sdan# Start of Tcl procs used by tests.
2599ebad90Sdan#
2699ebad90Sdanproc mit {blob} {
2799ebad90Sdan  set scan(littleEndian) i*
2899ebad90Sdan  set scan(bigEndian) I*
2999ebad90Sdan  binary scan $blob $scan($::tcl_platform(byteOrder)) r
3099ebad90Sdan  return $r
3199ebad90Sdan}
3299ebad90Sdandb func mit mit
3399ebad90Sdan
3499ebad90Sdanproc fix_near_expr {expr} {
3599ebad90Sdan  set out [list]
3699ebad90Sdan  lappend out [lindex $expr 0]
3799ebad90Sdan  foreach {a b} [lrange $expr 1 end] {
3899ebad90Sdan    if {[string match -nocase near $a]}   { set a 10 }
3999ebad90Sdan    if {[string match -nocase near/* $a]} { set a [string range $a 5 end] }
4099ebad90Sdan    lappend out $a
4199ebad90Sdan    lappend out $b
4299ebad90Sdan  }
4399ebad90Sdan  return $out
4499ebad90Sdan}
4599ebad90Sdan
46*3fd65192Sdanproc get_single_near_results {tbl expr arrayvar nullvar} {
47*3fd65192Sdan  upvar $arrayvar aMatchinfo
48*3fd65192Sdan  upvar $nullvar nullentry
49*3fd65192Sdan  catch {array unset aMatchinfo}
5099ebad90Sdan
5199ebad90Sdan  set expr [fix_near_expr $expr]
5299ebad90Sdan
5399ebad90Sdan  # Calculate the expected results using [fts3_near_match]. The following
5499ebad90Sdan  # loop populates the "hits" and "counts" arrays as follows:
5599ebad90Sdan  #
5699ebad90Sdan  #   1. For each document in the table that matches the NEAR expression,
5799ebad90Sdan  #      hits($docid) is set to 1. The set of docids that match the expression
5899ebad90Sdan  #      can therefore be found using [array names hits].
5999ebad90Sdan  #
6099ebad90Sdan  #   2. For each column of each document in the table, counts($docid,$iCol)
6199ebad90Sdan  #      is set to the -phrasecountvar output.
6299ebad90Sdan  #
6399ebad90Sdan  set res [list]
6499ebad90Sdan  catch { array unset hits }
6599ebad90Sdan  db eval "SELECT docid, * FROM $tbl" d {
6699ebad90Sdan    set iCol 0
6799ebad90Sdan    foreach col [lrange $d(*) 1 end] {
6899ebad90Sdan      set docid $d(docid)
6999ebad90Sdan      set hit [fts3_near_match $d($col) $expr -p counts($docid,$iCol)]
7099ebad90Sdan      if {$hit} { set hits($docid) 1 }
7199ebad90Sdan      incr iCol
7299ebad90Sdan    }
7399ebad90Sdan  }
7499ebad90Sdan  set nPhrase [expr ([llength $expr]+1)/2]
7599ebad90Sdan  set nCol $iCol
7699ebad90Sdan
7799ebad90Sdan  # This block populates the nHit and nDoc arrays. For each phrase/column
7899ebad90Sdan  # in the query/table, array elements are set as follows:
7999ebad90Sdan  #
8099ebad90Sdan  #   nHit($iPhrase,$iCol) - Total number of hits for phrase $iPhrase in
8199ebad90Sdan  #                          column $iCol.
8299ebad90Sdan  #
8399ebad90Sdan  #   nDoc($iPhrase,$iCol) - Number of documents with at least one hit for
8499ebad90Sdan  #                          phrase $iPhrase in column $iCol.
8599ebad90Sdan  #
8699ebad90Sdan  for {set iPhrase 0} {$iPhrase < $nPhrase} {incr iPhrase} {
8799ebad90Sdan    for {set iCol 0} {$iCol < $nCol} {incr iCol} {
8899ebad90Sdan      set nHit($iPhrase,$iCol) 0
8999ebad90Sdan      set nDoc($iPhrase,$iCol) 0
9099ebad90Sdan    }
9199ebad90Sdan  }
9299ebad90Sdan  foreach key [array names counts] {
9399ebad90Sdan    set iCol [lindex [split $key ,] 1]
9499ebad90Sdan    set iPhrase 0
9599ebad90Sdan    foreach c $counts($key) {
9699ebad90Sdan      if {$c>0} { incr nHit($iPhrase,$iCol) 1 }
9799ebad90Sdan      incr nDoc($iPhrase,$iCol) $c
9899ebad90Sdan      incr iPhrase
9999ebad90Sdan    }
10099ebad90Sdan  }
10199ebad90Sdan
10299ebad90Sdan  # Set up the aMatchinfo array. For each document, set aMatchinfo($docid) to
10399ebad90Sdan  # contain the output of matchinfo('x') for the document.
10499ebad90Sdan  #
10599ebad90Sdan  foreach docid [array names hits] {
10699ebad90Sdan    set mi [list]
10799ebad90Sdan    for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
10899ebad90Sdan      for {set iCol 0} {$iCol<$nCol} {incr iCol} {
10999ebad90Sdan        lappend mi [lindex $counts($docid,$iCol) $iPhrase]
11099ebad90Sdan        lappend mi $nDoc($iPhrase,$iCol)
11199ebad90Sdan        lappend mi $nHit($iPhrase,$iCol)
11299ebad90Sdan      }
11399ebad90Sdan    }
11499ebad90Sdan    set aMatchinfo($docid) $mi
11599ebad90Sdan  }
11699ebad90Sdan
117*3fd65192Sdan  # Set up the nullentry output.
118*3fd65192Sdan  #
119*3fd65192Sdan  set nullentry [list]
120*3fd65192Sdan  for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
121*3fd65192Sdan    for {set iCol 0} {$iCol<$nCol} {incr iCol} {
122*3fd65192Sdan      lappend nullentry 0 $nDoc($iPhrase,$iCol) $nHit($iPhrase,$iCol)
123*3fd65192Sdan    }
124*3fd65192Sdan  }
125*3fd65192Sdan}
126*3fd65192Sdan
127*3fd65192Sdan
128*3fd65192Sdanproc matching_brackets {expr} {
129*3fd65192Sdan  if {[string range $expr 0 0]!="(" || [string range $expr end end] !=")"} {
130*3fd65192Sdan    return 0
131*3fd65192Sdan  }
132*3fd65192Sdan
133*3fd65192Sdan  set iBracket 1
134*3fd65192Sdan  set nExpr [string length $expr]
135*3fd65192Sdan  for {set i 1} {$iBracket && $i < $nExpr} {incr i} {
136*3fd65192Sdan    set c [string range $expr $i $i]
137*3fd65192Sdan    if {$c == "("} {incr iBracket}
138*3fd65192Sdan    if {$c == ")"} {incr iBracket -1}
139*3fd65192Sdan  }
140*3fd65192Sdan
141*3fd65192Sdan  return [expr ($iBracket==0 && $i==$nExpr)]
142*3fd65192Sdan}
143*3fd65192Sdan
144*3fd65192Sdanproc get_near_results {tbl expr arrayvar {nullvar ""}} {
145*3fd65192Sdan  upvar $arrayvar aMatchinfo
146*3fd65192Sdan  if {$nullvar != ""} { upvar $nullvar nullentry }
147*3fd65192Sdan
148*3fd65192Sdan  set expr [string trim $expr]
149*3fd65192Sdan  while { [matching_brackets $expr] } {
150*3fd65192Sdan    set expr [string trim [string range $expr 1 end-1]]
151*3fd65192Sdan  }
152*3fd65192Sdan
153*3fd65192Sdan  set prec(NOT) 1
154*3fd65192Sdan  set prec(AND) 2
155*3fd65192Sdan  set prec(OR)  3
156*3fd65192Sdan
157*3fd65192Sdan  set currentprec 0
158*3fd65192Sdan  set iBracket 0
159*3fd65192Sdan  set expr_length [llength $expr]
160*3fd65192Sdan  for {set i 0} {$i < $expr_length} {incr i} {
161*3fd65192Sdan    set op [lindex $expr $i]
162*3fd65192Sdan    if {$iBracket==0 && [info exists prec($op)] && $prec($op)>=$currentprec } {
163*3fd65192Sdan      set opidx $i
164*3fd65192Sdan      set currentprec $prec($op)
165*3fd65192Sdan    } else {
166*3fd65192Sdan      for {set j 0} {$j < [string length $op]} {incr j} {
167*3fd65192Sdan        set c [string range $op $j $j]
168*3fd65192Sdan        if {$c == "("} { incr iBracket +1 }
169*3fd65192Sdan        if {$c == ")"} { incr iBracket -1 }
170*3fd65192Sdan      }
171*3fd65192Sdan    }
172*3fd65192Sdan  }
173*3fd65192Sdan  if {$iBracket!=0} { error "mismatched brackets in: $expr" }
174*3fd65192Sdan
175*3fd65192Sdan  if {[info exists opidx]==0} {
176*3fd65192Sdan    get_single_near_results $tbl $expr aMatchinfo nullentry
177*3fd65192Sdan  } else {
178*3fd65192Sdan    set eLeft  [lrange $expr 0 [expr $opidx-1]]
179*3fd65192Sdan    set eRight [lrange $expr [expr $opidx+1] end]
180*3fd65192Sdan
181*3fd65192Sdan    get_near_results $tbl $eLeft  aLeft  nullleft
182*3fd65192Sdan    get_near_results $tbl $eRight aRight nullright
183*3fd65192Sdan
184*3fd65192Sdan    switch -- [lindex $expr $opidx] {
185*3fd65192Sdan      "NOT" {
186*3fd65192Sdan        foreach hit [array names aLeft] {
187*3fd65192Sdan          if {0==[info exists aRight($hit)]} {
188*3fd65192Sdan            set aMatchinfo($hit) $aLeft($hit)
189*3fd65192Sdan          }
190*3fd65192Sdan        }
191*3fd65192Sdan        set nullentry $nullleft
192*3fd65192Sdan      }
193*3fd65192Sdan
194*3fd65192Sdan      "AND" {
195*3fd65192Sdan        foreach hit [array names aLeft] {
196*3fd65192Sdan          if {[info exists aRight($hit)]} {
197*3fd65192Sdan            set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
198*3fd65192Sdan          }
199*3fd65192Sdan        }
200*3fd65192Sdan        set nullentry [concat $nullleft $nullright]
201*3fd65192Sdan      }
202*3fd65192Sdan
203*3fd65192Sdan      "OR" {
204*3fd65192Sdan        foreach hit [array names aLeft] {
205*3fd65192Sdan          if {[info exists aRight($hit)]} {
206*3fd65192Sdan            set aMatchinfo($hit) [concat $aLeft($hit) $aRight($hit)]
207*3fd65192Sdan            unset aRight($hit)
208*3fd65192Sdan          } else {
209*3fd65192Sdan            set aMatchinfo($hit) [concat $aLeft($hit) $nullright]
210*3fd65192Sdan          }
211*3fd65192Sdan        }
212*3fd65192Sdan        foreach hit [array names aRight] {
213*3fd65192Sdan          set aMatchinfo($hit) [concat $nullleft $aRight($hit)]
214*3fd65192Sdan        }
215*3fd65192Sdan
216*3fd65192Sdan        set nullentry [concat $nullleft $nullright]
217*3fd65192Sdan      }
218*3fd65192Sdan    }
219*3fd65192Sdan  }
220*3fd65192Sdan}
221*3fd65192Sdan
222*3fd65192Sdanproc do_near_test {tn tbl expr} {
223*3fd65192Sdan
224*3fd65192Sdan  get_near_results $tbl $expr aMatchinfo
225*3fd65192Sdan  set match $expr
226*3fd65192Sdan
22799ebad90Sdan  set matchinfo_asc [list]
22899ebad90Sdan  foreach docid [lsort -integer -incr [array names aMatchinfo]] {
22999ebad90Sdan    lappend matchinfo_asc $docid $aMatchinfo($docid)
23099ebad90Sdan  }
23199ebad90Sdan  set matchinfo_desc [list]
23299ebad90Sdan  foreach docid [lsort -integer -decr [array names aMatchinfo]] {
23399ebad90Sdan    lappend matchinfo_desc $docid $aMatchinfo($docid)
23499ebad90Sdan  }
23599ebad90Sdan
236*3fd65192Sdan  set title "(\"$match\" -> [llength [array names aMatchinfo]] rows)"
23799ebad90Sdan
23899ebad90Sdan  do_execsql_test $tn$title.1 "
23999ebad90Sdan    SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid ASC
240*3fd65192Sdan  " [lsort -integer -incr [array names aMatchinfo]]
24199ebad90Sdan
24299ebad90Sdan  do_execsql_test $tn$title.2 "
24399ebad90Sdan    SELECT docid FROM $tbl WHERE $tbl MATCH '$match' ORDER BY docid DESC
244*3fd65192Sdan  " [lsort -integer -decr [array names aMatchinfo]]
24599ebad90Sdan
24699ebad90Sdan  do_execsql_test $tn$title.3 "
24799ebad90Sdan    SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl
24899ebad90Sdan    WHERE $tbl MATCH '$match' ORDER BY docid DESC
24999ebad90Sdan  " $matchinfo_desc
25099ebad90Sdan
25199ebad90Sdan  do_execsql_test $tn$title.4 "
25299ebad90Sdan    SELECT docid, mit(matchinfo($tbl, 'x')) FROM $tbl
25399ebad90Sdan    WHERE $tbl MATCH '$match' ORDER BY docid ASC
25499ebad90Sdan  " $matchinfo_asc
25599ebad90Sdan}
25699ebad90Sdan
257*3fd65192Sdan
258*3fd65192Sdan# End of test procs. Actual tests are below this line.
259*3fd65192Sdan#--------------------------------------------------------------------------
260*3fd65192Sdan
261*3fd65192Sdan#--------------------------------------------------------------------------
262*3fd65192Sdan# The following test cases - fts3auto-1.* - focus on testing the Tcl
263*3fd65192Sdan# command [fts3_near_match], which is used by other tests in this file.
264*3fd65192Sdan#
265*3fd65192Sdanproc test_fts3_near_match {tn doc expr res} {
266*3fd65192Sdan  fts3_near_match $doc $expr -phrasecountvar p
267*3fd65192Sdan  uplevel do_test [list $tn] [list [list set {} $p]] [list $res]
268*3fd65192Sdan}
269*3fd65192Sdan
270*3fd65192Sdantest_fts3_near_match 1.1.1 {a b c a b} a                   {2}
271*3fd65192Sdantest_fts3_near_match 1.1.2 {a b c a b} {a 5 b 6 c}         {2 2 1}
272*3fd65192Sdantest_fts3_near_match 1.1.3 {a b c a b} {"a b"}             {2}
273*3fd65192Sdantest_fts3_near_match 1.1.4 {a b c a b} {"b c"}             {1}
274*3fd65192Sdantest_fts3_near_match 1.1.5 {a b c a b} {"c c"}             {0}
275*3fd65192Sdan
276*3fd65192Sdantest_fts3_near_match 1.2.1 "a b c d e f g" {b 2 f}         {0 0}
277*3fd65192Sdantest_fts3_near_match 1.2.2 "a b c d e f g" {b 3 f}         {1 1}
278*3fd65192Sdantest_fts3_near_match 1.2.3 "a b c d e f g" {f 2 b}         {0 0}
279*3fd65192Sdantest_fts3_near_match 1.2.4 "a b c d e f g" {f 3 b}         {1 1}
280*3fd65192Sdantest_fts3_near_match 1.2.5 "a b c d e f g" {"a b" 2 "f g"} {0 0}
281*3fd65192Sdantest_fts3_near_match 1.2.6 "a b c d e f g" {"a b" 3 "f g"} {1 1}
282*3fd65192Sdan
283*3fd65192Sdanset A "a b c d e f g h i j k l m n o p q r s t u v w x y z"
284*3fd65192Sdantest_fts3_near_match 1.3.1 $A {"c d" 5 "i j" 1 "e f"}      {0 0 0}
285*3fd65192Sdantest_fts3_near_match 1.3.2 $A {"c d" 5 "i j" 2 "e f"}      {1 1 1}
286*3fd65192Sdan
287*3fd65192Sdan#--------------------------------------------------------------------------
288*3fd65192Sdan# Test cases fts3auto-2.* run some simple tests using the
289*3fd65192Sdan# [do_near_test] proc.
290*3fd65192Sdan#
291*3fd65192Sdanforeach {tn create} {
292*3fd65192Sdan  1    "CREATE VIRTUAL TABLE t1 USING fts4(a, b)"
293*3fd65192Sdan  2    "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=DESC)"
294*3fd65192Sdan  3    "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=ASC)"
295*3fd65192Sdan  4    "CREATE VIRTUAL TABLE t1 USING fts4(a, b, prefix=1)"
296*3fd65192Sdan  5    "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=DESC, prefix=1)"
297*3fd65192Sdan  6    "CREATE VIRTUAL TABLE t1 USING fts4(a, b, order=ASC, prefix=1)"
298*3fd65192Sdan} {
299*3fd65192Sdan  do_test 2.$tn.1 {
300*3fd65192Sdan    catchsql { DROP TABLE t1 }
301*3fd65192Sdan    execsql  $create
30299ebad90Sdan    for {set i 0} {$i<32} {incr i} {
30399ebad90Sdan      set doc [list]
30499ebad90Sdan      if {$i&0x01} {lappend doc one}
30599ebad90Sdan      if {$i&0x02} {lappend doc two}
30699ebad90Sdan      if {$i&0x04} {lappend doc three}
30799ebad90Sdan      if {$i&0x08} {lappend doc four}
30899ebad90Sdan      if {$i&0x10} {lappend doc five}
30999ebad90Sdan      execsql { INSERT INTO t1 VALUES($doc, null) }
31099ebad90Sdan    }
31199ebad90Sdan  } {}
312*3fd65192Sdan  foreach {tn2 expr} {
31399ebad90Sdan    1     {one}
31499ebad90Sdan    2     {one NEAR/1 five}
31599ebad90Sdan    3     {t*}
31699ebad90Sdan    4     {t* NEAR/0 five}
31799ebad90Sdan    5     {o* NEAR/1 f*}
31899ebad90Sdan    6     {one NEAR five NEAR two NEAR four NEAR three}
319*3fd65192Sdan    7     {one NEAR xyz}
320*3fd65192Sdan    8     {one OR two}
321*3fd65192Sdan    9     {one AND two}
322*3fd65192Sdan    10    {one NOT two}
323*3fd65192Sdan    11    {one AND two OR three}
324*3fd65192Sdan    12    {three OR one AND two}
325*3fd65192Sdan    13    {(three OR one) AND two}
326*3fd65192Sdan    14    {(three OR one) AND two NOT (five NOT four)}
327*3fd65192Sdan    15    {"one two"}
328*3fd65192Sdan    16    {"one two" NOT "three four"}
32999ebad90Sdan  } {
330*3fd65192Sdan    do_near_test 2.$tn.2.$tn2 t1 $expr
331*3fd65192Sdan  }
33299ebad90Sdan}
33399ebad90Sdan
334*3fd65192Sdanset sqlite_fts3_enable_parentheses $sfep
33599ebad90Sdanfinish_test
33699ebad90Sdan
337