FIX: add support for PG 14 and up (#20137)

Previously to_tsquery would split terms and join with &

In PG 14 terms are split and use <-> which means followed directly by.

In PG 13:

discourse_test=# SELECT to_tsquery('english', '''hello world''');
     to_tsquery
---------------------
 'hello' & 'world'
(1 row)

In PG 14:

discourse_test=# SELECT to_tsquery('english', '''hello world''');
     to_tsquery
---------------------
 'hello' <-> 'world'
(1 row)


Change is very unobtrosive, we simply amend our to_tsquery to behave like
it used to behave and make no use of the `<->` operator


More detail at: https://akorotkov.github.io/blog/2021/05/22/pg-14-query-parsing/

Note that plainto_tsquery used elsewhere in Discourse keeps the exact
same function.

This also corrects a faulty test that was passing by a fluke on older
version of PG
This commit is contained in:
Sam 2023-02-03 08:11:25 +11:00 committed by GitHub
parent 277c5770b0
commit 1dba1aca27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 7 additions and 4 deletions

View File

@ -1262,6 +1262,9 @@ class Search
ts_config = ActiveRecord::Base.connection.quote(ts_config) if ts_config
escaped_term = wrap_unaccent("'#{escape_string(term)}'")
tsquery = "TO_TSQUERY(#{ts_config || default_ts_config}, #{escaped_term})"
# PG 14 and up default to using the followed by operator
# this restores the old behavior
tsquery = "REPLACE(#{tsquery}::text, '<->', '&')::tsquery"
tsquery = "REPLACE(#{tsquery}::text, '&', '#{escape_string(joiner)}')::tsquery" if joiner
tsquery
end

View File

@ -969,7 +969,7 @@ RSpec.describe Search do
it "aggregates searches in a topic by returning the post with the lowest post number" do
post = Fabricate(:post, topic: topic, raw: "this is a play post")
post2 = Fabricate(:post, topic: topic, raw: "play play playing played play")
_post2 = Fabricate(:post, topic: topic, raw: "play play playing played play")
post3 = Fabricate(:post, raw: "this is a play post")
5.times { Fabricate(:post, topic: topic, raw: "play playing played") }
@ -1876,7 +1876,7 @@ RSpec.describe Search do
)
post2 = Fabricate(:post, raw: "test URL post with")
expect(Search.execute("test post with 'a URL).posts").posts).to eq([post2, post])
expect(Search.execute("test post URL l").posts).to eq([post2, post])
expect(Search.execute(%{"test post with 'a URL"}).posts).to eq([post])
expect(Search.execute(%{"https://some.site.com/search?q=test.test.test"}).posts).to eq([post])
expect(
@ -2311,11 +2311,11 @@ RSpec.describe Search do
it "escapes the term correctly" do
expect(Search.ts_query(term: 'Title with trailing backslash\\')).to eq(
"TO_TSQUERY('english', '''Title with trailing backslash\\\\\\\\'':*')",
"REPLACE(TO_TSQUERY('english', '''Title with trailing backslash\\\\\\\\'':*')::text, '<->', '&')::tsquery",
)
expect(Search.ts_query(term: "Title with trailing quote'")).to eq(
"TO_TSQUERY('english', '''Title with trailing quote'''''':*')",
"REPLACE(TO_TSQUERY('english', '''Title with trailing quote'''''':*')::text, '<->', '&')::tsquery",
)
end
end