text search query

This commit is contained in:
2025-11-08 14:29:54 +05:30
parent a795c0e8d6
commit a2b984c342
4 changed files with 38 additions and 31 deletions

View File

@@ -153,8 +153,8 @@ func (f Field) DateTrunc(level, as string) Field {
return Field("DATE_TRUNC('" + level + "', " + f.String() + ") AS " + as)
}
func (f Field) TsRank(query, as string) Field {
return Field("TS_RANK(" + f.String() + ", " + query + ") AS " + as)
func (f Field) TsRank(fieldName, as string) Field {
return Field("TS_RANK(" + f.String() + ", " + fieldName + ") AS " + as)
}
// EqualFold will use LOWER(column_name) = LOWER(val) for comparision

View File

@@ -1,13 +1,8 @@
package pgm
import (
"strconv"
)
// Table in database
type Table struct {
tsQuery *string
tsQueryAs *string
textSearch *textSearchCTE
Name string
DerivedTable Query
@@ -16,6 +11,13 @@ type Table struct {
debug bool
}
// text search Common Table Expression
type textSearchCTE struct {
name string
value string
alias string
}
// Debug when set true will print generated query string in stdout
func (t *Table) Debug() Clause {
t.debug = true
@@ -38,9 +40,8 @@ func (t *Table) Insert() InsertClause {
return qb
}
func (t *Table) WithTsQuery(q, as string) *Table {
t.tsQuery = &q
t.tsQueryAs = &as
func (t *Table) WithTextSearch(name, alias, textToSearch string) *Table {
t.textSearch = &textSearchCTE{name: name, value: textToSearch, alias: alias}
return t
}
@@ -49,6 +50,7 @@ func (t *Table) Select(field ...Field) SelectClause {
qb := &selectQry{
debug: t.debug,
fields: field,
textSearch: t.textSearch,
}
if t.DerivedTable != nil {
@@ -59,18 +61,6 @@ func (t *Table) Select(field ...Field) SelectClause {
qb.table = t.Name
}
if t.tsQuery != nil {
var as string
if t.tsQueryAs != nil && *t.tsQueryAs != "" {
as = *t.tsQueryAs
} else {
// add default as field
as = "query"
}
qb.args = append(qb.args, as)
qb.table += ", TO_TSQUERY('english', $" + strconv.Itoa(len(qb.args)) + ") " + as
}
return qb
}

View File

@@ -107,15 +107,19 @@ func TestSelectDerived(t *testing.T) {
}
func TestSelectTV(t *testing.T) {
expected := "SELECT users.first_name, users.last_name, users.email, TS_RANK(users.search_vector, query) AS rank" +
" FROM users, TO_TSQUERY('english', $1) query" +
" WHERE users.status_id = $2 AND users.search_vector @@ query" +
expected := "WITH ts AS (SELECT to_tsquery('english', $1) AS query)" +
" SELECT users.first_name, users.last_name, users.email, TS_RANK(users.search_vector, ts.query) AS rank" +
" FROM users" +
" JOIN user_sessions ON users.id = user_sessions.user_id" +
" CROSS JOIN ts" +
" WHERE users.status_id = $2 AND users.search_vector @@ ts.query" +
" ORDER BY rank DESC"
qry := db.User.
WithTsQuery("anki", "query").
Select(user.FirstName, user.LastName, user.Email, user.SearchVector.TsRank("query", "rank")).
Where(user.StatusID.Eq(1), user.SearchVector.TsQuery("query")).
WithTextSearch("ts", "query", "text to search").
Select(user.FirstName, user.LastName, user.Email, user.SearchVector.TsRank("ts.query", "rank")).
Join(db.UserSession, user.ID, usersession.UserID).
Where(user.StatusID.Eq(1), user.SearchVector.TsQuery("ts.query")).
OrderBy(pgm.Field("rank").Desc())
got := qry.String()

View File

@@ -135,6 +135,8 @@ type (
}
selectQry struct {
textSearch *textSearchCTE
table string
fields []Field
args []any
@@ -313,6 +315,12 @@ func (q *selectQry) Build(needArgs bool) (qry string, args []any) {
sb.Grow(q.averageLen())
if q.textSearch != nil {
var ts = q.textSearch
q.args = slices.Insert(q.args, 0, any(ts.value))
sb.WriteString("WITH " + ts.name + " AS (SELECT to_tsquery('english', $1) AS " + ts.alias + ") ")
}
// SELECT
sb.WriteString("SELECT ")
sb.WriteString(joinFileds(q.fields))
@@ -323,6 +331,11 @@ func (q *selectQry) Build(needArgs bool) (qry string, args []any) {
sb.WriteString(" " + strings.Join(q.join, " "))
}
// Search Query Cross join
if q.textSearch != nil {
sb.WriteString(" CROSS JOIN " + q.textSearch.name)
}
// WHERE
if len(q.where) > 0 {
sb.WriteString(" WHERE ")