Use two columns for FTS4 from sqlite3 - sqlite

I have a table with two columns (ldap, name). I want to be able to full text search any of those columns with the library FTS4. Here I have a couple of statements I'm using to create the virtual table but when I create a statement using Match the result is empty although it should return data.
CREATE VIRTUAL TABLE IF NOT EXISTS sales_rep USING FTS4(ldap,name, content="__sales_rep");
CREATE TRIGGER IF NOT EXISTS __sales_rep___after_insert AFTER INSERT ON __sales_rep BEGIN INSERT INTO sales_rep (ldap, name) VALUES (new.ldap, new.name);END;
I am inserting a row (ldap, name) VALUES ('test', 'Bryan');
But using
SELECT * FROM sales_rep where name MATCH 'Bry';
The result is empty

Inserting data in an external content FTS table requires to provide explicitly a value for the docid, which should be the rowid of the content table.
In your case you need to change the trigger :
CREATE TRIGGER __sales_rep___after_insert
AFTER INSERT ON __sales_rep
BEGIN
INSERT INTO sales_rep (docid, ldap, name)
VALUES (new.rowid, new.ldap, new.name);
END;

Related

SQLite FTS5 Match is returning nothing

I have a SQLite3 table:
CREATE TABLE "test" (
"title" TEXT,
"shortdesc" TEXT,
"longdesc" TEXT,
"id" TEXT,
PRIMARY KEY("id")
);
I insert anything in it:
INSERT INTO test (id, title, shortdesc, longdesc) VALUES ("abc", "hello world", "this is a hello world", "a nice hello world article about hello worlds")
Then I create a FTS5 virtual table:
CREATE VIRTUAL TABLE IF NOT EXISTS test_fts USING fts5 (
id,
title,
shortdesc,
longdesc,
content=test
);
So I check data in virtual table:
Everything seems fine... Now I try to use MATCH to find the article:
SELECT * FROM test_fts WHERE test_fts MATCH 'hello'
...and I get nothing in result. Obviously this database I showed is just an example, same thing happens with actual database. I tried on different computers (and different clients), I also checked if FTS5 is enabled and compiled in it with PRAGMA compile_options and ENABLE_FTS5 is there, meaning it's enabled. Same thing happens with FTS3 and 4.
So what am I missing? SQLite version is 3.36.0.
The FTS5 tables still need to be populated. This includes external content cases. From the documentation:
It is still the responsibility of the user to ensure that the contents of an external content FTS5 table are kept up to date with the content table. One way to do this is with triggers. For example:
-- Create a table. And an external content fts5 table to index it.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b, c);
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a');
-- Triggers to keep the FTS index up to date.
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
END;
CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGIN
INSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES('delete', old.a, old.b, old.c);
INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
So, you could either create the table and triggers before populating the main table, or in your test case, after you create the FTS table you can run a query like this to populate the FTS table for the first time:
INSERT INTO test_fts SELECT * FROM test;
Then your query will work as expected.

SQlite FTS table is not being populated

DROP TABLE IF EXISTS TEXTS;
CREATE TABLE TEXTS (text_id int PRIMARY KEY, text ntext,last_update datetime DEFAULT
CURRENT_TIMESTAMP);
DROP TABLE IF EXISTS FTSTEXTS;
CREATE VIRTUAL TABLE FTSTEXTS USING fts5(text,content="TEXTS",content_rowid="text_id",tokenize=unicode61);
Insert into texts (text_id,text) values (1,'first'),(2,'second'),(3,'third'),(4,'fourth');
Insert into ftstexts (rowid,text) values (1,'first'),(2,'second'),(3,'third'),(4,'fourth');
update texts set text='5555' where text_id=2;
update ftstexts set text='5555' where rowid=2;
select text from texts where text_id=(select rowid from ftstexts where text match('second') limit 1);
result is "5555"
After creating table and updating the value of row 2 to "5555" the query
select rowid from ftstexts where text match('second') limit 1
returns 2:
How can the FTS table be populated to exclude the term "second" from fts search ?
The query was run on SQLITEStudio 3.2.1
External content FTS5 tables don't support updates. You have to delete the old contents of a row and insert the new contents as separate steps.
The documentation includes some sample triggers to automate keeping such a table in sync with the table that holds the actual data.
You might make an update trigger something along the lines of
CREATE TRIGGER texts_au AFTER UPDATE ON texts BEGIN
INSERT INTO ftstexts(ftstexts, rowid, text) VALUES('delete', old.text_id, old.text);
INSERT INTO ftstexts(rowid, text) VALUES (new.text_id, new.text);
END;

SQLite regular table and table for fts

I have table news (id, news_id, news_title) and I creat FTS table:
CREATE VIRTUAL TABLE news_search USING fts4 (news_title, tokenize=porter);
I use trigger to keep table NEWS and news_search in sync:
CREATE TRIGGER IF NOT EXISTS insert_news_trigger
AFTER INSERT ON news
BEGIN
INSERT OR IGNORE INTO news_search (news_title) VALUES (NEW.news_title);
END;
Question: how to use search? When I do MATCH in news_search table it returns me only records from this table, but I need *news_id* from news table. May be I should add *news_id* column to news_search table?
What is the proper way to use fts in sqlite?
Read the documentation; FTS tables also have a rowid column (also called docid) that you can set explicitly to the same value as the corresponding key of the original table.
Assuming that news.id is the rowid (i.e., INTEGER PRIMARY KEY), you should change your trigger to also copy that ID value into the news_search table.
You can the use that to look up the original record:
SELECT *
FROM news
WHERE id IN (SELECT docid
FROM news_search
WHERE news_title MATCH '😸')

create table with constraints from another table in sqlite?

I want create table from another table with constraint?
I used this query "create table destination as select * from source;" fro table creation.
But its copy only the column name in table without column constraint.
There is a special table named sqlite_master, holding the full CREATE TABLE statement for each table (it's modified as appropriate during ALTER TABLE).
I would make my application retrieve that CREATE TABLE statement:
SELECT sql FROM sqlite_master WHERE type='table' AND name='source';
Then I would replace the table name right after CREATE TABLE tokens, and execute the result as a new sqlite query.
I don't think that it's possible to do in sqlite's pure SQL without extensions.

SQLite Query to Insert a record If not exists

I want to insert a record into a sqlite table if its actually not inserted.
Let's say it has three fields pk, name, address
I want to INSERT new record with name if that name not added preveously.
Can we do with this in a single Query. Seems like its slightly different from SQL Queries sometimes.
Yes, you can do that with a single query.
INSERT ON CONFLICT IGNORE should help you: http://www.sqlite.org/lang_conflict.html
Put a unique key on the name, this will create a conflict when you try inserting a record if the name already exists.
The default is ABORT, so without the IGNORE, the statement will return an error. If you don't want that, use IGNORE.
If you can't make use of a UNIQUE INDEX in combination with INSERT INTO or INSERT OR IGNORE INTO, you could write a query like this;
INSERT INTO table (column)
SELECT value
WHERE NOT EXISTS (SELECT 1
FROM table
WHERE column = value)

Resources