Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #37 from holdenk/master

Add support for raw queries and metall any to ES integration
  • Loading branch information...
commit af614b3d48dff2ac6ef492027b2aecf551122475 2 parents a71fd7d + 1a4feb7
@adamalix adamalix authored
View
2  build.sbt
@@ -1,6 +1,6 @@
name := "slashem"
-version := "0.9.15a"
+version := "0.10.2"
organization := "com.foursquare"
View
48 src/main/scala/com/foursquare/slashem/Ast.scala
@@ -372,9 +372,15 @@ object Ast {
*
* Represents a contiguous series of words to be matched in that order.
*/
- case class Phrase[T](query: T, escaped: Boolean = true) extends Query[T] {
+ case class Phrase[T](query: T, escapeQuery: Boolean = true) extends Query[T] {
/** @inheritdoc */
- def extend(): String = {'"' + escape(query.toString) + '"'}
+ def extend(): String = {
+ if (escapeQuery) {
+ '"' + escape(query.toString) + '"'
+ } else {
+ '"' + query.toString + '"'
+ }
+ }
/** @inheritdoc */
def elasticExtend(qf: List[WeightedField], pf: List[PhraseWeightedField], mm: Option[String]): ElasticQueryBuilder = {
val q = EQueryBuilders.queryString(this.extend())
@@ -387,9 +393,15 @@ object Ast {
* A Phrase Prefix.
* @see Phrase
*/
- case class PhrasePrefix[T](query: T, escaped: Boolean = true) extends Query[T] {
+ case class PhrasePrefix[T](query: T, escapeQuery: Boolean = true) extends Query[T] {
/** @inheritdoc */
- def extend(): String = {'"' + escape(query.toString) + '*' + '"'}
+ def extend(): String = {
+ if (escapeQuery) {
+ '"' + escape(query.toString) + '*' + '"'
+ } else {
+ '"' + query.toString + '*' + '"'
+ }
+ }
/** @inheritdoc */
def elasticExtend(qf: List[WeightedField], pf: List[PhraseWeightedField], mm: Option[String]): ElasticQueryBuilder = {
val q = EQueryBuilders.disMaxQuery()
@@ -412,13 +424,11 @@ object Ast {
*
* By default, elasticFilter() will always be cached!
*/
- case class Term[T](query: Iterable[T], escaped: Boolean = true, cached: Boolean = true) extends Query[T] {
- // hack for single term queries
- def this(query: T) = this(List(query))
+ case class Term[T](query: Iterable[T], escapeQuery: Boolean = true, cached: Boolean = true) extends Query[T] {
/** @inheritdoc */
//def extend() = throw new UnimplementedException("Slashem does not support Term queries Solr")
def extend(): String = {
- escaped match {
+ escapeQuery match {
// hack to fix wrapping the queries in a List()
case true => {
val queries = query.map(q => {'"' + escape(q.toString) + '"'})
@@ -472,9 +482,15 @@ object Ast {
/**
* A class representing a Bag of words style query
*/
- case class BagOfWords[T](query: T) extends Query[T] {
+ case class BagOfWords[T](query: T, escapeQuery: Boolean = true) extends Query[T] {
/** @inheritdoc */
- def extend(): String = escape(query.toString)
+ def extend(): String = {
+ if (escapeQuery) {
+ escape(query.toString)
+ } else {
+ query.toString
+ }
+ }
/**
* @inheritdoc
@@ -576,9 +592,15 @@ object Ast {
/** @inheritdoc */
//Is there a better way to do this?
def elasticExtend(qf: List[WeightedField], pf: List[PhraseWeightedField], mm: Option[String]): ElasticQueryBuilder = {
- val q = EQueryBuilders.queryString(this.extend())
- qf.map(f => q.field(f.fieldName, f.weight.toFloat))
- q
+ //So we have a special case, if we are searching against the
+ //"_all" field we can just construct a ninja query
+ if (qf.map(_.fieldName).contains("_all")) {
+ EQueryBuilders.matchAllQuery()
+ } else {
+ val q = EQueryBuilders.queryString(this.extend())
+ qf.map(f => q.field(f.fieldName, f.weight.toFloat))
+ q
+ }
}
}
View
26 src/main/scala/com/foursquare/slashem/Schema.scala
@@ -804,19 +804,18 @@ trait SlashemField[V, M <: Record[M]] extends OwnedField[M] {
// Override this value to produce unanalyzed queries!
val unanalyzed = false
- def produceQuery(v: V): Query[V] = {
+ def produceQuery(v: V, escapeQuery: Boolean = true): Query[V] = {
unanalyzed match {
- // use new to use Term's additional non-default constructor
- case true => new Term(v)
- case false => Phrase(v)
+ case true => Term(List(v),escapeQuery)
+ case false => Phrase(v,escapeQuery)
}
}
- def produceGroupedQuery(v: Iterable[V]): Query[V] = {
+ def produceGroupedQuery(v: Iterable[V], escapeQuery: Boolean = true): Query[V] = {
unanalyzed match {
// we don't want to groupWithOr and instead take advantage of "terms" queries
- case true => Term(v)
- case false => groupWithOr(v.map({x: V => produceQuery(x)}))
+ case true => Term(v, escapeQuery)
+ case false => groupWithOr(v.map({x: V => produceQuery(x,escapeQuery)}))
}
}
@@ -826,11 +825,22 @@ trait SlashemField[V, M <: Record[M]] extends OwnedField[M] {
def eqs(v: V, b: Float) = Clause[V](self.queryName, Boost(Group(produceQuery(v)),b))
def neqs(v: V, b:Float) = Clause[V](self.queryName, Boost(produceQuery(v),b),false)
-
//This allows for bag of words style matching.
def contains(v: V) = Clause[V](self.queryName, Group(BagOfWords(v)))
def contains(v: V, b: Float) = Clause[V](self.queryName, Boost(Group(BagOfWords(v)),b))
+ //Search with explicit escaping. By normal we escape, set e to false to disable
+ //Note eqs and neqs results in phrase queries!
+ def eqs(v: V, e: Boolean) = Clause[V](self.queryName, Group(produceQuery(v,e)))
+ def neqs(v: V, e: Boolean) = Clause[V](self.queryName, produceQuery(v,e),false)
+ //With a boost
+ def eqs(v: V, b: Float, e: Boolean) = Clause[V](self.queryName, Boost(Group(produceQuery(v,e)),b))
+ def neqs(v: V, b:Float, e: Boolean) = Clause[V](self.queryName, Boost(produceQuery(v,e),b),false)
+ //This allows for bag of words style matching.
+ def contains(v: V, e: Boolean) = Clause[V](self.queryName, Group(BagOfWords(v,e)))
+ def contains(v: V, b: Float, e: Boolean) = Clause[V](self.queryName, Boost(Group(BagOfWords(v,e)),b))
+
+
def in(v: Iterable[V]) = Clause[V](self.queryName, produceGroupedQuery(v))
def nin(v: Iterable[V]) = Clause[V](self.queryName, produceGroupedQuery(v),false)
View
6 src/test/scala/com/foursquare/slashem/ElasticQueryTest.scala
@@ -70,6 +70,12 @@ class ElasticQueryTest extends SpecsMatchers with ScalaCheckMatchers {
val r = ESimpleGeoPanda where (_.name contains "lolerskates") scoreBoostField(_.pos recipSqeGeoDistance(geoLat, geoLong, 1, 5000, 1)) fetch(Duration(0,TimeUnit.MILLISECONDS))
}
+ @Test
+ def testMatchAll {
+ val r = ESimpleGeoPanda where (_.metall any) fetch()
+ Assert.assertEquals(4,r.response.results.length)
+ }
+
@Test
def simpleOrderTest {
View
4 src/test/scala/com/foursquare/slashem/ElasticTest.scala
@@ -29,6 +29,10 @@ object ESimpleGeoPanda extends ESimpleGeoPanda with ElasticMeta[ESimpleGeoPanda]
}
class ESimpleGeoPanda extends ElasticSchema[ESimpleGeoPanda] {
def meta = ESimpleGeoPanda
+ //We use "_all" in ES rather than "*" as in Solr to query all fields
+ object metall extends SlashemStringField(this) {
+ override def name="_all"
+ }
object default extends SlashemDefaultStringField(this)
object id extends SlashemObjectIdField(this)
object name extends SlashemStringField(this)
View
10 src/test/scala/com/foursquare/slashem/QueryTest.scala
@@ -54,6 +54,16 @@ class QueryTest extends SpecsMatchers with ScalaCheckMatchers {
}
@Test
+ def testProduceCorrectSimpleUnEscapedQueryString {
+ val q = SUserTest where (_.fullname contains("jon*",false))
+ val qp = q.meta.queryParams(q).toList
+ Assert.assertEquals(qp.sortWith(_._1 > _._1),List("q" -> "fullname:(jon*)",
+ "start" -> "0",
+ "rows" -> "10").sortWith(_._1 > _._1))
+ }
+
+
+ @Test
def testProduceCorrectListfieldQueryStrings {
val q = SVenueTest where (_.commentList in List("hi", "there"))
val q2 = SVenueTest where (_.commentList nin List("hi", "there"))
Please sign in to comment.
Something went wrong with that request. Please try again.