]> granicus.if.org Git - postgresql/commitdiff
Make exact distance match for FTS phrase operator
authorTeodor Sigaev <teodor@sigaev.ru>
Mon, 27 Jun 2016 17:41:00 +0000 (20:41 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Mon, 27 Jun 2016 17:41:00 +0000 (20:41 +0300)
Phrase operator now requires exact distance betweens lexems instead of
less-or-equal.

Per discussion c19fcfec308e6ccd952cdde9e648b505@mail.gmail.com

doc/src/sgml/textsearch.sgml
src/backend/utils/adt/tsvector_op.c
src/test/regress/expected/tstypes.out
src/test/regress/sql/tstypes.sql

index 9028bedd1bbf1301f8c928d89016b9f91eb6afc8..72bef9f49e715d8af6a567958b5e0d354e1705a0 100644 (file)
@@ -346,10 +346,10 @@ SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal &lt;-&gt; error');
 
     There is a more general version of the FOLLOWED BY operator having the
     form <literal>&lt;<replaceable>N</>&gt;</literal>,
-    where <replaceable>N</> is an integer standing for the greatest distance
+    where <replaceable>N</> is an integer standing for the exact distance
     allowed between the matching lexemes.  <literal>&lt;1&gt;</literal> is
     the same as <literal>&lt;-&gt;</>, while <literal>&lt;2&gt;</literal>
-    allows one other lexeme to optionally appear between the matches, and so
+    allows one other lexeme to appear between the matches, and so
     on.  The <literal>phraseto_tsquery</> function makes use of this
     operator to construct a <literal>tsquery</> that can match a multi-word
     phrase when some of the words are stop words.  For example:
@@ -1529,7 +1529,7 @@ SELECT to_tsquery('fat') &lt;-&gt; to_tsquery('cat | rat');
       <para>
        Returns a query that searches for a match to the first given query
        followed by a match to the second given query at a distance of at
-       most <replaceable>distance</replaceable> lexemes, using
+       <replaceable>distance</replaceable> lexemes, using
        the <literal>&lt;<replaceable>N</>&gt;</literal>
        <type>tsquery</> operator.  For example:
 
index 6117ba9b3e4a083527bba6059df71983c04facda..04718829a0b960afaa560969e6ce22da6956c49c 100644 (file)
@@ -1375,6 +1375,7 @@ TS_phrase_execute(QueryItem *curitem,
                ExecPhraseData Ldata = {0, false, NULL},
                                        Rdata = {0, false, NULL};
                WordEntryPos *Lpos,
+                                  *LposStart,
                                   *Rpos,
                                   *pos_iter = NULL;
 
@@ -1416,52 +1417,60 @@ TS_phrase_execute(QueryItem *curitem,
                        pos_iter = data->pos;
                }
 
-               Lpos = Ldata.pos;
-               Rpos = Rdata.pos;
-
                /*
                 * Find matches by distance, WEP_GETPOS() is needed because
                 * ExecPhraseData->data can point to the tsvector's WordEntryPosVector
                 */
 
+               Rpos = Rdata.pos;
+               LposStart = Ldata.pos;
                while (Rpos < Rdata.pos + Rdata.npos)
                {
+                       /*
+                        * We need to check all possible distances, so reset Lpos
+                        * to guranteed not yet satisfied position.
+                        */
+                       Lpos = LposStart;
                        while (Lpos < Ldata.pos + Ldata.npos)
                        {
-                               if (WEP_GETPOS(*Lpos) <= WEP_GETPOS(*Rpos))
+                               if (WEP_GETPOS(*Rpos) - WEP_GETPOS(*Lpos) ==
+                                       curitem->qoperator.distance)
                                {
-                                       /*
-                                        * Lpos is behind the Rpos, so we have to check the
-                                        * distance condition
-                                        */
-                                       if (WEP_GETPOS(*Rpos) - WEP_GETPOS(*Lpos) <= curitem->qoperator.distance)
+                                       /* MATCH! */
+                                       if (data)
                                        {
-                                               /* MATCH! */
-                                               if (data)
-                                               {
-                                                       *pos_iter = WEP_GETPOS(*Rpos);
-                                                       pos_iter++;
-
-                                                       break;          /* We need to build a unique result
-                                                                                * array, so go to the next Rpos */
-                                               }
-                                               else
-                                               {
-                                                       /*
-                                                        * We are in the root of the phrase tree and hence
-                                                        * we don't have to store the resulting positions
-                                                        */
-                                                       return true;
-                                               }
+                                               /* Store position for upper phrase operator */
+                                               *pos_iter = WEP_GETPOS(*Rpos);
+                                               pos_iter++;
+
+                                               /*
+                                                * Set left start position to next, because current one
+                                                * could not satisfy distance for any other right
+                                                * position
+                                                */
+                                               LposStart = Lpos + 1;
+                                               break;
+                                       }
+                                       else
+                                       {
+                                               /*
+                                                * We are in the root of the phrase tree and hence
+                                                * we don't have to store the resulting positions
+                                                */
+                                               return true;
                                        }
+
                                }
-                               else
+                               else if (WEP_GETPOS(*Rpos) <= WEP_GETPOS(*Lpos) ||
+                                                WEP_GETPOS(*Rpos) - WEP_GETPOS(*Lpos) <
+                                                       curitem->qoperator.distance)
                                {
                                        /*
-                                        * Go to the next Rpos, because Lpos is ahead of the
-                                        * current Rpos
+                                        * Go to the next Rpos, because Lpos is ahead or on less
+                                        * distance than required by current operator
                                         */
                                        break;
+
                                }
 
                                Lpos++;
index 64d6de6050a6ef768976681df8d5d630e82aed45..781be70736bf03c0b4fdaa5695b1329787dea4a2 100644 (file)
@@ -665,10 +665,10 @@ SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 2' AS "true";
  t
 (1 row)
 
-SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "true";
tru
-------
- t
+SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "false";
fals
+-------
+ f
 (1 row)
 
 SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 3' AS "false";
@@ -683,6 +683,12 @@ SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 3' AS "true";
  t
 (1 row)
 
+SELECT to_tsvector('simple', '1 2 1 2') @@ '1 <3> 2' AS "true";
+ true 
+------
+ t
+(1 row)
+
 SELECT to_tsvector('simple', '1 2 11 3') @@ '1 <-> 3' AS "false";
  false 
 -------
@@ -897,7 +903,7 @@ SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:*');
 SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:A');
  ts_rank_cd 
 ------------
-  0.0714286
+          0
 (1 row)
 
 SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:B');
@@ -924,10 +930,10 @@ SELECT 'a:1 b:2'::tsvector @@ 'a <1> b'::tsquery AS "true";
  t
 (1 row)
 
-SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "true";
tru
-------
- t
+SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "false";
fals
+-------
+ f
 (1 row)
 
 SELECT 'a:1 b:3'::tsvector @@ 'a <-> b'::tsquery AS "false";
@@ -954,10 +960,10 @@ SELECT 'a:1 b:3'::tsvector @@ 'a <2> b'::tsquery AS "true";
  t
 (1 row)
 
-SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "true";
tru
-------
- t
+SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "false";
fals
+-------
+ f
 (1 row)
 
 -- tsvector editing operations
index 738ec824b99ab4e9d62077aeec5e7f5cb6c97050..abcf1504ce5386539b91295c3d113c166b27be1e 100644 (file)
@@ -130,9 +130,10 @@ SELECT 'supeznova supernova'::tsvector @@ 'super:*'::tsquery AS "true";
 
 --phrase search
 SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 2' AS "true";
-SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "true";
+SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "false";
 SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 3' AS "false";
 SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 3' AS "true";
+SELECT to_tsvector('simple', '1 2 1 2') @@ '1 <3> 2' AS "true";
 
 SELECT to_tsvector('simple', '1 2 11 3') @@ '1 <-> 3' AS "false";
 SELECT to_tsvector('simple', '1 2 11 3') @@ '1:* <-> 3' AS "true";
@@ -180,12 +181,12 @@ SELECT ts_rank_cd(' a:1 sa:2A sb:2D g'::tsvector, 'a <-> s:* <-> sa:B');
 SELECT 'a:1 b:2'::tsvector @@ 'a <-> b'::tsquery AS "true";
 SELECT 'a:1 b:2'::tsvector @@ 'a <0> b'::tsquery AS "false";
 SELECT 'a:1 b:2'::tsvector @@ 'a <1> b'::tsquery AS "true";
-SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "true";
+SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "false";
 SELECT 'a:1 b:3'::tsvector @@ 'a <-> b'::tsquery AS "false";
 SELECT 'a:1 b:3'::tsvector @@ 'a <0> b'::tsquery AS "false";
 SELECT 'a:1 b:3'::tsvector @@ 'a <1> b'::tsquery AS "false";
 SELECT 'a:1 b:3'::tsvector @@ 'a <2> b'::tsquery AS "true";
-SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "true";
+SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "false";
 
 -- tsvector editing operations