#include "utils/jsonfuncs.h"
+/*
+ * Opaque data structure, which is passed by parse_tsquery() to pushval_morph().
+ */
typedef struct MorphOpaque
{
Oid cfg_id;
- int qoperator; /* query operator */
+
+ /*
+ * Single tsquery morph could be parsed into multiple words. When these
+ * words reside in adjacent positions, they are connected using this
+ * operator. Usually, that is OP_PHRASE, which requires word positions of
+ * a complex morph to exactly match the tsvector.
+ */
+ int qoperator;
} MorphOpaque;
typedef struct TSVectorBuildState
MorphOpaque data;
data.cfg_id = PG_GETARG_OID(0);
- data.qoperator = OP_AND;
+
+ /*
+ * Passing OP_PHRASE as a qoperator makes tsquery require matching of word
+ * positions of a complex morph exactly match the tsvector. Also, when
+ * the complex morphs are connected with OP_PHRASE operator, we connect
+ * all their words into the OP_PHRASE sequence.
+ */
+ data.qoperator = OP_PHRASE;
query = parse_tsquery(text_to_cstring(in),
pushval_morph,
MorphOpaque data;
data.cfg_id = PG_GETARG_OID(0);
+
+ /*
+ * parse_tsquery() with P_TSQ_PLAIN flag takes the whole input text as a
+ * single morph. Passing OP_PHRASE as a qoperator makes tsquery require
+ * matching of all words independently on their positions.
+ */
data.qoperator = OP_AND;
query = parse_tsquery(text_to_cstring(in),
MorphOpaque data;
data.cfg_id = PG_GETARG_OID(0);
+
+ /*
+ * parse_tsquery() with P_TSQ_PLAIN flag takes the whole input text as a
+ * single morph. Passing OP_PHRASE as a qoperator makes tsquery require
+ * matching of word positions.
+ */
data.qoperator = OP_PHRASE;
query = parse_tsquery(text_to_cstring(in),
data.cfg_id = PG_GETARG_OID(0);
- data.qoperator = OP_AND;
+ /*
+ * Passing OP_PHRASE as a qoperator makes tsquery require matching of word
+ * positions of a complex morph exactly match the tsvector. Also, when
+ * the complex morphs are given in quotes, we connect all their words into
+ * the OP_PHRASE sequence.
+ */
+ data.qoperator = OP_PHRASE;
query = parse_tsquery(text_to_cstring(in),
pushval_morph,
UPDATE test_tsquery SET keyword = to_tsquery('english', txtkeyword);
ALTER TABLE test_tsquery ADD COLUMN sample tsquery;
UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text);
-SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new <-> york';
count
-------
2
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new <-> york';
count
-------
3
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new <-> york';
count
-------
1
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new <-> york';
count
-------
4
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new <-> york';
count
-------
3
CREATE UNIQUE INDEX bt_tsq ON test_tsquery (keyword);
SET enable_seqscan=OFF;
-SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new <-> york';
count
-------
2
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new <-> york';
count
-------
3
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new <-> york';
count
-------
1
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new <-> york';
count
-------
4
(1 row)
-SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new <-> york';
count
-------
3
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text );
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite('bar & qq & foo & (new <-> york)', 'SELECT keyword, sample FROM test_tsquery'::text );
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery');
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery');
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite( 'bar & qq & foo & (new <-> york)', 'SELECT keyword, sample FROM test_tsquery');
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
SELECT ts_rewrite('1 & (2 <-> 3)', 'SELECT keyword, sample FROM test_tsquery'::text );
(1 row)
SELECT keyword FROM test_tsquery WHERE keyword @> 'new';
- keyword
-----------------
- 'new' & 'york'
+ keyword
+------------------
+ 'new' <-> 'york'
(1 row)
SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow';
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops);
SET enable_seqscan=OFF;
SELECT keyword FROM test_tsquery WHERE keyword @> 'new';
- keyword
-----------------
- 'new' & 'york'
+ keyword
+------------------
+ 'new' <-> 'york'
(1 row)
SELECT keyword FROM test_tsquery WHERE keyword @> 'moscow';
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
'hotel' & ( 'moskva' | 'moscow' )
(1 row)
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
- ts_rewrite
----------------------------------------------------------------------------------
- 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' )
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
+ ts_rewrite
+-------------------------------------------------------------------------------------
+ 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' <-> 'appl' | 'new' <-> 'york' )
(1 row)
SELECT ts_rewrite(tsquery_phrase('foo', 'foo'), 'foo', 'bar | baz');
select websearch_to_tsquery('simple', 'fat*rat');
websearch_to_tsquery
----------------------
- 'fat' & 'rat'
+ 'fat' <-> 'rat'
(1 row)
select websearch_to_tsquery('simple', 'fat-rat');
- websearch_to_tsquery
----------------------------
- 'fat-rat' & 'fat' & 'rat'
+ websearch_to_tsquery
+-------------------------------
+ 'fat-rat' <-> 'fat' <-> 'rat'
(1 row)
select websearch_to_tsquery('simple', 'fat_rat');
websearch_to_tsquery
----------------------
- 'fat' & 'rat'
+ 'fat' <-> 'rat'
(1 row)
-- weights are completely ignored
(1 row)
select websearch_to_tsquery('simple', 'abc or-abc');
- websearch_to_tsquery
----------------------------------
- 'abc' & 'or-abc' & 'or' & 'abc'
+ websearch_to_tsquery
+-------------------------------------
+ 'abc' & 'or-abc' <-> 'or' <-> 'abc'
(1 row)
select websearch_to_tsquery('simple', 'abc OR_abc');
- websearch_to_tsquery
-----------------------
- 'abc' & 'or' & 'abc'
+ websearch_to_tsquery
+------------------------
+ 'abc' & 'or' <-> 'abc'
(1 row)
-- test quotes
select websearch_to_tsquery('english', '"pg_class pg');
- websearch_to_tsquery
------------------------
- 'pg' & 'class' & 'pg'
+ websearch_to_tsquery
+-------------------------
+ 'pg' <-> 'class' & 'pg'
(1 row)
select websearch_to_tsquery('english', 'pg_class pg"');
- websearch_to_tsquery
------------------------
- 'pg' & 'class' & 'pg'
+ websearch_to_tsquery
+-------------------------
+ 'pg' <-> 'class' & 'pg'
(1 row)
select websearch_to_tsquery('english', '"pg_class pg"');
- websearch_to_tsquery
------------------------------
- ( 'pg' & 'class' ) <-> 'pg'
+ websearch_to_tsquery
+---------------------------
+ 'pg' <-> 'class' <-> 'pg'
(1 row)
select websearch_to_tsquery('english', 'abc "pg_class pg"');
- websearch_to_tsquery
--------------------------------------
- 'abc' & ( 'pg' & 'class' ) <-> 'pg'
+ websearch_to_tsquery
+-----------------------------------
+ 'abc' & 'pg' <-> 'class' <-> 'pg'
(1 row)
select websearch_to_tsquery('english', '"pg_class pg" def');
- websearch_to_tsquery
--------------------------------------
- ( 'pg' & 'class' ) <-> 'pg' & 'def'
+ websearch_to_tsquery
+-----------------------------------
+ 'pg' <-> 'class' <-> 'pg' & 'def'
(1 row)
select websearch_to_tsquery('english', 'abc "pg pg_class pg" def');
- websearch_to_tsquery
-------------------------------------------------------
- 'abc' & 'pg' <-> ( 'pg' & 'class' ) <-> 'pg' & 'def'
+ websearch_to_tsquery
+--------------------------------------------------------
+ 'abc' & 'pg' <-> ( 'pg' <-> 'class' ) <-> 'pg' & 'def'
(1 row)
select websearch_to_tsquery('english', ' or "pg pg_class pg" or ');
- websearch_to_tsquery
---------------------------------------
- 'pg' <-> ( 'pg' & 'class' ) <-> 'pg'
+ websearch_to_tsquery
+----------------------------------------
+ 'pg' <-> ( 'pg' <-> 'class' ) <-> 'pg'
(1 row)
select websearch_to_tsquery('english', '""pg pg_class pg""');
- websearch_to_tsquery
-------------------------------
- 'pg' & 'pg' & 'class' & 'pg'
+ websearch_to_tsquery
+--------------------------------
+ 'pg' & 'pg' <-> 'class' & 'pg'
(1 row)
select websearch_to_tsquery('english', 'abc """"" def');
select websearch_to_tsquery('''abc''''def''');
websearch_to_tsquery
----------------------
- 'abc' & 'def'
+ 'abc' <-> 'def'
(1 row)
select websearch_to_tsquery('\abc');
CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT);
\set ECHO none
\copy test_tsquery from stdin
-'New York' new & york | big & apple | nyc
+'New York' new <-> york | big <-> apple | nyc
Moscow moskva | moscow
'Sanct Peter' Peterburg | peter | 'Sanct Peterburg'
-'foo bar qq' foo & (bar | qq) & city
+foo & bar & qq foo & (bar | qq) & city
1 & (2 <-> 3) 2 <-> 4
5 <-> 6 5 <-> 7
\.
UPDATE test_tsquery SET sample = to_tsquery('english', txtsample::text);
-SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new <-> york';
CREATE UNIQUE INDEX bt_tsq ON test_tsquery (keyword);
SET enable_seqscan=OFF;
-SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new & york';
-SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword <= 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword = 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword >= 'new <-> york';
+SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new <-> york';
RESET enable_seqscan;
SELECT ts_rewrite('moscow', 'SELECT keyword, sample FROM test_tsquery'::text );
SELECT ts_rewrite('moscow & hotel', 'SELECT keyword, sample FROM test_tsquery'::text );
-SELECT ts_rewrite('bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery'::text );
+SELECT ts_rewrite('bar & qq & foo & (new <-> york)', 'SELECT keyword, sample FROM test_tsquery'::text );
SELECT ts_rewrite( 'moscow', 'SELECT keyword, sample FROM test_tsquery');
SELECT ts_rewrite( 'moscow & hotel', 'SELECT keyword, sample FROM test_tsquery');
-SELECT ts_rewrite( 'bar & new & qq & foo & york', 'SELECT keyword, sample FROM test_tsquery');
+SELECT ts_rewrite( 'bar & qq & foo & (new <-> york)', 'SELECT keyword, sample FROM test_tsquery');
SELECT ts_rewrite('1 & (2 <-> 3)', 'SELECT keyword, sample FROM test_tsquery'::text );
SELECT ts_rewrite('1 & (2 <2> 3)', 'SELECT keyword, sample FROM test_tsquery'::text );
SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow';
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query;
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query;
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
CREATE INDEX qq ON test_tsquery USING gist (keyword tsquery_ops);
SET enable_seqscan=OFF;
SELECT keyword FROM test_tsquery WHERE keyword <@ 'moscow';
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query;
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow') AS query;
SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'moscow & hotel') AS query;
-SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & new & qq & foo & york') AS query;
+SELECT ts_rewrite( query, 'SELECT keyword, sample FROM test_tsquery' ) FROM to_tsquery('english', 'bar & qq & foo & (new <-> york)') AS query;
SELECT ts_rewrite(tsquery_phrase('foo', 'foo'), 'foo', 'bar | baz');
SELECT to_tsvector('foo bar') @@