Sqlite FTS, Using OR between match operators - sqlite

When I execute the following query in a sqlite engine (android or sqlitebrowser) it throws an exception that says unable to use function MATCH in the requested context.
select
a.Body1,
b.Body2
from
tbl1_fts as a,
tbl2_fts as b
where
a.ID = b.ParentID and
(
a.Body1 match('value') or
b.Body2 match('value')
)
-Both tables have fts.
-Using And operator between two matches (instead of OR) runs normally.
How can I fix this or change the query to find rows with above condition?

you can not use OR Operation, just change your Match Keyword.
like
SELECT * FROM docs WHERE docs MATCH 'sqlite OR database';
OR maybe you can use union
SELECT docid FROM docs WHERE docs MATCH 'sqlite AND database'
UNION
SELECT docid FROM docs WHERE docs MATCH 'library';

MATCH as a function would have two parameters:
... WHERE match('value', SomeColumn) ...
However, the usual method of using MATCH is as an operator:
... WHERE SomeColumn MATCH 'value' ...

MATCH has to be used without parentheses.
The AND and OR operators must be in capital letters when used with FTS.

Related

Unary NOT in SQLite FTS5 MATCH query

The SQLite FTS5 docs say that search queries such as SELECT ... WHERE MATCH '<query1> NOT <query2>' are supported, but it looks like there's no support for the unary NOT operator.
For example, if I want to search for everything that doesn't match <query>, I cannot use MATCH 'NOT <query>'. I would have to use NOT MATCH '<query>', which is a completely different thing (the FTS5 module never gets to see the NOT operator, as it is outside the quotation marks). Only the text inside the quotation marks is the search query.
I need to find a way to use an unary NOT operator inside the search query. I can't use it outside, because I only get to control the search query text, and not the rest of the SQL statement.
A possible approach I've thought of would be to find a search query that matches anything, and do MATCH '<match_anything> NOT <query>'. However, I've found no way to match everything in a search query.
Can you think of a way to have the behaviour of the unary NOT operator inside the search query?
Try this ..
SELECT * FROM docs
WHERE ROWID NOT IN (
SELECT ROWID FROM docs WHERE content MATCH '<query>'
)

SQLite MATCH query

I have created a virtual table and am executing the following query that returns nothing:
SELECT * FROM table_search WHERE name MATCH 'Test'
If I change the MATCH to '=' or 'LIKE' then the query returns a row of data.
As the documentation explains:
The MATCH operator is a special syntax for the match()
application-defined function. The default match() function
implementation raises an exception and is not really useful for
anything. But extensions can override the match() function with more
helpful logic.
If you haven't defined such a function, then it will select no rows. If you have defined such a function, then explain that in your question. Otherwise, stick with like, =, or regexp.
I am wondering what changed since Gordon Linoff's answer, because he is obviously quite the SQL expert and I'm not. Just talking about my own experience:
Don't specify the column but the table name for FTS queries:
SELECT * FROM table_search WHERE table_search MATCH 'Test'
I did the same mistake and even tried the implementation of a custom MATCH function from this repository after reading the answer from Gordon Linoff and the doc.
The MATCH operator is a special syntax for the match() application-defined function. The default match() function implementation raises an exception and is not really useful for anything. But extensions can override the match() function with more helpful logic.
Contrary, I found out the MATCH function works out of box (using sqlite 3.39) if I used the table name, and an asterix 'Test*' but that was just because of my specific dataset and a wrong assumption.
The custom function is not being called by the MATCH, but it is called if I applied it to a column like
SELECT match(name) FROM table_search WHERE table_search MATCH 'Test'
which is a very different intention.

How can you exclude a search term in an SQLite fulltext search?

I am using DB Browser for SQLite. The documentation for SQLite's fts3 says "FTS is primarily designed to support Boolean full-text queries". I built a virtual table using fts4 and successfully executed a few WHERE ... MATCH queries. But the following attempts give errors:
SELECT id FROM histsearch WHERE id MATCH ("-1456" IN BOOLEAN MODE);
SELECT id FROM histsearch WHERE NOT EXIST id MATCH ("1457");
Is the problem in DB Browser or in SQLite? How else can I write this query so it will work?
SQLite's full text service (fts3) basically offers Boolean Mode by default, no search modifier needed. DB Browser uses fts's standard query syntax, so NOT is not supported. To exclude a term, do something like
SELECT * FROM indexed WHERE indexed MATCH 'sqlite -database';
Edit: however, you cannot only exclude search terms in fulltext search:
An FTS query may not consist entirely of terms or term-prefix queries with unary "-" operators attached to them.
You'll have to use NOT LIKE for that.

SQLite FTS example doesn't work

I've downloaded the latest SQLite 3.7.15.2 shell (Win32) and tried to execute one of the FTS examples exactly as it is written at http://sqlite.org/fts3.html#section_3
-- Virtual table declaration
CREATE VIRTUAL TABLE docs USING fts3();
-- Virtual table data
INSERT INTO docs(docid, content) VALUES(1, 'a database is a software system');
INSERT INTO docs(docid, content) VALUES(2, 'sqlite is a software system');
INSERT INTO docs(docid, content) VALUES(3, 'sqlite is a database');
-- Return the set of documents that contain the term "sqlite", and the
-- term "database". This query will return the document with docid 3 only.
SELECT * FROM docs WHERE docs MATCH 'sqlite AND database';
but in spite of last comment SELECT resulted in empty set. Is it a bug in SQLite or just outdated documentation? (and what is the correct syntax for that?).
What is most important for me is that query
SELECT * FROM docs WHERE docs MATCH '(database OR sqlite) NEAR/5 system';
doesn't work either and that type of queries I need in my app. Is there any other way to write it so it would work?
The example from the documentation uses the enhanced query syntax.
Check that PRAGMA compile_options; includes ENABLE_FTS3_PARENTHESIS.
That your NEAR query does not work is not a problem with compilation options:
> SELECT * FROM docs WHERE docs MATCH '(database OR sqlite) NEAR/5 system';
Error: malformed MATCH expression: [(database OR sqlite) NEAR/5 system]
The problem is that, according to the documentation, NEAR does work only with basic search expressions:
A NEAR query is specified by putting the keyword "NEAR" between two phrase, term or prefix queries.
So you have to rewrite your search expression accordingly:
> SELECT * FROM docs WHERE docs MATCH '(database NEAR/5 system) OR (sqlite NEAR/5 system)';
a database is a software system
sqlite is a software system
I don't know if it is the docs or if it is a bug with SQLite, but here are some alternatives:
For AND queries
Doesn't work:
select * from docs where docs match 'sqlite AND database';
Works (using implied AND):
select * from docs where docs match 'sqlite database';
OR seems to work:
select * from docs where docs match 'sqlite OR database';
For OR + NEAR queries:
Doesn't Work:
SELECT * FROM docs WHERE docs MATCH '(database OR sqlite) NEAR/5 system';
Works:
SELECT * FROM docs WHERE docs MATCH 'database NEAR/5 system'
UNION
SELECT * FROM docs WHERE docs MATCH 'sqlite NEAR/5 system'
EDIT: For the form mentioned in the comments (word11 OR word12 OR word13) NEAR/2 (word21 OR word22 OR word23) NEAR/2 (word31 OR word32 OR word33. This is the best I could do is to put all combinations together with a UNION:
SELECT * FROM docs WHERE docs MATCH 'word11 NEAR/2 word21 NEAR/2 word31'
UNION
SELECT * FROM docs WHERE docs MATCH 'word11 NEAR/2 word22 NEAR/2 word32'
UNION
SELECT * FROM docs WHERE docs MATCH 'word11 NEAR/2 word23 NEAR/2 word33'
UNION
SELECT * FROM docs WHERE docs MATCH 'word12 NEAR/2 word21 NEAR/2 word31'
...
The above of course creates large amounts of SQL. If your words are similar in that only the endings differ, you could use wildcards:
SELECT * FROM docs WHERE docs MATCH 'word1* NEAR/2 word2* NEAR/2 word3*';
According to the documentation (https://www.sqlite.org/fts3.html), parentheses are not supported by default.
Look at part 2. Compiling and Enabling FTS3 and FTS4.

A limitation of Sqlite3's full text search doesn't allow ORs with MATCHes. Workaround?

Sqlite3's full text search facility - FTS3 - allows to use MATCH operator for fast full-text search:
SELECT ItemId FROM docs WHERE docs.text MATCH 'linux'
However, it does not support OR operator anywhere in an SQL query where there's a MATCH (source: 1, 2):
SELECT ItemId FROM docs WHERE docs.text MATCH 'linux' OR column=value
error: unable to use function MATCH in the requested context
(Not to be confused with OR operator in the FTS3 query itself, i.e. SELECT ItemId FROM docs WHERE docs.text MATCH 'linux OR unix'. This works fine.)
Is there a way to rewrite the query so that it works (even if it's somewhat slower)?
Rewriting the query using temporary views will work as expected:
CREATE TEMP VIEW view1 AS SELECT ItemId FROM docs WHERE docs.text MATCH 'linux'
SELECT * FROM docs WHERE ItemId IN view1 OR column=value
DROP VIEW view1
The speed will be comparable to that of a "direct" query (without the temporary view) if the temporary view is not "sweeping", i.e. it does not generate a lot of rows.

Resources