Skip to content

Commit

Permalink
fix: lucene query some cases not negates closes#71
Browse files Browse the repository at this point in the history
  • Loading branch information
QuadStingray committed Feb 3, 2025
1 parent 76d4bc6 commit 3d3b60a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 28 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.html
//crossScalaVersions := Seq("2.13.16")
crossScalaVersions := Seq("3.6.0", "2.13.16")

scalaVersion := crossScalaVersions.value.last
scalaVersion := crossScalaVersions.value.head

scalacOptions += "-deprecation"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import com.typesafe.scalalogging.LazyLogging
import dev.mongocamp.driver.mongodb._
import dev.mongocamp.driver.mongodb.exception.NotSupportedException
import org.apache.lucene.queryparser.classic.QueryParser
import org.apache.lucene.search._
import org.apache.lucene.search.BooleanClause.Occur
import org.apache.lucene.search._
import org.joda.time.DateTime
import org.mongodb.scala.bson.conversions.Bson

Expand Down Expand Up @@ -35,7 +35,7 @@ object LuceneQueryConverter extends LazyLogging {
private def getMongoDbSearchMap(query: Query, negated: Boolean, searchWithValueAndString: Boolean): Map[String, Any] = {
val searchMapResponse = mutable.Map[String, Any]()
query match {
case booleanQuery: BooleanQuery => appendBooleanQueryToSearchMap(searchMapResponse, booleanQuery, searchWithValueAndString)
case booleanQuery: BooleanQuery => appendBooleanQueryToSearchMap(searchMapResponse, booleanQuery, searchWithValueAndString, negated)
case termRangeQuery: TermRangeQuery => appendTermRangeQueryToSearchMap(negated, searchMapResponse, termRangeQuery, searchWithValueAndString)
case termQuery: TermQuery => appendTermQueryToSearchMap(negated, searchMapResponse, termQuery, searchWithValueAndString)
case query: PrefixQuery => appendPrefixQueryToSearchMap(negated, searchMapResponse, query)
Expand All @@ -53,14 +53,17 @@ object LuceneQueryConverter extends LazyLogging {
private def appendBooleanQueryToSearchMap(
searchMapResponse: mutable.Map[String, Any],
booleanQuery: BooleanQuery,
searchWithValueAndString: Boolean
searchWithValueAndString: Boolean,
negate: Boolean
): Unit = {
val subQueries = booleanQuery.clauses().asScala
val listOfAnd = ArrayBuffer[Map[String, Any]]()
val listOfOr = ArrayBuffer[Map[String, Any]]()
val listOfNOr = ArrayBuffer[Map[String, Any]]()
var nextTypeAnd = true
subQueries.foreach(c => {
val queryMap = getMongoDbSearchMap(c.query(), c.isProhibited, searchWithValueAndString)
val negateSubquery = (c.occur() == Occur.MUST_NOT)
val queryMap = getMongoDbSearchMap(c.query(), negateSubquery, searchWithValueAndString)
var thisTypeAnd = true

if (c.occur == Occur.MUST) {
Expand All @@ -87,10 +90,18 @@ object LuceneQueryConverter extends LazyLogging {
})

if (listOfAnd.nonEmpty) {
searchMapResponse.put("$and", listOfAnd.toList)
if (negate) {
searchMapResponse.put("$nor", listOfAnd.toList)
} else {
searchMapResponse.put("$and", listOfAnd.toList)
}
}
if (listOfOr.nonEmpty) {
searchMapResponse.put("$or", listOfOr.toList)
if (negate) {
searchMapResponse.put("$nor", listOfOr.toList)
} else {
searchMapResponse.put("$or", listOfOr.toList)
}
}
}

Expand Down Expand Up @@ -186,15 +197,17 @@ object LuceneQueryConverter extends LazyLogging {
}

private def appendPhraseQueryToSearchMap(negated: Boolean, searchMapResponse: mutable.Map[String, Any], query: PhraseQuery): Unit = {
val listOfSearches = query.getTerms.map(term => {
val convertedValue = checkAndConvertValue(term.text())
if (convertedValue.isInstanceOf[String]) {
Map(term.field() -> generateRegexQuery(s"(.*?)$convertedValue(.*?)", "i"))
}
else {
Map(term.field() -> Map("$eq" -> convertedValue))
}
}).toList
val listOfSearches = query.getTerms
.map(term => {
val convertedValue = checkAndConvertValue(term.text())
if (convertedValue.isInstanceOf[String]) {
Map(term.field() -> generateRegexQuery(s"(.*?)$convertedValue(.*?)", "i"))
}
else {
Map(term.field() -> Map("$eq" -> convertedValue))
}
})
.toList
if (negated) {
searchMapResponse.put("$nor", listOfSearches)
}
Expand Down Expand Up @@ -228,18 +241,20 @@ object LuceneQueryConverter extends LazyLogging {
val convertedValue: Option[Any] =
(List() ++ checkOrReturn(() => s.toDouble) ++ checkOrReturn(() => s.toLong) ++ checkOrReturn(() => s.toBoolean)).headOption
val response = convertedValue.getOrElse({
val parsedOptions: List[Date] = Try(new DateTime(s).toDate).toOption.toList ++ datePatters.flatMap(pattern => {
try {
val formatter = new SimpleDateFormat(pattern)
val r = Option(formatter.parse(s))
logger.info(s"parsed date $s with pattern $pattern to $r")
r
}
catch {
case e: Exception =>
None
}
}).distinct
val parsedOptions: List[Date] = Try(new DateTime(s).toDate).toOption.toList ++ datePatters
.flatMap(pattern => {
try {
val formatter = new SimpleDateFormat(pattern)
val r = Option(formatter.parse(s))
logger.info(s"parsed date $s with pattern $pattern to $r")
r
}
catch {
case e: Exception =>
None
}
})
.distinct
parsedOptions.headOption.getOrElse(s)
})
response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dev.mongocamp.driver.mongodb.lucene
import dev.mongocamp.driver.mongodb._
import dev.mongocamp.driver.mongodb.dao.BasePersonSuite
import dev.mongocamp.driver.mongodb.test.TestDatabase._
import org.mongodb.scala.Document

import java.util.TimeZone

Expand Down Expand Up @@ -135,4 +136,13 @@ class LuceneSearchSuite extends BasePersonSuite {
assertEquals(search.size, 199)
}

test("negate query with values in braces") {
val luceneQuery = LuceneQueryConverter.parse("NOT fieldName:('value1' OR 'value2' OR 'value2')", "ube")
val document = LuceneQueryConverter.toDocument(luceneQuery)
assertEquals("{\"$and\": [{\"$nor\": [{\"fieldName\": {\"$eq\": \"value1\"}}, {\"fieldName\": {\"$eq\": \"value2\"}}, {\"fieldName\": {\"$eq\": \"value2\"}}]}]}", document.asInstanceOf[Document].toJson())
val luceneQuery2 = LuceneQueryConverter.parse("NOT fieldName:('value1' AND 'value2' AND 'value2')", "ube")
val document2 = LuceneQueryConverter.toDocument(luceneQuery2)
assertEquals("{\"$and\": [{\"$nor\": [{\"fieldName\": {\"$eq\": \"value1\"}}, {\"fieldName\": {\"$eq\": \"value2\"}}, {\"fieldName\": {\"$eq\": \"value2\"}}]}]}", document2.asInstanceOf[Document].toJson())
}

}

0 comments on commit 3d3b60a

Please sign in to comment.