Skip to content

Commit

Permalink
Support synonyms api (#3073)
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippus authored May 31, 2024
1 parent 114361c commit f9ad561
Show file tree
Hide file tree
Showing 26 changed files with 406 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.sksamuel.elastic4s

import com.sksamuel.elastic4s.api.{AggregationApi, AliasesApi, AnalyzeApi, AnalyzerApi, BulkApi, CatsApi, ClearRolesCacheApi, ClusterApi, CollapseApi, CountApi, CreateIndexApi, CreateRoleApi, CreateUserApi, DeleteApi, DeleteIndexApi, DeleteRoleApi, DeleteUserApi, ExistsApi, ExplainApi, ForceMergeApi, GetApi, HighlightApi, IndexAdminApi, IndexApi, IndexRecoveryApi, IndexTemplateApi, IngestApi, KnnApi, LocksApi, MappingApi, NodesApi, NormalizerApi, PipelineAggregationApi, PitApi, QueryApi, ReindexApi, ReloadSearchAnalyzersApi, RoleApi, ScoreApi, ScriptApi, ScrollApi, SearchApi, SearchTemplateApi, SettingsApi, SnapshotApi, SortApi, StoredScriptApi, SuggestionApi, TaskApi, TermVectorApi, TokenFilterApi, TokenizerApi, TypesApi, UpdateApi, UserAdminApi, UserApi, ValidateApi}
import com.sksamuel.elastic4s.api.{AggregationApi, AliasesApi, AnalyzeApi, AnalyzerApi, BulkApi, CatsApi, ClearRolesCacheApi, ClusterApi, CollapseApi, CountApi, CreateIndexApi, CreateRoleApi, CreateUserApi, DeleteApi, DeleteIndexApi, DeleteRoleApi, DeleteUserApi, ExistsApi, ExplainApi, ForceMergeApi, GetApi, HighlightApi, IndexAdminApi, IndexApi, IndexRecoveryApi, IndexTemplateApi, IngestApi, KnnApi, LocksApi, MappingApi, NodesApi, NormalizerApi, PipelineAggregationApi, PitApi, QueryApi, ReindexApi, ReloadSearchAnalyzersApi, RoleApi, ScoreApi, ScriptApi, ScrollApi, SearchApi, SearchTemplateApi, SettingsApi, SnapshotApi, SortApi, StoredScriptApi, SuggestionApi, SynonymsApi, TaskApi, TermVectorApi, TokenFilterApi, TokenizerApi, TypesApi, UpdateApi, UserAdminApi, UserApi, ValidateApi}
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}

Expand Down Expand Up @@ -52,6 +52,7 @@ trait ElasticApi
with SortApi
with StoredScriptApi
with SuggestionApi
with SynonymsApi
with TaskApi
with TermVectorApi
with TokenizerApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.sksamuel.elastic4s.handlers.security.roles.{RoleAdminHandlers, RoleHa
import com.sksamuel.elastic4s.handlers.security.users.{UserAdminHandlers, UserHandlers}
import com.sksamuel.elastic4s.handlers.settings.SettingsHandlers
import com.sksamuel.elastic4s.handlers.snapshot.SnapshotHandlers
import com.sksamuel.elastic4s.handlers.synonyms.SynonymsHandlers
import com.sksamuel.elastic4s.handlers.task.TaskHandlers
import com.sksamuel.elastic4s.handlers.termvectors.TermVectorHandlers
import com.sksamuel.elastic4s.handlers.update.UpdateHandlers
Expand Down Expand Up @@ -61,6 +62,7 @@ with SearchScrollHandlers
with SettingsHandlers
with SnapshotHandlers
with StoredScriptHandlers
with SynonymsHandlers
with UpdateHandlers
with TaskHandlers
with TermVectorHandlers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.sksamuel.elastic4s.api

import com.sksamuel.elastic4s.requests.synonyms.{DeleteSynonymRuleRequest, DeleteSynonymsSetRequest, GetSynonymsSetRequest, ListSynonymsSetRequest, SynonymRule, CreateOrUpdateSynonymRuleRequest, CreateOrUpdateSynonymsSetRequest}

trait SynonymsApi {
def createOrUpdateSynonymsSet(synonymsSet: String, synonymRules: Seq[SynonymRule]): CreateOrUpdateSynonymsSetRequest =
CreateOrUpdateSynonymsSetRequest(synonymsSet, synonymRules)

def getSynonymsSet(synonymsSet: String, from: Option[Int] = None, size: Option[Int] = None): GetSynonymsSetRequest =
GetSynonymsSetRequest(synonymsSet, from, size)

def listSynonymsSet(from: Option[Int] = None, size: Option[Int] = None): ListSynonymsSetRequest =
ListSynonymsSetRequest(from, size)

def deleteSynonymsSet(synonymsSet: String): DeleteSynonymsSetRequest = DeleteSynonymsSetRequest(synonymsSet)

def upsertSynonymRule(synonymsSet: String, synonymRule: String, synonyms: String): CreateOrUpdateSynonymRuleRequest =
CreateOrUpdateSynonymRuleRequest(synonymsSet, synonymRule, synonyms)

def deleteSynonymRule(synonymsSet: String, synonymRule: String): DeleteSynonymRuleRequest =
DeleteSynonymRuleRequest(synonymsSet, synonymRule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ case class SynonymTokenFilter(override val name: String,
expand: Option[Boolean] = None,
@deprecated tokenizer: Option[String] = None,
updateable: Option[Boolean] = None,
lenient: Option[Boolean] = None) extends TokenFilter {
require(path.isDefined || synonyms.nonEmpty, "synonym requires either `synonyms` or `synonyms_path` to be configured")
lenient: Option[Boolean] = None,
synonymsSet: Option[String] = None) extends TokenFilter {
require(synonymsSet.isDefined || path.isDefined || synonyms.nonEmpty, "synonym requires either `synonyms_set`, `synonyms_path` or `synonyms` to be configured")

override def build: XContentBuilder = {
val b = XContentFactory.jsonBuilder()
b.field("type", "synonym")
if (synonyms.isEmpty) path.foreach(b.field("synonyms_path", _))
if (synonyms.nonEmpty) b.array("synonyms", synonyms.toArray)
if (synonymsSet.nonEmpty) synonymsSet.foreach(b.field("synonyms_set", _))
else if (path.nonEmpty) path.foreach(b.field("synonyms_path", _))
else b.array("synonyms", synonyms.toArray)
format.foreach(b.field("format", _))
ignoreCase.foreach(b.field("ignore_case", _))
updateable.foreach(b.field("updateable", _))
Expand Down Expand Up @@ -106,15 +108,17 @@ case class SynonymGraphTokenFilter(override val name: String,
expand: Option[Boolean] = None,
@deprecated tokenizer: Option[String] = None,
updateable: Option[Boolean] = None,
lenient: Option[Boolean] = None) extends TokenFilter {
lenient: Option[Boolean] = None,
synonymsSet: Option[String] = None) extends TokenFilter {

require(path.isDefined || synonyms.nonEmpty, "synonym_graph requires either `synonyms` or `synonyms_path` to be configured")
require(synonymsSet.isDefined || path.isDefined || synonyms.nonEmpty, "synonym requires either `synonyms_set`, `synonyms_path` or `synonyms` to be configured")

override def build: XContentBuilder = {
val b = XContentFactory.jsonBuilder()
b.field("type", "synonym_graph")
if (synonyms.isEmpty) path.foreach(b.field("synonyms_path", _))
if (synonyms.nonEmpty) b.array("synonyms", synonyms.toArray)
if (synonymsSet.nonEmpty) synonymsSet.foreach(b.field("synonyms_set", _))
else if (path.nonEmpty) path.foreach(b.field("synonyms_path", _))
else b.array("synonyms", synonyms.toArray)
format.foreach(b.field("format", _))
ignoreCase.foreach(b.field("ignore_case", _))
updateable.foreach(b.field("updateable", _))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class CreateOrUpdateSynonymRuleRequest(synonymsSet: String, synonymRule: String, synonyms: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class CreateOrUpdateSynonymsSetRequest(synonymsSet: String, synonymRules: Seq[SynonymRule])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class DeleteSynonymRuleRequest(synonymsSet: String, synonymRule: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

import com.fasterxml.jackson.annotation.JsonProperty

case class DeleteSynonymRuleResponse(result: String,
@JsonProperty("reload_analyzers_details") reloadAnalyzersDetails: ReloadAnalyzersDetails)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class DeleteSynonymsSetRequest(synonymsSet: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class GetSynonymRuleRequest(synonymsSet: String, synonymRule: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class GetSynonymRuleResponse(id: String, synonyms: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

case class GetSynonymsSetRequest(synonymsSet: String, from: Option[Int] = None, size: Option[Int] = None) {
def from(from: Int): GetSynonymsSetRequest = copy(from = Some(from))
def size(size: Int): GetSynonymsSetRequest = copy(size = Some(size))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class GetSynonymsSetResponse(count: Int, synonymsSet: Seq[SynonymRule])
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

case class ListSynonymsSetRequest(from: Option[Int], size: Option[Int]) {
def from(from: Int): ListSynonymsSetRequest = copy(from = Some(from))
def size(size: Int): ListSynonymsSetRequest = copy(size = Some(size))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

import com.fasterxml.jackson.annotation.JsonProperty

case class ListSynonymsResult(@JsonProperty("synonyms_set") synonymsSet: String, count: Int)
case class ListSynonymsSetResponse(count: Int, results: Seq[ListSynonymsResult])
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sksamuel.elastic4s.requests.synonyms

import com.fasterxml.jackson.annotation.JsonProperty
import com.sksamuel.elastic4s.requests.common.Shards

case class ReloadDetails(index: String, @JsonProperty("reloaded_analyzers") reloadedAnalyzers: Seq[String],
@JsonProperty("reloaded_node_ids") reloadedNodeIds: Seq[String])
case class ReloadAnalyzersDetails(@JsonProperty("reload_details") reloadDetails: Seq[ReloadDetails],
@JsonProperty("_shards") shards: Option[Shards])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.sksamuel.elastic4s.requests.synonyms

case class SynonymRule(synonyms: String, id: Option[String] = None)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

import com.fasterxml.jackson.annotation.JsonProperty

case class UpdateSynonymRuleResponse(result: String,
@JsonProperty("reload_analyzers_details") reloadAnalyzersDetails: ReloadAnalyzersDetails)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sksamuel.elastic4s.requests.synonyms

import com.fasterxml.jackson.annotation.JsonProperty

case class UpdateSynonymsSetResponse(result: String,
@JsonProperty("reload_analyzers_details") reloadAnalyzersDetails: ReloadAnalyzersDetails)
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.sksamuel.elastic4s.handlers.synonyms

import com.sksamuel.elastic4s.handlers.ElasticErrorParser
import com.sksamuel.elastic4s.requests.synonyms.{DeleteSynonymRuleRequest, DeleteSynonymRuleResponse, DeleteSynonymsSetRequest, GetSynonymRuleRequest, GetSynonymRuleResponse, GetSynonymsSetRequest, GetSynonymsSetResponse, ListSynonymsSetRequest, ListSynonymsSetResponse, CreateOrUpdateSynonymRuleRequest, UpdateSynonymRuleResponse, CreateOrUpdateSynonymsSetRequest, UpdateSynonymsSetResponse}
import com.sksamuel.elastic4s.{ElasticError, ElasticRequest, Handler, HttpEntity, HttpResponse, ResponseHandler}

trait SynonymsHandlers {
implicit object UpdateSynonymsSetHandler extends Handler[CreateOrUpdateSynonymsSetRequest, UpdateSynonymsSetResponse] {

override def responseHandler: ResponseHandler[UpdateSynonymsSetResponse] = new ResponseHandler[UpdateSynonymsSetResponse] {
override def handle(response: HttpResponse): Either[ElasticError, UpdateSynonymsSetResponse] = {
response.statusCode match {
case 200 | 201 => Right(ResponseHandler.fromResponse[UpdateSynonymsSetResponse](response))
case 400 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}
}

override def build(request: CreateOrUpdateSynonymsSetRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}"

val body = UpdateSynonymsBodyFn(request).string
val entity = HttpEntity(body, "application/json")

ElasticRequest("PUT", endpoint, entity)
}
}

implicit object GetSynonymsSetHandler extends Handler[GetSynonymsSetRequest, GetSynonymsSetResponse] {
override def responseHandler: ResponseHandler[GetSynonymsSetResponse] = new ResponseHandler[GetSynonymsSetResponse] {
override def handle(response: HttpResponse): Either[ElasticError, GetSynonymsSetResponse] =
response.statusCode match {
case 200 => Right(ResponseHandler.fromResponse[GetSynonymsSetResponse](response))
case 400 | 404 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}

override def build(request: GetSynonymsSetRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}"
val params = scala.collection.mutable.Map.empty[String, String]
request.from.foreach(from => params.put("from", from.toString))
request.size.foreach(size => params.put("size", size.toString))
ElasticRequest("GET", endpoint, params.toMap)
}
}

implicit object ListSynonymsSetHandler extends Handler[ListSynonymsSetRequest, ListSynonymsSetResponse] {
override def responseHandler: ResponseHandler[ListSynonymsSetResponse] = new ResponseHandler[ListSynonymsSetResponse] {
override def handle(response: HttpResponse): Either[ElasticError, ListSynonymsSetResponse] =
response.statusCode match {
case 200 => Right(ResponseHandler.fromResponse[ListSynonymsSetResponse](response))
case _ => sys.error("Invalid response")
}
}

override def build(request: ListSynonymsSetRequest): ElasticRequest = {
val endpoint = "_synonyms"
val params = scala.collection.mutable.Map.empty[String, String]
request.from.foreach(from => params.put("from", from.toString))
request.size.foreach(size => params.put("size", size.toString))
ElasticRequest("GET", endpoint, params.toMap)
}
}

implicit object DeleteSynonymsSetHandler extends Handler[DeleteSynonymsSetRequest, Unit] {
override def responseHandler: ResponseHandler[Unit] = new ResponseHandler[Unit] {
override def handle(response: HttpResponse): Either[ElasticError, Unit] =
response.statusCode match {
case 200 => Right(())
case 400 | 404 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}

override def build(request: DeleteSynonymsSetRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}"
ElasticRequest("DELETE", endpoint)
}
}

implicit object UpdateSynonymRuleHandler extends Handler[CreateOrUpdateSynonymRuleRequest, UpdateSynonymRuleResponse] {
override def responseHandler: ResponseHandler[UpdateSynonymRuleResponse] = new ResponseHandler[UpdateSynonymRuleResponse] {
override def handle(response: HttpResponse): Either[ElasticError, UpdateSynonymRuleResponse] =
response.statusCode match {
case 200 | 201 => Right(ResponseHandler.fromResponse[UpdateSynonymRuleResponse](response))
case 400 | 404 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}

override def build(request: CreateOrUpdateSynonymRuleRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}/${request.synonymRule}"
println(endpoint)
val body = UpdateSynonymRuleBodyFn(request).string
val entity = HttpEntity(body, "application/json")
ElasticRequest("PUT", endpoint, entity)
}
}

implicit object GetSynonymRuleHandler extends Handler[GetSynonymRuleRequest, GetSynonymRuleResponse] {
override def responseHandler: ResponseHandler[GetSynonymRuleResponse] = new ResponseHandler[GetSynonymRuleResponse] {
override def handle(response: HttpResponse): Either[ElasticError, GetSynonymRuleResponse] =
response.statusCode match {
case 200 => Right(ResponseHandler.fromResponse[GetSynonymRuleResponse](response))
case 404 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}

override def build(request: GetSynonymRuleRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}/${request.synonymRule}"
ElasticRequest("GET", endpoint)
}
}

implicit object DeleteSynonymRuleHandler extends Handler[DeleteSynonymRuleRequest, DeleteSynonymRuleResponse] {
override def responseHandler: ResponseHandler[DeleteSynonymRuleResponse] = new ResponseHandler[DeleteSynonymRuleResponse] {
override def handle(response: HttpResponse): Either[ElasticError, DeleteSynonymRuleResponse] =
response.statusCode match {
case 200 => Right(ResponseHandler.fromResponse[DeleteSynonymRuleResponse](response))
case 404 => Left(ElasticErrorParser.parse(response))
case _ => sys.error("Invalid response")
}
}

override def build(request: DeleteSynonymRuleRequest): ElasticRequest = {
val endpoint = s"_synonyms/${request.synonymsSet}/${request.synonymRule}"
ElasticRequest("DELETE", endpoint)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sksamuel.elastic4s.handlers.synonyms

import com.sksamuel.elastic4s.json.{XContentBuilder, XContentFactory}
import com.sksamuel.elastic4s.requests.synonyms.CreateOrUpdateSynonymRuleRequest

object UpdateSynonymRuleBodyFn {
def apply(request: CreateOrUpdateSynonymRuleRequest): XContentBuilder = {
val builder = XContentFactory.jsonBuilder()
builder.field("synonyms", request.synonyms)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sksamuel.elastic4s.handlers.synonyms

import com.sksamuel.elastic4s.json.{XContentBuilder, XContentFactory}
import com.sksamuel.elastic4s.requests.synonyms.CreateOrUpdateSynonymsSetRequest

object UpdateSynonymsBodyFn {
def apply(request: CreateOrUpdateSynonymsSetRequest): XContentBuilder = {
val builder = XContentFactory.jsonBuilder()
builder.startArray("synonyms_set")
request.synonymRules.foreach { rule =>
builder.startObject()
rule.id.foreach(builder.field("id", _))
builder.field("synonyms", rule.synonyms)
builder.endObject()
}
builder.endArray()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"type": "synonym_graph",
"synonyms_set": "my_synonyms_set",
"format": "solr",
"ignore_case": true,
"updateable": true,
"expand": true,
"lenient": true,
"tokenizer": "whitespace"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"type": "synonym",
"synonyms_set": "my_synonyms_set",
"format": "solr",
"ignore_case": true,
"updateable": true,
"expand": true,
"lenient": true,
"tokenizer": "whitespace"
}
Loading

0 comments on commit f9ad561

Please sign in to comment.