There is a more general version of the FOLLOWED BY operator having the
form <literal><<replaceable>N</>></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><1></literal> is
the same as <literal><-></>, while <literal><2></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:
<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><<replaceable>N</>></literal>
<type>tsquery</> operator. For example:
ExecPhraseData Ldata = {0, false, NULL},
Rdata = {0, false, NULL};
WordEntryPos *Lpos,
+ *LposStart,
*Rpos,
*pos_iter = NULL;
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++;
t
(1 row)
-SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "true";
- true
-------
- t
+SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <2> 2' AS "false";
+ false
+-------
+ f
(1 row)
SELECT to_tsvector('simple', '1 2 3 1') @@ '1 <-> 3' AS "false";
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
-------
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');
t
(1 row)
-SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "true";
- true
-------
- t
+SELECT 'a:1 b:2'::tsvector @@ 'a <2> b'::tsquery AS "false";
+ false
+-------
+ f
(1 row)
SELECT 'a:1 b:3'::tsvector @@ 'a <-> b'::tsquery AS "false";
t
(1 row)
-SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "true";
- true
-------
- t
+SELECT 'a:1 b:3'::tsvector @@ 'a <3> b'::tsquery AS "false";
+ false
+-------
+ f
(1 row)
-- tsvector editing operations
--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";
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