diff --git a/all-jdk7/build.gradle b/all-jdk7/build.gradle index 7771d5a..dda9eca 100644 --- a/all-jdk7/build.gradle +++ b/all-jdk7/build.gradle @@ -7,4 +7,5 @@ dependencies { runtime relativeProject(":klutter-core-jodatime-jdk6") runtime relativeProject(":klutter-json-jackson-jdk6") runtime relativeProject(":klutter-netflix-graph-jdk6") + runtime relativeProject(":klutter-elasticsearch-jdk7") } \ No newline at end of file diff --git a/all-jdk8/build.gradle b/all-jdk8/build.gradle index 8a32f60..622c41e 100644 --- a/all-jdk8/build.gradle +++ b/all-jdk8/build.gradle @@ -7,4 +7,5 @@ dependencies { runtime relativeProject(":klutter-json-jackson-jdk8") runtime relativeProject(":klutter-netflix-graph-jdk6") runtime relativeProject(":klutter-vertx3-jdk8") + runtime relativeProject(":klutter-elasticsearch-jdk7") } \ No newline at end of file diff --git a/elasticsearch-jdk7/README.md b/elasticsearch-jdk7/README.md new file mode 100644 index 0000000..6295063 --- /dev/null +++ b/elasticsearch-jdk7/README.md @@ -0,0 +1,3 @@ +## klutter/elasticsearch-jdk7 + +See module documentation in [klutter/elasticsearch](../elasticsearch) diff --git a/elasticsearch-jdk7/build.gradle b/elasticsearch-jdk7/build.gradle new file mode 100644 index 0000000..7806997 --- /dev/null +++ b/elasticsearch-jdk7/build.gradle @@ -0,0 +1,7 @@ +dependencies { + compile relativeProject(":klutter-json-jackson-jdk6") + compile relativeProject(":klutter-core-jdk7") + + compile "org.elasticsearch:elasticsearch:$version_elasticsearch" + compile "nl.komponents.kovenant:kovenant:$version_kovenant" +} \ No newline at end of file diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Client.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Client.kt new file mode 100644 index 0000000..d7a399a --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Client.kt @@ -0,0 +1,153 @@ +package uy.klutter.elasticsearch + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.all +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.functional.bind +import nl.komponents.kovenant.functional.map +import org.elasticsearch.action.ActionListener +import org.elasticsearch.action.ActionRequest +import org.elasticsearch.action.ActionRequestBuilder +import org.elasticsearch.action.ActionResponse +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse +import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus +import org.elasticsearch.client.Client +import org.elasticsearch.client.ElasticsearchClient +import org.elasticsearch.client.transport.TransportClient +import org.elasticsearch.common.settings.ImmutableSettings +import org.elasticsearch.common.transport.InetSocketTransportAddress +import org.elasticsearch.common.transport.TransportAddress +import org.elasticsearch.common.unit.TimeValue +import org.elasticsearch.node.NodeBuilder +import uy.klutter.core.common.with +import java.nio.file.Path + +public object EsConfig { + public volatile var adminActionTimeoutInSeconds: Long = 30 + public volatile var indexReplicaCount: Int = 1 + public volatile var indexShardCount: Int = 4 + + public volatile var objectMapper: ObjectMapper = jacksonObjectMapper() +} + + +public fun esNodeClient(clusterName: String, settings: Map): Client { + return esNodeClient(clusterName) { + settings.entrySet().forEach { + put(it.getKey(), it.getValue()) + } + } +} + +public fun esNodeClient(clusterName: String, init: ImmutableSettings.Builder.()->Unit): Client { + val settings = ImmutableSettings.settingsBuilder() + .put("cluster.name", clusterName) + .put("client.transport.sniff", false) + .put("node.name", "nodeClient-" + System.currentTimeMillis()) + .put("http.enabled", false) + .put("node.data", false) + .put("node.master", false) + .with { init() } + .build() + return NodeBuilder.nodeBuilder().settings(settings).node().client() +} + +public fun esTransportClient(clusterName: String, nodes: List, settings: Map): Client { + return esTransportClient(clusterName, nodes) { + settings.entrySet().forEach { + put(it.getKey(), it.getValue()) + } + } +} + +public fun esTransportClient(clusterName: String, nodes: List, init: ImmutableSettings.Builder.()->Unit): Client { + val settings = ImmutableSettings.settingsBuilder() + .put("cluster.name", clusterName) + .put("client.transport.sniff", false) + .with { init() } + .build() + val client = TransportClient(settings) + nodes.forEach { + client.addTransportAddress(it) + } + return NodeBuilder.nodeBuilder().settings(settings).node().client() +} + + +public fun esEmbeddedClient(clusterName: String, baseDir: Path, settings: Map): Promise { + return esEmbeddedClient(clusterName, baseDir) { + settings.entrySet().forEach { + put(it.getKey(), it.getValue()) + } + } +} + +public fun esEmbeddedClient(clusterName: String, baseDir: Path, init: ImmutableSettings.Builder.()->Unit): Promise { + val deferred = deferred() + try { + val esRoot = baseDir.toAbsolutePath() + val settings = ImmutableSettings.settingsBuilder() + .put("path.data", "$esRoot/data") + .put("path.work", "$esRoot/work") + .put("path.logs", "$esRoot/logs") + .put("http.enabled", false) + .put("index.number_of_shards", "2") + .put("index.number_of_replicas", "0") + .put("cluster.routing.schedule", "50ms") + .put("cluster.name", clusterName) + .put("client.transport.sniff", false) + .put("node.name", "nodeEmbedded-" + System.currentTimeMillis()) + .put("node.data", true) + .put("cluster.routing.allocation.disk.threshold_enabled", true) + .put("cluster.routing.allocation.disk.watermark.low", "10gb") + .with { init() } + .build() + val tempNode = NodeBuilder.nodeBuilder().local(true).data(true).settings(settings).node() + val tempClient = tempNode.client() + return tempClient.waitForYellowCluster().bind { deferred.promise } + } + catch (ex: Throwable) { + deferred.reject(wrapThrowable(ex)) + } + return deferred.promise +} + +public fun Client.waitForGreenCluster(): Promise { + return admin().cluster().prepareHealth().setWaitForGreenStatus().setTimeout(TimeValue.timeValueSeconds(EsConfig.adminActionTimeoutInSeconds)).promise { it.getStatus() } +} + +public fun Client.waitForYellowCluster(): Promise { + return admin().cluster().prepareHealth().setWaitForYellowStatus().setTimeout(TimeValue.timeValueSeconds(EsConfig.adminActionTimeoutInSeconds)).promise { it.getStatus() } +} + +public fun Client.waitForGreenIndex(vararg indices: String): Promise { + return admin().cluster().prepareHealth(*indices).setWaitForGreenStatus().setTimeout(TimeValue.timeValueSeconds(EsConfig.adminActionTimeoutInSeconds)).promise { it.getStatus() } +} + +public fun Client.waitForYellowIndex(vararg indices: String): Promise { + return admin().cluster().prepareHealth(*indices).setWaitForYellowStatus().setTimeout(TimeValue.timeValueSeconds(EsConfig.adminActionTimeoutInSeconds)).promise { it.getStatus() } +} + +public fun Client.indexExists(vararg indices: String): Promise { + return admin().indices().prepareExists(*indices).promise { it.isExists() } +} + +public fun Client.createIndex(index: String, mappings: List, shardCount: Int = EsConfig.indexShardCount, replicaCount: Int = EsConfig.indexReplicaCount, settingsInit: ImmutableSettings.Builder.()->Unit = {}): Promise { + val indexSettings = ImmutableSettings.settingsBuilder() + .put("number_of_shards", shardCount) + .put("number_of_replicas", replicaCount) + .with { settingsInit() } + .build() + return admin().indices().prepareCreate(index).setSettings(indexSettings).with { mappings.forEach { addMapping(it.type, it.json) } }.promiseNothing() +} + +public fun Client.updateIndexMappings(index: String, mappings: List): Promise, Exception> { + val actions = linkedListOf>() + mappings.forEach { + actions.add(admin().indices().preparePutMapping(index).setType(it.type).setSource(it.json).promise { it.isAcknowledged() }) + } + return all(actions) +} + diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Exceptions.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Exceptions.kt new file mode 100644 index 0000000..a411633 --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Exceptions.kt @@ -0,0 +1,7 @@ +package uy.klutter.elasticsearch + +import nl.komponents.kovenant.Deferred + +public class WrappedThrowableException(cause: Throwable): Exception(cause.getMessage(), cause) + +public fun wrapThrowable(rawEx: Throwable): Exception = if (rawEx is Exception) rawEx else WrappedThrowableException(rawEx) diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Indexing.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Indexing.kt new file mode 100644 index 0000000..6c7b05b --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Indexing.kt @@ -0,0 +1,8 @@ +package uy.klutter.elasticsearch + +import org.elasticsearch.action.index.IndexRequestBuilder + + +public fun IndexRequestBuilder.setSourceFromObject(pojo: Any) { + setSource(EsConfig.objectMapper.writeValueAsString(pojo)) +} diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Kovenant.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Kovenant.kt new file mode 100644 index 0000000..fce7600 --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Kovenant.kt @@ -0,0 +1,70 @@ +package uy.klutter.elasticsearch + +import nl.komponents.kovenant.Deferred +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.deferred +import org.elasticsearch.action.ActionListener +import org.elasticsearch.action.ActionRequest +import org.elasticsearch.action.ActionRequestBuilder +import org.elasticsearch.action.ActionResponse +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse +import org.elasticsearch.client.ElasticsearchClient + +public fun promiseResult(deferred: Deferred): ActionListener { + return object: ActionListener { + override fun onResponse(response: T) { + deferred.resolve(response) + } + + override fun onFailure(e: Throwable) { + deferred.reject(wrapThrowable(e)) + } + } +} + +public fun promiseResult(deferred: Deferred, map: (O)->T): ActionListener { + return object: ActionListener { + override fun onResponse(response: O) { + deferred.resolve(map(response)) + } + + override fun onFailure(e: Throwable) { + deferred.reject(wrapThrowable(e)) + } + } +} + +public fun , Response: ActionResponse, RequestBuilder: ActionRequestBuilder<*, *, *, *>, Client: ElasticsearchClient<*>> + ActionRequestBuilder.promise(deferred: Deferred): Promise { + this.execute(promiseResult(deferred)) + return deferred.promise +} + + +public fun , Response: ActionResponse, RequestBuilder: ActionRequestBuilder<*, *, *, *>, Client: ElasticsearchClient<*>> + ActionRequestBuilder.promise(): Promise { + val deferred = deferred() + this.execute(promiseResult(deferred)) + return deferred.promise +} + +public fun , Response: ActionResponse, RequestBuilder: ActionRequestBuilder<*, *, *, *>, Client: ElasticsearchClient<*>> + ActionRequestBuilder.promiseNothing(): Promise { + val deferred = deferred() + this.execute(promiseResult(deferred, {})) + return deferred.promise +} + + +public fun , Response: ActionResponse, RequestBuilder: ActionRequestBuilder<*, *, *, *>, Client: ElasticsearchClient<*>, O: Any> + ActionRequestBuilder.promise(map: (Response)->O): Promise { + val deferred = deferred() + this.execute(promiseResult(deferred, map)) + return deferred.promise +} + +public fun , Response: ActionResponse, RequestBuilder: ActionRequestBuilder<*, *, *, *>, Client: ElasticsearchClient<*>, O: Any> + ActionRequestBuilder.promise(deferred: Deferred, map: (Response)->O): Promise { + this.execute(promiseResult(deferred, map)) + return deferred.promise +} \ No newline at end of file diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Mappings.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Mappings.kt new file mode 100644 index 0000000..05e4296 --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Mappings.kt @@ -0,0 +1,109 @@ +package uy.klutter.elasticsearch + +import org.elasticsearch.common.xcontent.XContentBuilder + + +public data class IndexTypeMapping(val type: String, val json: XContentBuilder) + +public enum class EsSystemFields { + _uid, _id, _type, _source, _all, _analyzer, _boost, _parent, _field_names, _routing, _index, _size, _timestamp, _ttl +} + +public enum class EsStoredField { + STORED, NOT_STORED +} + +public enum class EsIndexedField { + NOT_ANALYZED, ANALYZED, NOT_INDEXED +} + +public fun mappingsForTypeWithEnum>(type: String, allowDynamic: Boolean = false, initTopLevel: XContentJsonObjectWithFields.()->Unit = {}, initProperties: XContentJsonObjectWithFields.()->Unit): IndexTypeMapping { + val mappings = xsonObject { + ObjectWithFields(type) { + setValue("dynamic", if (allowDynamic) "true" else "strict") + initTopLevel() + ObjectWithFields("properties") { + initProperties() + } + } + } + return IndexTypeMapping(type, mappings) +} + +public fun mappingsForType(type: String, allowDynamic: Boolean = false, initTopLevel: XContentJsonObjectWithFields.()->Unit = {}, initProperties: XContentJsonObject.()->Unit): IndexTypeMapping { + val mappings = xsonObject { + ObjectWithFields(type) { + setValue("dyanmic", if (allowDynamic) "true" else "strict") + initTopLevel() + Object("properties") { + initProperties() + } + } + } + return IndexTypeMapping(type, mappings) +} + +public fun > XContentJsonObjectWithFields.stringField(field: T, indexed: EsIndexedField = EsIndexedField.NOT_ANALYZED, stored: EsStoredField = EsStoredField.NOT_STORED, init: XContentJsonObject.()->Unit) { + Object(field) { + setValue("type", "string") + setValue("store", stored == EsStoredField.STORED) + setValue("index", when(indexed) { + EsIndexedField.ANALYZED -> "analyzed" + EsIndexedField.NOT_ANALYZED -> "not_analyzed" + else -> "no" + }) + init() + } +} + +public fun > XContentJsonObjectWithFields.ignoreField(field: T) { + Object(field) { + setValue("type", "string") + setValue("store", false) + setValue("index", "no") + } +} + +public fun > XContentJsonObjectWithFields.dateField(field: T, indexed: EsIndexedField = EsIndexedField.NOT_ANALYZED, stored: EsStoredField = EsStoredField.NOT_STORED, init: XContentJsonObject.()->Unit) { + Object(field) { + setValue("type", "date") + setValue("store", stored == EsStoredField.STORED) + if (indexed == EsIndexedField.NOT_INDEXED) { + setValue("index", "no") + } + init() + } +} + +public fun > XContentJsonObjectWithFields.booleanField(field: T, indexed: EsIndexedField = EsIndexedField.NOT_ANALYZED, stored: EsStoredField = EsStoredField.NOT_STORED, init: XContentJsonObject.()->Unit) { + Object(field) { + setValue("type", "boolean") + setValue("store", stored == EsStoredField.STORED) + if (indexed == EsIndexedField.NOT_INDEXED) { + setValue("index", "no") + } + init() + } +} + +public fun > XContentJsonObjectWithFields.integerField(field: T, indexed: EsIndexedField = EsIndexedField.NOT_ANALYZED, stored: EsStoredField = EsStoredField.NOT_STORED, init: XContentJsonObject.()->Unit) { + Object(field) { + setValue("type", "integer") + setValue("store", stored == EsStoredField.STORED) + if (indexed == EsIndexedField.NOT_INDEXED) { + setValue("index", "no") + } + init() + } +} + +public fun > XContentJsonObjectWithFields.longField(field: T, indexed: EsIndexedField = EsIndexedField.NOT_ANALYZED, stored: EsStoredField = EsStoredField.NOT_STORED, init: XContentJsonObject.()->Unit) { + Object(field) { + setValue("type", "long") + setValue("store", stored == EsStoredField.STORED) + if (indexed == EsIndexedField.NOT_INDEXED) { + setValue("index", "no") + } + init() + } +} \ No newline at end of file diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Results.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Results.kt new file mode 100644 index 0000000..255ce86 --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/Results.kt @@ -0,0 +1,14 @@ +package uy.klutter.elasticsearch + +import com.fasterxml.jackson.core.type.TypeReference +import org.elasticsearch.action.search.SearchResponse +import org.elasticsearch.search.SearchHits + + +public inline fun SearchResponse.getHitsAsObjects(): Sequence { + return getHits().getHits().asSequence().map { EsConfig.objectMapper.readValue(it.sourceAsString(), object : TypeReference(){}) } +} + +public inline fun SearchHits.getHitsAsObjects(): Sequence { + return getHits().asSequence().map { EsConfig.objectMapper.readValue(it.sourceAsString(), object : TypeReference(){}) } +} diff --git a/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/XContent.kt b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/XContent.kt new file mode 100644 index 0000000..1cc65c3 --- /dev/null +++ b/elasticsearch-jdk7/src/main/kotlin/uy/klutter/elasticsearch/XContent.kt @@ -0,0 +1,103 @@ +package uy.klutter.elasticsearch + +import org.elasticsearch.common.xcontent.XContentBuilder +import org.elasticsearch.common.xcontent.XContentFactory +import java.math.BigDecimal + +public class XContentJsonObjectWithFields>(x: XContentBuilder): XContentJsonObject(x) { + public fun setValue(field: T, value: String): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Long): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Int): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Short): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Byte): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Double): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Float): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: BigDecimal): Unit { x.field(field.name(), value) } + public fun setValue(field: T, value: Boolean): Unit { x.field(field.name(), value) } + public fun setValueNull(field: T): Unit { x.nullField(field.name()) } + public fun Object(field: T, init: XContentJsonObject.()->Unit): Unit { + x.startObject(field.name()) + XContentJsonObject(x).init() + x.endObject() + } + public fun Array(field: T, init: XContentJsonArray.()->Unit): Unit { + x.startArray(field.name()) + XContentJsonArray(x).init() + x.endArray() + } +} + +public open class XContentJsonObject(protected val x: XContentBuilder) { + public fun setValue(name: String, value: String): Unit { x.field(name, value) } + public fun setValue(name: String, value: Long): Unit { x.field(name, value) } + public fun setValue(name: String, value: Int): Unit { x.field(name, value) } + public fun setValue(name: String, value: Short): Unit { x.field(name, value) } + public fun setValue(name: String, value: Byte): Unit { x.field(name, value) } + public fun setValue(name: String, value: Double): Unit { x.field(name, value) } + public fun setValue(name: String, value: Float): Unit { x.field(name, value) } + public fun setValue(name: String, value: BigDecimal): Unit { x.field(name, value) } + public fun setValue(name: String, value: Boolean): Unit { x.field(name, value) } + public fun setValueNull(name: String): Unit { x.nullField(name) } + public fun Object(name: String, init: XContentJsonObject.()->Unit): Unit { + x.startObject(name) + XContentJsonObject(x).init() + x.endObject() + } + public fun > ObjectWithFields(name: String, init: XContentJsonObjectWithFields.()->Unit): Unit { + x.startObject(name) + XContentJsonObjectWithFields(x).init() + x.endObject() + } + public fun Array(name: String, init: XContentJsonArray.()->Unit): Unit { + x.startArray(name) + XContentJsonArray(x).init() + x.endArray() + } +} + +public class XContentJsonArray(private val x: XContentBuilder) { + public fun addValue(value: String): Unit { x.value(value) } + public fun addValue(value: Long): Unit { x.value(value) } + public fun addValue(value: Int): Unit { x.value(value) } + public fun addValue(value: Short): Unit { x.value(value) } + public fun addValue(value: Byte): Unit { x.value(value) } + public fun addValue(value: Double): Unit { x.value(value) } + public fun addValue(value: Float): Unit { x.value(value) } + public fun addValue(value: BigDecimal): Unit { x.value(value) } + public fun addValue(value: Boolean): Unit { x.value(value) } + public fun addValueNull(): Unit { x.nullValue() } + public fun addObject(init: XContentJsonObject.()->Unit): Unit { + x.startObject() + XContentJsonObject(x).init() + x.endObject() + } + public fun addArray(init: XContentJsonArray.()->Unit): Unit { + x.startArray() + XContentJsonArray(x).init() + x.endArray() + } +} + +public fun xsonObjectWithFields>(init: XContentJsonObjectWithFields.()->Unit): XContentBuilder { + val builder = XContentFactory.jsonBuilder() + builder.startObject() + XContentJsonObjectWithFields(builder).init() + builder.endObject() + return builder +} + +public fun xsonObject(init: XContentJsonObject.()->Unit): XContentBuilder { + val builder = XContentFactory.jsonBuilder() + builder.startObject() + XContentJsonObject(builder).init() + builder.endObject() + return builder +} + +public fun xsonArray(init: XContentJsonArray.()->Unit): XContentBuilder { + val builder = XContentFactory.jsonBuilder() + builder.startArray() + XContentJsonArray(builder).init() + builder.endArray() + return builder +} diff --git a/elasticsearch/README.md b/elasticsearch/README.md new file mode 100644 index 0000000..712e9ff --- /dev/null +++ b/elasticsearch/README.md @@ -0,0 +1,65 @@ +## klutter/elasticsearch + +Helper functions for ElasticSearch. + +## Kovenant Integration + +Actions created for ElasticSearch normally executed synchronously with `execute().actionGet()` or async with `excute()` +can be called with extension methods `promise()` and `promiseNothing()` (for void methods) to return a [Kovenant](http://kovenant.komponents.nl) +promise. + +## Client Helpers + +Methods available to create ES Clients (each returns Kovenant promises): + +|Method|Description| +|------|-----------| +|esNodeClient(clusterName,settingsMap)|Create Node client with provided settings| +|esNodeClient(clusterName,initFunc)|Create Node client with builder function on settings object| +|esTransportClient(clusterName,nodes,settingsMap)|Create Transport client for a given node list and provided settings| +|esTransportClient(clusterName,nodes,initFunc)|Create Transport client for a given node list with builder function on settings object| +|esEmbeddedClient(clusterName,baseDir,settingsMap)|Create embedded local ElasticSearch configured to use data, work and log dirs under baseDir| +|esEmbeddedClient(clusterName,baseDir,initFunc)|Create embedded local ElasticSearch configured to use data, work and log dirs under baseDir| + +Extension functions on all ES clients: + +|Method|Description| +|------|-----------| +|waitForGreenCluster()|Returns promise of waiting for green cluster state| +|waitForYellowCluster()|Returns promise of waiting at least for yellow cluster state (actual state returned)| +|waitForGreenIndex(indices)|Returns promise of waiting for green indices state| +|waitForYellowIndex(indices)|Returns promise of waiting at least for yellow indices state (actual state returned| +|indexExists(indices)|Returns promise Boolean of whether all indices provided exist or not| +|createIndex(index,mappings,shards,replicas,initFunc)|Create an index with provided mappings per type, number of shards and replicas, and with a builder function on the index settings object| +|updateIndexMappings(index,mappings)|Update an index for a list of type mappings| + +Note that type mappings for `createIndex` and `updateIndexMappings` are a data class: + +```kotlin +public data class IndexTypeMapping(val type: String, val json: XContentBuilder) +``` + +## Index Mapping And XContent Helpers + +XContent when required, can be created using helper functions that start a JSON style builder: + +`xsonObject(initFunc)` and `xsonArray(initFunc)` start JSON style builders to create XContent for any purpose. If you use +`xsonObjectWithFields(initFunc)` your builder can use the Enum as the field name for more "name safety". + +For creating mappings, it is easier to start with `mappingsForType(typeName,allowDynamic,topLevelInitFunc,propertiesInitFunc)` or +the similar `mappingsForTypeWithEnum(...)` which adds extra builder methods such as: + +* `stringField(fieldName, indexed, stored, initFunc)` +* `dateField(fieldName, indexed, stored, initFunc)` +* `integerField(fieldName, indexed, stored, initFunc)` +* `longField(fieldName, indexed, stored, initFunc)` +* `booleanField(fieldName, indexed, stored, initFunc)` +* `ignoreField(fieldName)` + +Where the initFunc allows extra properties to be set on the XContent such as custom analyzers and more. `indexed` and `stored` are Enums. + +## Other Helpers + +When indexing, you can use the `IndexRequestBuilder.setSourceFromObject(pojo)` helper to databind using Jackson your object into JSON source. + +And for search results, `SearchResponse.getHitsAsObjects(): Sequence` and same extension on `SearchHits` returns databound objects from the JSON results. \ No newline at end of file diff --git a/elasticsearch/build.gradle b/elasticsearch/build.gradle new file mode 100644 index 0000000..f17e08b --- /dev/null +++ b/elasticsearch/build.gradle @@ -0,0 +1,3 @@ +dependencies { + runtime relativeProject(":klutter-elasticsearch-jdk7") +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 18d304e..5af6e87 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=0.3.4 +version=0.4.0 group=uy.klutter version_gradle=2.5 @@ -23,4 +23,6 @@ version_typesafe_config_jdk8=1.3.0 version_vertx=3.0.0 version_kovenant=2.3.3 -version_netflix_graph=1.5.2 \ No newline at end of file +version_netflix_graph=1.5.2 + +version_elasticsearch=1.6.0 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1349196..33ecc50 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,6 +21,8 @@ include 'core', 'netflix-graph-jdk6', 'vertx3', 'vertx3-jdk8', + 'elasticsearch-jdk7', + 'elasticsearch', 'all-jdk6', 'all-jdk7', 'all-jdk8', @@ -47,6 +49,8 @@ project(':netflix-graph').name = 'klutter-netflix-graph' project(':netflix-graph-jdk6').name = 'klutter-netflix-graph-jdk6' project(':vertx3').name = 'klutter-vertx3' project(':vertx3-jdk8').name = 'klutter-vertx3-jdk8' +project(':elasticsearch').name = 'klutter-elasticsearch' +project(':elasticsearch-jdk7').name = 'klutter-elasticsearch-jdk7' project(':all').name = 'klutter-all' project(':all-jdk6').name = 'klutter-all-jdk6'