Getting Started

To get started you will need to add a dependency to either

depending on which client you intend you use(or both).

The basic usage is that you create an instance of a client and then invoke the execute method with the requests you want to perform. The execute method is asynchronous and will return a standard Scala Future[T] where T is the response type appropriate for your request type. For example a search request will return a response of type SearchResponse which contains the results of the search.

To create an instance of the HTTP client, use the HttpClient companion object methods. To create an instance of the TCP client, use the TcpClient companion object methods. Requests are the same for either client, but response classes may vary slightly as the HTTP response classes model the returned JSON whereas the TCP response classes wrap the Java client classes.

Requests, such as inserting a document, searching, creating an index, and so on, are created using the DSL syntax that is similar in style to SQL queries. For example to create a search request, you would do:

search("index" / "type") query "findthistext"`

The DSL methods are located in the ElasticDsl trait which needs to be imported or extended. Although the syntax is identical whether you use the HTTP or TCP client, you must import the appropriate trait (com.sksamuel.elastic4s.ElasticDSL for TCP or com.sksamuel.elastic4s.http.ElasticDSL for HTTP) depending on which client you are using.

One final import is required if you are using the HTTP client. The API needs a way to unmarshall the JSON response from the elastic server into the strongly typed case classes used by the API. Rather than bringing in a JSON library of our choosing and potentially causing dependency issues (or simply bloat), the client expects an implicit JsonFormat implementation.

Elastic4s provides several out of the box (or you can roll your own) JSON serializers and deserializers. The provided implementations are

For example, to use the jackson implementation, add the module to your build and then add this import:

SBT Setup

// major.minor are in sync with the elasticsearch releases
val elastic4sVersion = "x.x.x"
libraryDependencies ++= Seq(
  "com.sksamuel.elastic4s" %% "elastic4s-core" % elastic4sVersion,
  // for the tcp client
  "com.sksamuel.elastic4s" %% "elastic4s-tcp" % elastic4sVersion,

  // for the http client
  "com.sksamuel.elastic4s" %% "elastic4s-http" % elastic4sVersion,

  // if you want to use reactive streams
  "com.sksamuel.elastic4s" %% "elastic4s-streams" % elastic4sVersion,

  // a json library
  "com.sksamuel.elastic4s" %% "elastic4s-jackson" % elastic4sVersion,
  "com.sksamuel.elastic4s" %% "elastic4s-play-json" % elastic4sVersion,
  "com.sksamuel.elastic4s" %% "elastic4s-spray-json" % elastic4sVersion,
  "com.sksamuel.elastic4s" %% "elastic4s-circe" % elastic4sVersion,
  "com.sksamuel.elastic4s" %% "elastic4s-json4s" % elastic4sVersion,

  // testing
  "com.sksamuel.elastic4s" %% "elastic4s-testkit" % elastic4sVersion % "test",
  "com.sksamuel.elastic4s" %% "elastic4s-embedded" % elastic4sVersion % "test"
)

Your first request

```tut:invisible import java.io.IOException import java.nio.file._ import java.nio.file.attribute.BasicFileAttributes

// setup a home directory val clusterName: String = "getting-started-with-elastic4s" val homePath: Path = Files.createTempDirectory(clusterName)


All examples use a local node for demonstration purpose. You can start an embedded node with

```tut:silent
import com.sksamuel.elastic4s.embedded.LocalNode

import scala.concurrent.Await
import scala.concurrent.duration._

val localNode = LocalNode(clusterName, homePath.toAbsolutePath.toString)
val client = localNode.elastic4sclient()

You can import the DSL and a default execution context with and execute a simple cluster state request with

```tut:book import com.sksamuel.elastic4s.ElasticDsl._

// await is a helper method to make this operation synchronous instead of async // You would normally avoid doing this in a real program as it will block your thread val response = client.execute { clusterState() }.await

response.getClusterName().value

localNode.close()


## Example Application

An example is worth 1000 characters so here is a quick example of how to connect to a node with a client, create and
index and index a one field document. Then we will search for that document using a simple text query.

For this example we will use the `elastic4s-circe` json serializer.


```tut:silent
import com.sksamuel.elastic4s.{ElasticsearchClientUri, TcpClient}
import com.sksamuel.elastic4s.ElasticDsl._
import com.sksamuel.elastic4s.searches.RichSearchResponse
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy
import org.elasticsearch.common.settings.Settings

// circe
import com.sksamuel.elastic4s.circe._
import io.circe.generic.auto._ 

case class Artist(name: String)

object ArtistIndex extends App {

    // spawn an embedded node
    val localNode = LocalNode(clusterName, homePath.toAbsolutePath.toString)
    val client = localNode.elastic4sclient()

  // This creates a TcpClient. We don't actually use it in our little demo,
  // because elasticsearch 5.x dropped embedded node support and we use the
  // elastic4s embedded node that don't suppot connecting via tcp
  def tcpClient(): TcpClient = TcpClient.transport(
    Settings.builder().put("cluster.name", clusterName).build(),
    ElasticsearchClientUri(s"elasticsearch://${localNode.ipAndPort}")
  )

  // await is a helper method to make this operation synchronous instead of async
  // You would normally avoid doing this in a real program as it will block your thread
  client.execute {
    createIndex("bands").mappings(
       mapping("artist") as(
          stringField("name")
       )
    )
  }.await

  client.execute { 
    indexInto("bands" / "artists") doc Artist("Coldplay") refresh(RefreshPolicy.IMMEDIATE)
  }.await

  // now we can search for the document we just indexed
  val resp: RichSearchResponse = client.execute { 
    search("bands" / "artists") query "coldplay" 
  }.await

  println("---- Search Hit Parsed ----")
  resp.to[Artist].foreach(println)

  // pretty print the complete response
  import io.circe.Json
  import io.circe.parser._
  println("---- Response as JSON ----")
  println(decode[Json](resp.original.toString).right.get.spaces2)

  localNode.close()
}

Now lets run our App on our embedded node.

```tut:book ArtistIndex.main(Array())



```tut:invisible
// clean up cluster
Files.walkFileTree(homePath, new SimpleFileVisitor[Path] {
  override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
    Files.delete(file)
    FileVisitResult.CONTINUE
  }
  override def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult = {
    Files.delete(dir)
    FileVisitResult.CONTINUE
  }
})