Darren
Darren

Reputation: 795

Elasticsearch index array of made up of another class

I have a class which I want to begin indexing into ElasticSearch using the Scala client elastic4s. I have extended DocumentMap to allow me to insert the documents. The simple values like String, Int etc are working but I cannot seem to get a List of another class to map correctly.

The documents look similar to this:

case class AThing(UserName: String, Comment: String, Time: String) 
extends  DocumentMap { 
  override def map: Map[String, Any] = Map(
   "UserName" -> UserName,
   "Comment" -> Comment,
   "Time" -> Time
  )
}

case class ThingsThatHappened(Id: String, Things: Seq[AThing] = Nil) 
extends DocumentMap {
  override def map: Map[String, Any] = Map(
    "Id" -> Id,
    "Things" ->  Things
  )
}

It will map the Id field fine within elasticsearch but then I get a an incorrect value which looks similar to this, when the document is inserted into elasticsearch:

List(AThing(id_for_the_thing,user_name_a,typed_in_comment,2015-03-12))

Obviously this is wrong and I am expecting something a kin to this JSON structure once it has been inserted into elasticsearch, such as:

"events" : [
   {
     "UserName" :"user_name_a", 
     "Comment": "typed_in_comment", 
     "Time": "2015-03-12"
   }
]

Does anyone know a way to map an array of complex types when indexing data using elastic4s?

Upvotes: 0

Views: 969

Answers (2)

sksamuel
sksamuel

Reputation: 16387

Elastic4s or the java client (currently) isn't smart enough to figure out that you have a nested sequence or array, but it would work if it was a nested java map (still a bit rubbish from the Scala point of view).

I think the best thing to do is use the new Indexable typeclass that was added in 1.4.13

So, given

case class AThing(UserName: String, Comment: String, Time: String) 

Then create a type class and bring it into scope

implicit object AThingIndexable extends Indexable[AThing] {
  def json = ... create json here using Jackson or similar which will handle nested sequences properly
}

Then you should be able to do:

client.execute { index into "myIndex/AThings" source aThing }

It's not quite as automatic as using the DocumentMap but gives you more control.

See a unit test here with it in action

Upvotes: 1

user4562504
user4562504

Reputation:

First of all you need to create index in elastic4s. I assume you did this.

 client.execute {
  create index "myIndex" mappings (
    "AThings" as(
      "UserName" typed StringType,
      "Comemnt" typed StringType,
      "Time" typed StringType,
      )
    )
}

if you create this index, then you can put case class into this directly.

val aThings = AThings("username","comment","time")
client.execute {index into "myIndex/AThings" doc aThings}

Upvotes: 0

Related Questions