Feature #5219
implement native full-text search for word index and CONTAINS support
90%
Related issues
History
#1 Updated by Eric Faulhaber over 5 years ago
- Subject changed from improve word index and CONTAINS performance to further word index and CONTAINS improvements
With #1587, we have provided first class support for the word index and CONTAINS operator, including parsing/indexing the content of word indexed columns, better sorting for results of CONTAINS-based queries, and improving the performance significantly over the previous, UDF-based approach.
This task serves as a placeholder for further work to improve the performance of CONTAINS queries and word indexed fields and any other refinements to word index and CONTAINS support.
The exploration and possible use of database native support to implement CONTAINS support is considered part of this effort, unless other improvements can be devised to bring the performance of the current implementation more in line with that of the 4GL's implementation.
We also need to review edit 3821c/12004, which changed the index selection portion of query conversion to ignore word indices. I am concerned this change may have had unwanted side effects to downstream conversion, besides removing the unwanted effect on the ORDER BY clause of the target query.
#2 Updated by Igor Skornyakov over 5 years ago
Eric Faulhaber wrote:
We also need to review edit 3821c/12004, which changed the index selection portion of query conversion to ignore word indices. I am concerned this change may have had unwanted side effects to downstream conversion, besides removing the unwanted effect on the ORDER BY clause of the target query.
Actuallу, after recent changes regarding sorting restoring the logic with word indices selection, should be easy. Moreover, it can make the logic for deciding if the word table should be selected for implicit sorting more consistent.
#4 Updated by Greg Shah almost 2 years ago
The exploration and possible use of database native support to implement CONTAINS support is considered part of this effort, unless other improvements can be devised to bring the performance of the current implementation more in line with that of the 4GL's implementation.
I think we should test native full text search now, to see if it can eliminate the performance bottlenecks (e.g. #8454) we are finding in our implementation (SQL UDFs + special tables for each index). Is the current implementation well encapsulated so that a prototype could be "plugged in"?
#6 Updated by Greg Shah about 1 month ago
- Parent task deleted (
#1585) - Subject changed from further word index and CONTAINS improvements to implement native full-text search for word index and CONTAINS support
The idea of this task is to leverage the native database full-text-search capability. It could be implemented for one database (e.g. PostgreSQL) and hidden in the dialect support so that the existing cross-database approach (our own word tables that we maintain with functions) is still available for any database that doesn't have its native approach implemented (or which doesn't have suitable FTS capability).
For some early discussion about this idea, please see #1587-37 and #1587-41 through #1587-51.
Considering tasks like #11616 and #11298, I think it is time for a prototype of the PostgeSQL native FTS support to see how it performs.
#9 Updated by Greg Shah about 1 month ago
- Related to Feature #1587: implement full support for word indexes added
#10 Updated by Constantin Asofiei 21 days ago
For FTS support, we have to_tsvector in PostgreSQL to get the 'words'; does this split into words in the same way as OpenEdge?
We need to ensure we have broad coverage of how OpenEdge splits text into words; we need to look into our UDF's implementation into words, any existing testcases, and expand them as needed.
Stefanel, please see the above.
#11 Updated by Stefanel Pezamosca 21 days ago
I don't think PostgreSQL to_tsvector splits words in the same way as OE does. But I'm thinking if we can use array_to_tsvector instead. So we have our custom word array that is output of our words UDF, that we save in a tsvector column.
Like:
SELECT array_to_tsvector(
ARRAY['Johns', 'ab', 'cd', '12.34', 'abc_def', 'user@example', 'com', 'x']
) as word_index;
word_index (tsvector)
----------
'12.34' 'Johns' 'ab' 'abc_def' 'cd' 'com' 'user@example' 'x'
And then use word_index column like word_index @@ to_tsquery('english', '(ab & c) | x')#12 Updated by Ovidiu Maxiniuc 21 days ago
to_tsquery() and to_tsvector() have a first parameter that configures how the word split is performed. Currently, I am aware of two values for this:
"english"- will use an English dictionary to split the target string into lexemes. These are 'root' forms of a word (no plurals, no conjugation/declination) and the not-interesting words (like prepositions and articles) are dropped. This is NOT what we want to use."simple"- the target text is converted to lowercase and stripped of stop words, but it does not undergo stemming. It is not splitting the text strictly by spaces, but uses Postgres's standard built-in tokenization engine, so we may not get the exact output to match 4GL's. We need to adjust it a bit. And we can do this by replacing the problematic characters from source text by the spaces (' '), which are safe.
#13 Updated by Stefanel Pezamosca 19 days ago
- Status changed from New to WIP
- % Done changed from 0 to 50
I have been working with some AI assistance on tsvector and optimization changes for CONTAINS implementation.
Summary¶
Instead of storing one row for every word, PostgreSQL now stores one aggregate tsvector row for each parent record, or for each extent element, and indexes it with GIN. This still uses the current separate word table approach, I will change the implementation later.
Main Changes¶
- Adds dialect-controlled support through
P2JPostgreSQLDialect.useTsvector4Contains. It is disabled by default and enabled only by a system property and when PostgreSQL word tables are in use; other database dialects retain the existing implementation. - Generates
tsvectorword columns (usingarray_to_tsvectorover our custom word splitting array), a primary key based onparent__idand optionallist__index, and a GIN index onword. - Rewrites PostgreSQL
CONTAINSpredicates as indexed matches between atsvectorand atsquery(word @@ cast(upper('a&(b|c)') as tsquery)). Boolean AND/OR logic, prefix matching, and case-sensitive or case-insensitive indexes are preserved. - Adds SQL functions that build and rebuild aggregate
tsvectorword tables using the existing 4GL-compatible word-splitting rules.
- Removing the obsolete relevance-weight aggregation and implicit
CONTAINSresult ordering code, because this never worked right and just added useless complexity. The legacy text word-table SQL is also simplified to query word tables directly without redundant joins to the parent table.
The migration code can convert existing word tables to the new format. This will change if we want to create the tsvector directly in the base table and get rid of the additional word table providing more performance (by avoiding joins) and less complexity.
Deployment and Migration¶
The PostgreSQL script runner detects legacy text-based word-table indexes and migrates them transactionally. Existing word rows are grouped into aggregate vectors, the word column is converted to tsvector, primary keys and GIN indexes are recreated, and generated triggers are updated to call words_tsvector.
The previous PostgreSQL word-table behavior can be selected by setting P2JPostgreSQLDialect.useTsvector4Contains=false. H2 and other dialects continue to use their existing word-table format and query behavior.
Storing automatically generated tsvector in the base table:¶
AS I said, the current tsvector implementation uses the same additional word-table approach. But, this is how we can store the generated tsvector in the base table instead of a separate word table. This is a more efficient approach because it avoids joins and allows for faster queries.
Example:
ALTER TABLE imdb_movies
ADD COLUMN textsearchable_index_col tsvector
GENERATED ALWAYS AS (
to_tsvector('simple', coalesce(series_title, ''))
) STORED;
CREATE INDEX textsearch_idx
ON imdb_movies
USING GIN (textsearchable_index_col);
This adds a persistent tsvector column that is automatically generated and updated by PostgreSQL whenever the row changes, so we don't need custom triggers. The index is built on this stored column.
CONTAINS Performance Test Comparison¶
Here are some performance results from testing the new implementation against the legacy implementation. The same test cases were run against the same dataset using: trunk / trunk + 5219a(legacy) and trunk + 5219a(tsvector):
Note: These results may not be fully accurate because each test case was run only once, but they should provide a rough indication of the performance differences. I will test more serious when the implementation is complete. Also I believe the rest of the performance degradation is caused by how FWD handles the large number of rows in the result set.
| search_w | counter | trunk(ms) | 5219a(legacy) | 5219a(tsvector) |
|---|---|---|---|---|
| abandon | 26,037 | 330 | 292 | 340 |
| abandon & ability | 672 | 39 | 23 | 22 |
| bit & buy | 721 | 35 | 22 | 21 |
| abandon & (ability & absolute) | 19 | 27 | 10 | 7 |
| above & ability | 701 | 25 | 21 | 16 |
| abandon & (ability | academic) | 1,382 | 132 | 143 | 22 |
| accident | (account & accurate) | 26,407 | 347 | 272 | 175 |
| accident | account | accurate | 75,220 | 539 | 296 | 242 |
| expr | records | trunk(ms) | 5219a(legacy) | 5219a(tsvector) |
|---|---|---|---|---|
| 'a0' | 1 | 764 | 508 | 2 |
| 'b0' | 2 | 771 | 508 | 4 |
| 'c0' | 4 | 777 | 507 | 2 |
| 'g0' | 64 | 774 | 529 | 5 |
| 'g0 | g1' | 32 | 3 | 3 | 3 |
| 'h0' | 128 | 1387 | 963 | 8 |
| 'h0 | h1' | 64 | 4 | 3 | 3 |
| 'h0 | h1 | h2' | 32 | 17 | 5 | 2 |
| 'r0' | 131072 | 6059 | 4956 | 5187 |
| 'r0 | r1' | 65536 | 3505 | 3357 | 3016 |
| 'r0 | r1 | r2' | 32768 | 1502 | 1949 | 1706 |
| 's0' | 262144 | 11094 | 10589 | 10170 |
| 's0 | s1' | 131072 | 7356 | 7014 | 6204 |
| 's0 | s1 | s2' | 65536 | 3257 | 4091 | 3619 |
| 't0' | 0 | 1356 | 863 | 831 |
| 't0 | t1' | 0 | 2 | 1 | 2 |
| 't0 | t1 | t2' | 0 | 8 | 3 | 2 |
So, I will commit the changes in 5219a after some cleanups.
The implementation is not complete in the sens that it still uses the additional word table to store the tsvector. The next step is to store the generated tsvector directly in the base table, which will improve performance and reduce complexity. Another thing is that is only specific to PostgreSQL, so I need to move some logic from FqlToSqlGenerator to the dialect class, so that other dialects can implement their own full text search support in the future.
Let me now what do you think about the above and if you have any suggestions.
#14 Updated by Stefanel Pezamosca 19 days ago
I have created 5219a from trunk/16671 and committed in revision 16672 the first version of CONTAINS implementation using PostgreSQL tsvector columns and GIN indexes.
#15 Updated by Eric Faulhaber 19 days ago
These are exciting results! But please help me understand what 5219a (legacy) represents (compared to trunk). Thanks.
#16 Updated by Stefanel Pezamosca 19 days ago
Eric Faulhaber wrote:
These are exciting results! But please help me understand what 5219a (legacy) represents (compared to trunk). Thanks.
Ok, so legacy means the usual word tables like how are they in trunk but with some optimizations made on how the SQL query is built in FqlToSqlConverter.
#17 Updated by Eric Faulhaber 19 days ago
Stefanel Pezamosca wrote:
[...]
The changes also improves the performance further for the legacy word-table implementation by:
- Removing the obsolete relevance-weight aggregation and implicit
CONTAINSresult ordering code, because this never worked right and just added useless complexity. The legacy text word-table SQL is also simplified to query word tables directly without redundant joins to the parent table.
How does our results-sorting behavior for CONTAINS queries compare to the 4GL behavior, currently with trunk and now with 5219a? I suspect we probably need additional unit tests in this area.
#18 Updated by Stefanel Pezamosca 19 days ago
Eric Faulhaber wrote:
Stefanel Pezamosca wrote:
[...]
The changes also improves the performance further for the legacy word-table implementation by:
- Removing the obsolete relevance-weight aggregation and implicit
CONTAINSresult ordering code, because this never worked right and just added useless complexity. The legacy text word-table SQL is also simplified to query word tables directly without redundant joins to the parent table.How does our results-sorting behavior for CONTAINS queries compare to the 4GL behavior with trunk and now with 5219a? I suspect we probably need additional unit tests in this area.
I need to do more testing on the sorting behavior, but based on an initial online search, word indexes do not appear to follow any defined order.
Also, even if the sorting is influenced by CONTAINS in some way, the resulting order seems fairly random and unintuitive. In most cases, I expect it to be overridden by explicit ORDER BY clauses anyway.
#20 Updated by Eric Faulhaber 19 days ago
Ok. I had the impression from your comment about what was removed that we were previously attempting to implement some implicit, relevance-based sorting.
#21 Updated by Stefanel Pezamosca 19 days ago
Greg Shah wrote:
enabled only by a system property
Please never use properties for FWD configuration. Server cfg should be in the directory.
Ok, I copied how useCTE4Contains or others are set in PosgreSQLDialect. So, I need a configuration option to enable tsvector usage both for conversion and runtime.
#22 Updated by Greg Shah 19 days ago
Stefanel Pezamosca wrote:
Greg Shah wrote:
enabled only by a system property
Please never use properties for FWD configuration. Server cfg should be in the directory.
Ok, I copied how useCTE4Contains or others are set in PosgreSQLDialect. So, I need a configuration option to enable tsvector usage both for conversion and runtime.
Which never should have been implemented but wasn't caught at the time.
#23 Updated by Stefanel Pezamosca 19 days ago
Eric Faulhaber wrote:
Ok. I had the impression from your comment about what was removed that we were previously attempting to implement some implicit, relevance-based sorting.
I believe this was the intended behavior, but it does not appear to work as expected and differs from the 4GL implementation. I’ll run additional tests to better understand the behavior. That said, I’m concerned that replicating a sorting approach without a clear or predictable sort order may not be very user-friendly.
#24 Updated by Stefanel Pezamosca 16 days ago
I have been experimenting with creating the tsvector column directly in the base table. I also moved most of the PostgreSQL-specific implementation from FqlToSqlConverter to the dialect class, and replaced P2JPostgreSQLDialect.useTsvector4Contains with a property named tsvector-contains that can be read from p2j.cfg.xml and directory.xml. I guess I could add a new parameter to the Table or Column Parameter during conversion to preserve the tsvector-contains state?
Adding this to schema_table_* after the create table statement creates the new tsvector column with the tsvector__ prefix:
alter table words
add column if not exists tsvector__words tsvector
generated always as (udf.words_to_tsvector(coalesce(words, ''), true)) stored;
The udf.words_to_tsvector() function uses our custom udf.words function. For this to work, we need to make sure the UDFs are updated before schema_table_* is run during the import process:
CREATE OR REPLACE FUNCTION udf.words_to_tsvector(txt text, toUpperCase boolean)
RETURNS tsvector
LANGUAGE sql
IMMUTABLE STRICT PARALLEL SAFE
AS $function$
SELECT array_to_tsvector(udf.words(txt, toUpperCase, toUpperCase));
$function$;
I also added this index to the schema_index_* DDL:
create index if not exists idx__tsvector__words__words on words using gin (tsvector__words);
With this FQL:
select words.recid, words.words from Words__Impl__ as words where (contains(words.words, ?0)) order by words.recno asc, words.recid asc;
We can generate this simpler SQL regardless of the contains expression:
select words__imp0_.recid, words__imp0_.words as col0_0_ from words words__imp0_ where (words__imp0_.tsvector__words @@ cast(upper(?) as tsquery)) order by words__imp0_.recno asc, words__imp0_.recid asc limit ? offset ?
#25 Updated by Stefanel Pezamosca 16 days ago
These are some cold performance results. Compared with #5219-13, the results are better. After the Bitmap Index Scan, PostgreSQL uses a Bitmap Heap Scan that takes most of the time.
| search_w | counter | f_time |
|---|---|---|
| abandon | 26,037 | 268 |
| abandon & ability | 672 | 16 |
| bit & buy | 721 | 12 |
| abandon & (ability & absolute) | 19 | 5 |
| above & ability | 701 | 11 |
| abandon & (ability | academic) | 1,382 | 15 |
| accident | (account & accurate) | 26,407 | 125 |
| accident | account | accurate | 75,220 | 269 |
| expr | records | elapsed time |
|---|---|---|
| 'a0' | 1 | 1 |
| 'b0' | 2 | 2 |
| 'c0' | 4 | 2 |
| 'g0' | 64 | 4 |
| 'g0 & g1' | 32 | 2 |
| 'h0' | 128 | 6 |
| 'h0 & h1' | 64 | 2 |
| 'h0 & h1 & h2' | 32 | 2 |
| 'r0' | 131072 | 4476 |
| 'r0 & r1' | 65536 | 2268 |
| 'r0 & r1 & r2' | 32768 | 1244 |
| 's0' | 262144 | 9100 |
| 's0 & s1' | 131072 | 5004 |
| 's0 & s1 & s2' | 65536 | 2745 |
| 't0' | 0 | 169 |
| 't0 & t1' | 0 | 1 |
| 't0 & t1 & t2' | 0 | 2 |
The bad performace above is because of how FWD handles the query. It's selecting a list of RECIDs first and then iterates each RECID to get the actual data.
#26 Updated by Stefanel Pezamosca 15 days ago
- % Done changed from 50 to 80
Committed latest changes implementing these in 5219a revision 16673.
Overall, the changes are almost complete and stable. I'll continue doing more tests, including sorting behavior.
#27 Updated by Stefanel Pezamosca 15 days ago
- Assignee set to Stefanel Pezamosca
In 5219a revision 16678 (rebased from trunk/16674) I committed more changes to move tsvector to PostgreSQL specific dialect. Renamed other global references from tsvector support to FTS (Full-Text Search) support.
#28 Updated by Stefanel Pezamosca 13 days ago
I got some progress working on making some testcases to test 5219a.
I have a pipeline working where the tests are written, copied to a machine with OE installed, tested with OE both simple procedures and ABLUnit. If any error occurs, they are fixed and rerun, then copied to a template fwd project, compiled and tested.
Finally, the result are compared between OE and FWD and if there are differences I can start a multi phase process to try and fix each difference one at a time.
The current test-suite I have, helped me find and fix some issues like client crashes and wrongly handled syntax errors. I'm trying to aggregate more cases.
I didn't run the tests using trunk yet, but with 5219a all of them passed exactly like in OE. I also have to test other projects because there are some changes on how CONTAINS syntax errors gets handled, and they might be risky, so I'm not sure if I want to really keep them.
#29 Updated by Greg Shah 13 days ago
To the degree that we have a bunch of native Java test code for this (I recall that may be how Igor wrote them), each scenario should be re-implemented as a 4GL ABLUnit test. Those can be used along with the new tests you are writing, which should also be ABLUnit.
#30 Updated by Stefanel Pezamosca 12 days ago
- File trunk-wordtables-result.txt
added - File 5219a-wordtables-result.txt
added - File 5219a-tsvector-result.txt
added - % Done changed from 80 to 90
I will check out any other testcase I find with CONTAINS usage. I remember that Razvan also expanded the existing Contains test suite for: #9021 or #10447.
I’ve attached the results from the newly created CONTAINS test suite.
Both 5219a variants (wordtable and tsvector enabled) have only 14 failing test cases, all related to query error/parsing handling.
Trunk, however, fails much more severely, with NPEs and other errors. So 5219a fixes a number of issues that are currently in trunk.
I’m still analyzing whether I can simplify some fixes and make the implementation cleaner before I commit the latest fixes. Overall 5219a is significantly more stable than trunk was.
5219a:
Test run finished after 1756 ms [ 11 containers found ] [ 0 containers skipped ] [ 11 containers started ] [ 0 containers aborted ] [ 11 containers successful ] [ 0 containers failed ] [ 103 tests found ] [ 0 tests skipped ] [ 103 tests started ] [ 0 tests aborted ] [ 89 tests successful ] [ 14 tests failed ]trunk:
Test run finished after 1712 ms [ 11 containers found ] [ 0 containers skipped ] [ 11 containers started ] [ 0 containers aborted ] [ 11 containers successful ] [ 0 containers failed ] [ 103 tests found ] [ 0 tests skipped ] [ 103 tests started ] [ 0 tests aborted ] [ 55 tests successful ] [ 48 tests failed ]There are 14 tests failing because I removed some potential fixes from 5219a that affected how Contains Exceptions are handled. These changes where risky because were potentially affecting other things unrelated to CONTAINS implementation. (Also these didn't fix all cases just a few of them)
I’ll continue with additional testing and focus only on functional issues. Error-handling inconsistencies, such as differences in error message output, can be ignored for now.
#32 Updated by Stefanel Pezamosca 9 days ago
- File 5219a_rev16687_results_v1.txt
added
I got all the tests from testcases/tests/persistence/word_index into the newly created test suite and converted the remaining tests to ABLUnit.
5219a has been rebased from trunk/16681, and the latest fixes have been committed in revision 16687.
I also need to validate this fixes by running regression tests for the other available projects.
I still need to revisit the sorting issue. If a query includes an explicit BY clause in 4GL, that ordering will be honored. For simpler queries (with no BY or other predicates), there appears to be an implicit ordering. The weight aggregator implementation in trunk attempted to replicate that behavior, but it didn’t work that well. There may be a better way to do it.
This is reflected by the following testcases:
testNaturalOrderIsWordIndexNotRecno ✘ Expected: 2,4,5,3,1, but was: 1,2,3,4,5, naturalOrderFollowsTheLeadingGroup ✘ 4 of 4 cases disagree for the same expressions with no BY clause; [two-groups-forward] expected [1,3,2,4], got [1,2,3,4]; [two-groups-swapped] expected [1,4,2,3], got [1,2,3,4]; [three-way-forward] expected [7,10,8,12,9,11], got [7,8,9,10,11,12]; [three-way-swapped] expected [7,11,8,10,9,12], got [7,8,9,10,11,12]As you can see each of the Test() methods in the newly created test suite have more cases each. Should I separate each specific case into separate methods? (Like: naturalOrderFollowsTheLeadingGroup01, naturalOrderFollowsTheLeadingGroup02, etc?)
Test run finished after 10773 ms [ 40 containers found ] [ 0 containers skipped ] [ 40 containers started ] [ 0 containers aborted ] [ 40 containers successful ] [ 0 containers failed ] [ 505 tests found ] [ 0 tests skipped ] [ 505 tests started ] [ 0 tests aborted ] [ 495 tests successful ] [ 10 tests failed ]
#34 Updated by Stefanel Pezamosca 9 days ago
Greg Shah wrote:
Should I separate each specific case into separate methods? (Like: naturalOrderFollowsTheLeadingGroup01, naturalOrderFollowsTheLeadingGroup02, etc?)
This is indeed what we prefer. Can it be done relatively quickly? If so, please do it.
I have done it, there are 1429 tests and 26 failures with 5219a now.
#35 Updated by Stefanel Pezamosca 8 days ago
- File 5219a_16687_preview.txt
added
I have some more committed changes in revision 16688 that fixed the majority of the word_index testcases.
I attached the current state of the results. The remaining tests are related with some unusual characters being processed differently (7 cases in tests.persistence.word_index.ContainsCollationTest) and the natural order thing:
testNaturalOrderIsWordIndexNotRecno ✘ Expected: 2,4,5,3,1, but was: 1,2,3,4,5, │ ├─ naturalOrderFollowsTheLeadingGroup1 ✘ an AND of two OR-groups with no BY clause, forward: expected [1,3,2,4], got [1,2,3,4] │ ├─ naturalOrderFollowsTheLeadingGroup2 ✘ an AND of two OR-groups with no BY clause, groups swapped: expected [1,4,2,3], got [1,2,3,4] │ ├─ naturalOrderFollowsTheLeadingGroup3 ✘ an AND of two three-term OR-groups with no BY clause, forward: expected [7,10,8,12,9,11], got [7,8,9,10,11,12] │ ├─ naturalOrderFollowsTheLeadingGroup4 ✘ an AND of two three-term OR-groups with no BY clause, groups swapped: expected [7,11,8,10,9,12], got [7,8,9,10,11,12]
#36 Updated by Stefanel Pezamosca about 14 hours ago
- Status changed from WIP to Review
- reviewer Alexandru Lungu, Constantin Asofiei, Greg Shah added
Aside from the failing test cases related to natural ordering without an explicit BY clause, which I don’t expect to matter in most scenarios, since custom sorting is usually selected based on an index, the changes in 5219a seem quite stable.
That said, 5219a includes a fairly large set of changes to improve how we handle dynamic query parsing and error handling especially for CONTAINS. If this feels too risky or not appropriate to commit as is, I can extract only the critical functional fixes and move the remaining changes into 5219b.
Please review 5219a. I’ll continue testing to make sure the changes remain stable.
To enable CONTAINS conversion with FTS (tsvector) this is needed in p2j.cfg.xml (PosgreSQL UDFs need to be updated also):
<parameter name="fts4contains" value="true" />
#37 Updated by Constantin Asofiei about 14 hours ago
Stefanel Pezamosca wrote:
Aside from the failing test cases related to natural ordering without an explicit BY clause, which I don’t expect to matter in most scenarios, since custom sorting is usually selected based on an index, the changes in 5219a seem quite stable.
That said, 5219a includes a fairly large set of changes to improve how we handle dynamic query parsing and error handling especially for CONTAINS. If this feels too risky or not appropriate to commit as is, I can extract only the critical functional fixes and move the remaining changes into 5219b.
Please review 5219a. I’ll continue testing to make sure the changes remain stable.
To enable CONTAINS conversion with FTS (tsvector) this is needed in
p2j.cfg.xml:
[...]
Is there a migration script for existing databases? Or is running word_reindex via ImportWorker enough?
#38 Updated by Stefanel Pezamosca about 12 hours ago
Constantin Asofiei wrote:
Please review 5219a. I’ll continue testing to make sure the changes remain stable.
To enable CONTAINS conversion with FTS (tsvector) this is needed in
p2j.cfg.xml:
[...]Is there a migration script for existing databases? Or is running
word_reindexviaImportWorkerenough?
word_reindex is only used when word_tables are used. If fts4contains is false then no migration is needed, 5219a is compatible with current database structure from trunk.
If fts4contains is true. Then schema_word_tables_ are not generating anymore. Everything will be generated in schema_table_* and schema_index_*. At the moment the only migration stepts will be to update UDFs (for the new udf.words_to_tsvector used, see #5219-24). After the UDF update we need to run all ALTER TABLE statements generated in schema_table_* and the GIN index definitions from schema_index_*.
#39 Updated by Ovidiu Maxiniuc about 10 hours ago
Stefanel,
I did a review of 5219a. The update is OK. I have nothing to comment. It is actually very good. Thumbs up!
At the same time Greg asked me last week to do a brainstorm and compose some edge-case test-cases to have an strong baseline for possible future changes. Now I have 100+ hand-crafted tests. I encountered some issues which I have fixed (actually errors, in TRPL, to be handled via CompileException). However, I was able to create some testcases which fail to convert statically (the code is not valid in Java) and in dynamic mode it fails at an unexpected level (the JAST tree gets to be processed by the RuntimeInterpreter, but the problem should have been detected earlier, at dynamic conversion, when the specific JAST was constructed).
#40 Updated by Stefanel Pezamosca about 10 hours ago
Ovidiu Maxiniuc wrote:
At the same time Greg asked me last week to do a brainstorm and compose some edge-case test-cases to have an strong baseline for possible future changes. Now I have 100+ hand-crafted tests. I encountered some issues which I have fixed (actually errors, in TRPL, to be handled via
CompileException). However, I was able to create some testcases which fail to convert statically (the code is not valid in Java) and in dynamic mode it fails at an unexpected level (the JAST tree gets to be processed by theRuntimeInterpreter, but the problem should have been detected earlier, at dynamic conversion, when the specific JAST was constructed).
Thank you for the review.
Are these issues a regression of 5219a or they are also in trunk, or something else ?
#41 Updated by Stefanel Pezamosca about 10 hours ago
Here is the final results after running some local performance testcases. There are 2 tables used with a milion records each. There is some error margin of few ms, but overall the performance gain is still noticeable for some cases.
| search_w | counter | trunk (ms) | 5219a word tables (ms) | gain | 5219a tsvector (ms) | gain |
|---|---|---|---|---|---|---|
| abandon | 26,037 | 362 | 285 | +21% (1.27x) | 324 | +10% (1.12x) |
| abandon & ability | 672 | 17 | 26 | -53% (0.65x) | 15 | n/s |
| bit & buy | 721 | 15 | 22 | -47% (0.68x) | 13 | n/s |
| abandon & (ability & absolute) | 19 | 9 | 9 | n/s | 5 | n/s |
| above & ability | 701 | 14 | 16 | n/s | 11 | n/s |
| abandon & (ability | academic) | 1,382 | 113 | 134 | -19% (0.84x) | 18 | +84% (>4.9x) |
| accident | (account & accurate) | 26,407 | 359 | 335 | +7% (1.07x) | 160 | +55% (2.24x) |
| accident | account | accurate | 75,220 | 563 | 356 | +37% (1.58x) | 296 | +47% (1.90x) |
| Total | 1,452 | 1,183 | +19% (1.23x) | 842 | +42% (1.72x) |
I think the big times reported in this second table are because the test uses a FOR EACH without NO LOCK.
| expr | records | trunk (ms) | 5219a w. tables (ms) | gain | 5219a tsvector (ms) | gain |
|---|---|---|---|---|---|---|
| 'a0' | 1 | 952 | 542 | +43% (1.76x) | 144 | +85% (6.61x) |
| 'b0' | 2 | 933 | 515 | +45% (1.81x) | 146 | +84% (6.39x) |
| 'c0' | 4 | 980 | 523 | +47% (1.87x) | 146 | +85% (6.71x) |
| 'g0' | 64 | 989 | 534 | +46% (1.85x) | 5 | +99% (>98x) |
| 'g0 & g1' | 32 | 3 | 4 | n/s | 2 | n/s |
| 'h0' | 128 | 1,812 | 988 | +45% (1.83x) | 7 | +99% (>150x) |
| 'h0 & h1' | 64 | 4 | 3 | n/s | 2 | n/s |
| 'h0 & h1 & h2' | 32 | 5 | 3 | n/s | 2 | n/s |
| 'r0' | 131,072 | 7,615 | 5,341 | +30% (1.43x) | 4,994 | +34% (1.52x) |
| 'r0 & r1' | 65,536 | 4,326 | 3,135 | +28% (1.38x) | 2,520 | +42% (1.72x) |
| 'r0 & r1 & r2' | 32,768 | 2,635 | 2,185 | +17% (1.21x) | 1,193 | +55% (2.21x) |
| 's0' | 262,144 | 12,358 | 10,991 | +11% (1.12x) | 9,646 | +22% (1.28x) |
| 's0 & s1' | 131,072 | 7,993 | 7,474 | +6% (1.07x) | 5,166 | +35% (1.55x) |
| 's0 & s1 & s2' | 65,536 | 4,981 | 4,196 | +16% (1.19x) | 2,813 | +44% (1.77x) |
| 't0' | 0 | 1,659 | 855 | +48% (1.94x) | 136 | +92% (12.20x) |
| 't0 & t1' | 0 | 2 | 2 | n/s | 1 | n/s |
| 't0 & t1 & t2' | 0 | 3 | 2 | n/s | 2 | n/s |
| Total | 47,250 | 37,293 | +21% (1.27x) | 26,925 | +43% (1.75x) |
I tested also 2 projects with 5219a in word table mode and I also asked Razvan and Artur to test their apps. Didn't get any issues at the moment.
I will add some change to ImportWorker word_reindex target to work with FTS/tsvector implementation.
#42 Updated by Constantin Asofiei about 10 hours ago
Stefanel, please get a table with an word index (with extent field also) and post the DDL (and associated triggers, etc) with trunk and 5219a.
#43 Updated by Greg Shah about 10 hours ago
What is the reasoning behind making the new approach optional/conditional? If the changes don't cause regressions and are uniformly better, then we want this to be the ONLY word index approach.
At the moment the only migration stepts will be to update UDFs (for the new udf.words_to_tsvector used, see #5219-24). After the UDF update we need to run all ALTER TABLE statements generated in schema_table_* and the GIN index definitions from schema_index_*.
We need a simple process for this. If the customer has to implement this themselves, it will be potentially error prone and will cause issues that will take time to figure out. Instead, we need our own migration process to be available as part of the change.
#44 Updated by Stefanel Pezamosca about 10 hours ago
Greg Shah wrote:
What is the reasoning behind making the new approach optional/conditional? If the changes don't cause regressions and are uniformly better, then we want this to be the ONLY word index approach.
I think to help with backward compatibility and migration until all the clients are using this mode? This can be easily changed to activate by default for postgresql databases if wanted.
We need a simple process for this. If the customer has to implement this themselves, it will be potentially error prone and will cause issues that will take time to figure out. Instead, we need our own migration process to be available as part of the change.
Working on it.
#45 Updated by Stefanel Pezamosca about 9 hours ago
- File trunk_word_table_ddl.sql added
- File 5219a_tsvector_ddl.sql added
- File 5219a_word_table_ddl.sql added
Constantin Asofiei wrote:
Stefanel, please get a table with an word index (with extent field also) and post the DDL (and associated triggers, etc) with trunk and 5219a.
I created a table with a 1 simple field and 1 extent field both with a word index. I combined all related ddl statements in one file for each: trunk, 5219a(with w. tables) and 5219a(with tsvector).
#46 Updated by Greg Shah about 9 hours ago
I think to help with backward compatibility and migration until all the clients are using this mode? This can be easily changed to activate by default for postgresql databases if wanted.
OK, we will have to track the customers and when each has migrated then we will eliminate this flag.
How does this get configured at runtime?
#47 Updated by Stefanel Pezamosca about 9 hours ago
Greg Shah wrote:
How does this get configured at runtime?
Runtime can also read from cfg/p2j.cfg.xml that is saved in project.jar, so the same flag.
#48 Updated by Greg Shah about 9 hours ago
No, we don't want to do that. The flag for runtime needs to be in the directory.
#49 Updated by Constantin Asofiei about 9 hours ago
Greg Shah wrote:
No, we don't want to do that. The flag for runtime needs to be in the directory.
Greg, I don't think we should do this in directory, p2j.cfg.xml and directory will need to be in sync - as the DDL for word tables and triggers and such will be different. We can't switch runtime from one approach to the other.
#50 Updated by Greg Shah about 9 hours ago
OK, but I dislike this intensely. The use of p2j.cfg.xml at runtime is something we never should have had in the first place. I intend to murder it sooner rather than later. This dependency now make my assassination job harder.