packages feed

orville-postgresql-1.1.0.0: src/Orville/PostgreSQL/Extension/PgTrgm.hs

{- |
Copyright : Flipstone Technology Partners 2024
License   : MIT
Stability : Stable

Functionality for loading and using the
[pg_trgm](https://www.postgresql.org/docs/current/pgtrgm.html) extension. While this extension is
supplied by default with PostgreSQL, it is entirely up to users to ensure access to the extension.

@since 1.1.0.0
-}
module Orville.PostgreSQL.Extension.PgTrgm
  ( trigramSimilaritySyntheticField
  , trigramWordSimilaritySyntheticField
  , trigramStrictWordSimilaritySyntheticField
  , mkNamedTrigramGinIndexDefinition
  , mkNamedTrigramGistIndexDefinition
  , trigramSimilarity
  , trigramWordSimilarity
  , trigramStrictWordSimilarity
  ) where

import qualified Data.List.NonEmpty as NEL

import qualified Orville.PostgreSQL.Expr as Expr
import qualified Orville.PostgreSQL.Internal.IndexDefinition as IndexDefinition
import qualified Orville.PostgreSQL.Marshall as Marshall
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql

{- | Create a named GIN index over the given fields for fast text searching.

@since 1.1.0.0
-}
mkNamedTrigramGinIndexDefinition ::
  -- | The name of the index to be created.
  String ->
  -- | Field names to create the index over.
  NEL.NonEmpty Marshall.FieldName ->
  IndexDefinition.IndexDefinition
mkNamedTrigramGinIndexDefinition name =
  IndexDefinition.mkNamedIndexDefinition Expr.NonUniqueIndex name . RawSql.unsafeFromRawSql . trigramGinIndexFieldsExpr

trigramGinIndexFieldsExpr :: NEL.NonEmpty Marshall.FieldName -> RawSql.RawSql
trigramGinIndexFieldsExpr fields =
  let
    fieldExpr :: Marshall.FieldName -> RawSql.RawSql
    fieldExpr name =
      RawSql.toRawSql (Marshall.fieldNameToColumnName name)
        <> RawSql.space
        <> RawSql.fromString "gin_trgm_ops"
  in
    RawSql.fromString "USING GIN "
      <> RawSql.parenthesized (RawSql.intercalate RawSql.commaSpace (fmap fieldExpr fields))

{- | Create a named GIST index for fast text searching.  The index is created, over the fields each of
  with an optional override of the index parameter "siglen". See [pg_trgm index
  docs](https://www.postgresql.org/docs/current/pgtrgm.html#PGTRGM-INDEX) for more information.

@since 1.1.0.0
-}
mkNamedTrigramGistIndexDefinition ::
  -- | The name of the index to be created.
  String ->
  -- | Pairs of field name and optionally a value of the siglen parameter, to create the index over.
  NEL.NonEmpty (Marshall.FieldName, Maybe Int) ->
  IndexDefinition.IndexDefinition
mkNamedTrigramGistIndexDefinition name =
  IndexDefinition.mkNamedIndexDefinition Expr.NonUniqueIndex name . RawSql.unsafeFromRawSql . trigramGistIndexFieldsExpr

trigramGistIndexFieldsExpr ::
  NEL.NonEmpty (Marshall.FieldName, Maybe Int) ->
  RawSql.RawSql
trigramGistIndexFieldsExpr fields =
  let
    fieldExpr :: (Marshall.FieldName, Maybe Int) -> RawSql.RawSql
    fieldExpr (name, mbSiglen) =
      RawSql.toRawSql (Marshall.fieldNameToColumnName name)
        <> RawSql.space
        <> RawSql.fromString "gist_trgm_ops"
        <> maybe
          mempty
          (\len -> RawSql.parenthesized (RawSql.fromString "siglen=" <> RawSql.intDecLiteral len))
          mbSiglen
  in
    RawSql.fromString "USING GIST "
      <> RawSql.parenthesized (RawSql.intercalate RawSql.commaSpace (fmap fieldExpr fields))

{- | Create a synthetic field using the similarity function provided by pg_trgm.

@since 1.1.0.0
-}
trigramSimilaritySyntheticField ::
  -- | The column to be used in the similarity comparison
  Expr.ColumnName ->
  -- | The value to be compared against.
  Expr.ValueExpression ->
  -- | The alias to be used to name the similarity result.
  String ->
  -- | A field with the resulting similarity score between the column value and the comparison.
  Marshall.SyntheticField Double
trigramSimilaritySyntheticField colname compareVal fieldAlias =
  Marshall.syntheticField
    (trigramSimilarity (Expr.columnReference $ Expr.unqualified colname) compareVal)
    fieldAlias
    Marshall.double

{- | Create a synthetic field using the word_similarity function provided by pg_trgm.

@since 1.1.0.0
-}
trigramWordSimilaritySyntheticField ::
  -- | The column to be used in the word_similarity comparison
  Expr.ColumnName ->
  -- | The value to be compared against.
  Expr.ValueExpression ->
  -- | The alias to be used to name the word_similarity result.
  String ->
  -- | A field with the resulting word_similarity score between the column value and the comparison.
  Marshall.SyntheticField Double
trigramWordSimilaritySyntheticField colname compareVal fieldAlias =
  Marshall.syntheticField
    (trigramWordSimilarity (Expr.columnReference $ Expr.unqualified colname) compareVal)
    fieldAlias
    Marshall.double

{- | Create a synthetic field using the strict_word_similarity function provided by pg_trgm.

@since 1.1.0.0
-}
trigramStrictWordSimilaritySyntheticField ::
  -- | The column to be used in the strict_word_similarity comparison
  Expr.ColumnName ->
  -- | The value to be compared against.
  Expr.ValueExpression ->
  -- | The alias to be used to name the strict_word_similarity result.
  String ->
  -- | A field with the resulting strict_word_similarity score between the column value and the comparison.
  Marshall.SyntheticField Double
trigramStrictWordSimilaritySyntheticField colname compareVal fieldAlias =
  Marshall.syntheticField
    (trigramStrictWordSimilarity (Expr.columnReference $ Expr.unqualified colname) compareVal)
    fieldAlias
    Marshall.double

{- | Call the similarity function provided by pg_trgm, comparing the pair of values.

@since 1.1.0.0
-}
trigramSimilarity ::
  Expr.ValueExpression ->
  Expr.ValueExpression ->
  Expr.ValueExpression
trigramSimilarity firstVal secondVal =
  Expr.functionCall
    (Expr.functionName "similarity")
    [firstVal, secondVal]

{- | Call the word_similarity function provided by pg_trgm, comparing the pair of values.

@since 1.1.0.0
-}
trigramWordSimilarity ::
  Expr.ValueExpression ->
  Expr.ValueExpression ->
  Expr.ValueExpression
trigramWordSimilarity firstVal secondVal =
  Expr.functionCall
    (Expr.functionName "word_similarity")
    [firstVal, secondVal]

{- | Call the strict_word_similarity function provided by pg_trgm, comparing the pair of values.

@since 1.1.0.0
-}
trigramStrictWordSimilarity ::
  Expr.ValueExpression ->
  Expr.ValueExpression ->
  Expr.ValueExpression
trigramStrictWordSimilarity firstVal secondVal =
  Expr.functionCall
    (Expr.functionName "strict_word_similarity")
    [firstVal, secondVal]