Skip to content

Commit

Permalink
add pg_trgm support
Browse files Browse the repository at this point in the history
  • Loading branch information
tminglei committed Jun 21, 2017
1 parent 6e94646 commit 3ab5ee8
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 1 deletion.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ before_script:
- psql test -c 'CREATE EXTENSION IF NOT EXISTS hstore;' -U postgres
- psql test -c 'CREATE EXTENSION IF NOT EXISTS ltree;' -U postgres
- psql test -c 'CREATE EXTENSION IF NOT EXISTS postgis;' -U postgres
- psql test -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;' -U postgres
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.github.tminglei.slickpg.str
package com.github.tminglei.slickpg
package str

import slick.jdbc.PostgresProfile

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.github.tminglei.slickpg
package trgm

import slick.ast.Library.{SqlFunction, SqlOperator}
import slick.ast.TypedType
import slick.jdbc.{JdbcTypesComponent, PostgresProfile}
import slick.lifted.ExtensionMethods

/**
* Created by minglei on 6/21/17.
*/
trait PgTrgmExtensions extends JdbcTypesComponent { driver: PostgresProfile =>
import driver.api._

object TrgmLibrary {
val % = new SqlOperator("%")
val`<%` = new SqlOperator("<%")
val %> = new SqlOperator("%>")
val <-> = new SqlOperator("<->")
val <<-> = new SqlOperator("<<->")
val <->> = new SqlOperator("<->>")

val similarity = new SqlFunction("similarity")
val word_similarity = new SqlFunction("word_similarity")
}

class PgTrgmColumnExtensionMethods[P1](val c: Rep[P1]) extends ExtensionMethods[String, P1] {
protected implicit def b1Type = implicitly[TypedType[String]]

def % [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Boolean, R]) = {
om.column(TrgmLibrary.%, n, e.toNode)
}
def `<%` [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Boolean, R]) = {
om.column(TrgmLibrary.`<%`, n, e.toNode)
}
def %> [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Boolean, R]) = {
om.column(TrgmLibrary.%>, n, e.toNode)
}

def <-> [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Double, R]) = {
om.column(TrgmLibrary.<->, n, e.toNode)
}
def <<-> [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Double, R]) = {
om.column(TrgmLibrary.<<->, n, e.toNode)
}
def <->> [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Double, R]) = {
om.column(TrgmLibrary.<->>, n, e.toNode)
}

def similarity [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Double, R]) = {
om.column(TrgmLibrary.similarity, n, e.toNode)
}
def wordSimilarity [P2, R](e: Rep[P2])(implicit om: o#arg[String, P2]#to[Double, R]) = {
om.column(TrgmLibrary.word_similarity, n, e.toNode)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.tminglei.slickpg
package trgm

import slick.jdbc.PostgresProfile

/**
* Created by minglei on 6/21/17.
*/
trait PgTrgmSupport extends PgTrgmExtensions { driver: PostgresProfile =>
import driver.api._

trait PgTrgmImplicits {
implicit def pgStringColumnExtensionMethods(c: Rep[String]) = new PgTrgmColumnExtensionMethods[String](c)
implicit def pgStringOptionColumnExtensionMethods(c: Rep[Option[String]]) = new PgTrgmColumnExtensionMethods[Option[String]](c)
}
}
13 changes: 13 additions & 0 deletions core/src/main/scala/com/github/tminglei/slickpg/trgm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Supported Search Oper/Functions
-------------------------------

| Slick Oper/Function | PG Oper/Function | Description |
| ------------------- | ---------------- | --------------------------------------------------------------------------------- |
| % | % | Returns true if its arguments have a similarity that is greater than the current similarity threshold set by pg_trgm.similarity_threshold |
| &lt; | &lt;% | Returns true if its first argument has the similar word in the second argument and they have a similarity that is greater than the current word similarity threshold set by pg_trgm.word_similarity_threshold |
| %&gt; | %&gt; | Commutator of the &lt;% operator |
| &lt;-&gt; | &lt;-&gt; | Returns the "distance" between the arguments, that is one minus the similarity() value |
| &lt;&lt;-&gt; | &lt;&lt;-&gt; | Returns the "distance" between the arguments, that is one minus the word_similarity() value |
| &lt;-&gt;&gt; | &lt;-&gt;&gt; | Commutator of the &lt;&lt;-&gt; operator |
| similarity | similarity | Returns a number that indicates how similar the two strings are |
| wordSimilarity | word_similarity | Returns a number that indicates how similar the first string to the most similar word of the second string |
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.github.tminglei.slickpg
package trgm

import org.scalatest.FunSuite

import scala.concurrent.Await
import scala.concurrent.duration.Duration

/**
* Created by minglei on 6/21/17.
*/
class PgTrgmSupportSuite extends FunSuite {

trait MyPostgresProfile1 extends ExPostgresProfile with PgTrgmSupport {
override val api: API = new API {}

///
trait API extends super.API with PgTrgmImplicits
}
object MyPostgresProfile1 extends MyPostgresProfile1

///
import MyPostgresProfile1.api._

val db = Database.forURL(url = utils.dbUrl, driver = "org.postgresql.Driver")

case class StrBean(id: Long, str: String)

class StringTestTable(tag: Tag) extends Table[StrBean](tag, "trgm_test") {
val id = column[Long]("id")
val str = column[String]("str")

def * = (id, str) <> (StrBean.tupled, StrBean.unapply)
}
val trgmTestTable = TableQuery[StringTestTable]

///
val testRec1 = StrBean(101L, "hello")
val testRec2 = StrBean(102L, "see you")
val testRec3 = StrBean(103L, "how are you")

test("String Lifted support") {
Await.result(db.run(
DBIO.seq(
trgmTestTable.schema create,
trgmTestTable forceInsertAll List(testRec1, testRec2, testRec3)
).andThen(
DBIO.seq(
trgmTestTable.filter(_.id === 101L).map(_.str % "hi").result.head.map {
r => assert(false === r)
},
trgmTestTable.filter(_.id === 101L).map(_.str `<%` "hello").result.head.map {
r => assert(true === r)
},
trgmTestTable.filter(_.id === 101L).map(_.str %> "hello").result.head.map {
r => assert(true === r)
},
trgmTestTable.filter(_.id === 102L).map(_.str <-> "hi").result.head.map {
r => assert(Math.abs(r - 1.0d) < 0.1d)
},
trgmTestTable.filter(_.id === 103L).map(_.str <<-> "hi").result.head.map {
r => assert(r < 1.0d)
},
trgmTestTable.filter(_.id === 102L).map(_.str <->> "hi").result.head.map {
r => assert(Math.abs(r - 1.0d) < 0.1d)
},
trgmTestTable.filter(_.id === 103L).map(_.str.similarity("hi")).result.head.map {
r => assert(r < 1.0d)
},
trgmTestTable.filter(_.id === 103L).map(_.str.wordSimilarity("hi")).result.head.map {
r => assert(r < 1.0d)
}
)
).andFinally(
trgmTestTable.schema drop
)
), Duration.Inf)
}
}

0 comments on commit 3ab5ee8

Please sign in to comment.