mirror of
https://github.com/pgsty/minio.git
synced 2026-08-11 00:33:28 +03:00
api: Implement bucket notification. (#2271)
* Implement basic S3 notifications through queues Supports multiple queues and three basic queue types: 1. NilQueue -- messages don't get sent anywhere 2. LogQueue -- messages get logged 3. AmqpQueue -- messages are sent to an AMQP queue * api: Implement bucket notification. Supports two different queue types - AMQP - ElasticSearch. * Add support for redis
This commit is contained in:
committed by
Anand Babu (AB) Periasamy
parent
f85d94288d
commit
f248089523
+363
@@ -0,0 +1,363 @@
|
||||
# Elastic 3.0
|
||||
|
||||
Elasticsearch 2.0 comes with some [breaking changes](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/breaking-changes-2.0.html). You will probably need to upgrade your application and/or rewrite part of it due to those changes.
|
||||
|
||||
We use that window of opportunity to also update Elastic (the Go client) from version 2.0 to 3.0. This will introduce both changes due to the Elasticsearch 2.0 update as well as changes that make Elastic cleaner by removing some old cruft.
|
||||
|
||||
So, to summarize:
|
||||
|
||||
1. Elastic 2.0 is compatible with Elasticsearch 1.7+ and is still actively maintained.
|
||||
2. Elastic 3.0 is compatible with Elasticsearch 2.0+ and will soon become the new master branch.
|
||||
|
||||
The rest of the document is a list of all changes in Elastic 3.0.
|
||||
|
||||
## Pointer types
|
||||
|
||||
All types have changed to be pointer types, not value types. This not only is cleaner but also simplifies the API as illustrated by the following example:
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
q := elastic.NewMatchAllQuery()
|
||||
res, err := elastic.Search("one").Query(&q).Do() // notice the & here
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
q := elastic.NewMatchAllQuery()
|
||||
res, err := elastic.Search("one").Query(q).Do() // no more &
|
||||
// ... which can be simplified as:
|
||||
res, err := elastic.Search("one").Query(elastic.NewMatchAllQuery()).Do()
|
||||
```
|
||||
|
||||
It also helps to prevent [subtle issues](https://github.com/olivere/elastic/issues/115#issuecomment-130753046).
|
||||
|
||||
## Query/filter merge
|
||||
|
||||
One of the biggest changes in Elasticsearch 2.0 is the [merge of queries and filters](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_queries_and_filters_merged). In Elasticsearch 1.x, you had a whole range of queries and filters that were basically identical (e.g. `term_query` and `term_filter`).
|
||||
|
||||
The practical aspect of the merge is that you can now basically use queries where once you had to use filters instead. For Elastic 3.0 this means: We could remove a whole bunch of files. Yay!
|
||||
|
||||
Notice that some methods still come by "filter", e.g. `PostFilter`. However, they accept a `Query` now when they used to accept a `Filter` before.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
q := elastic.NewMatchAllQuery()
|
||||
f := elastic.NewTermFilter("tag", "important")
|
||||
res, err := elastic.Search().Index("one").Query(&q).PostFilter(f)
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
q := elastic.NewMatchAllQuery()
|
||||
f := elastic.NewTermQuery("tag", "important") // it's a query now!
|
||||
res, err := elastic.Search().Index("one").Query(q).PostFilter(f)
|
||||
```
|
||||
|
||||
## Facets are removed
|
||||
|
||||
[Facets have been removed](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_removed_features.html#_facets_have_been_removed) in Elasticsearch 2.0. You need to use aggregations now.
|
||||
|
||||
## Errors
|
||||
|
||||
Elasticsearch 2.0 returns more information about an error in the HTTP response body. Elastic 3.0 now reads this information and makes it accessible by the consumer.
|
||||
|
||||
Errors and all its details are now returned in [`Error`](https://github.com/olivere/elastic/blob/release-branch.v3/errors.go#L59).
|
||||
|
||||
### HTTP Status 404 (Not Found)
|
||||
|
||||
When Elasticsearch does not find an entity or an index, it generally returns HTTP status code 404. In Elastic 2.0 this was a valid result and didn't raise an error from the `Do` functions. This has now changed in Elastic 3.0.
|
||||
|
||||
Starting with Elastic 3.0, there are only two types of responses considered successful. First, responses with HTTP status codes [200..299]. Second, HEAD requests which return HTTP status 404. The latter is used by Elasticsearch to e.g. check for existence of indices or documents. All other responses will return an error.
|
||||
|
||||
To check for HTTP Status 404 (with non-HEAD requests), e.g. when trying to get or delete a missing document, you can use the [`IsNotFound`](https://github.com/olivere/elastic/blob/release-branch.v3/errors.go#L84) helper (see below).
|
||||
|
||||
The following example illustrates how to check for a missing document in Elastic 2.0 and what has changed in 3.0.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
res, err = client.Get().Index("one").Type("tweet").Id("no-such-id").Do()
|
||||
if err != nil {
|
||||
// Something else went wrong (but 404 is NOT an error in Elastic 2.0)
|
||||
}
|
||||
if !res.Found {
|
||||
// Document has not been found
|
||||
}
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
res, err = client.Get().Index("one").Type("tweet").Id("no-such-id").Do()
|
||||
if err != nil {
|
||||
if elastic.IsNotFound(err) {
|
||||
// Document has not been found
|
||||
} else {
|
||||
// Something else went wrong
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Status 408 (Timeouts)
|
||||
|
||||
Elasticsearch now responds with HTTP status code 408 (Timeout) when a request fails due to a timeout. E.g. if you specify a timeout with the Cluster Health API, the HTTP response status will be 408 if the timeout is raised. See [here](https://github.com/elastic/elasticsearch/commit/fe3179d9cccb569784434b2135ca9ae13d5158d3) for the specific commit to the Cluster Health API.
|
||||
|
||||
To check for HTTP Status 408, we introduced the [`IsTimeout`](https://github.com/olivere/elastic/blob/release-branch.v3/errors.go#L101) helper.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
health, err := client.ClusterHealth().WaitForStatus("yellow").Timeout("1s").Do()
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
if health.TimedOut {
|
||||
// We have a timeout
|
||||
}
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
health, err := client.ClusterHealth().WaitForStatus("yellow").Timeout("1s").Do()
|
||||
if elastic.IsTimeout(err) {
|
||||
// We have a timeout
|
||||
}
|
||||
```
|
||||
|
||||
### Bulk Errors
|
||||
|
||||
The error response of a bulk operation used to be a simple string in Elasticsearch 1.x.
|
||||
In Elasticsearch 2.0, it returns a structured JSON object with a lot more details about the error.
|
||||
These errors are now captured in an object of type [`ErrorDetails`](https://github.com/olivere/elastic/blob/release-branch.v3/errors.go#L59) which is used in [`BulkResponseItem`](https://github.com/olivere/elastic/blob/release-branch.v3/bulk.go#L206).
|
||||
|
||||
### Removed specific Elastic errors
|
||||
|
||||
The specific error types `ErrMissingIndex`, `ErrMissingType`, and `ErrMissingId` have been removed. They were only used by `DeleteService` and are replaced by a generic error message.
|
||||
|
||||
## Numeric types
|
||||
|
||||
Elastic 3.0 has settled to use `float64` everywhere. It used to be a mix of `float32` and `float64` in Elastic 2.0. E.g. all boostable queries in Elastic 3.0 now have a boost type of `float64` where it used to be `float32`.
|
||||
|
||||
## Pluralization
|
||||
|
||||
Some services accept zero, one or more indices or types to operate on.
|
||||
E.g. in the `SearchService` accepts a list of zero, one, or more indices to
|
||||
search and therefor had a func called `Index(index string)` and a func
|
||||
called `Indices(indices ...string)`.
|
||||
|
||||
Elastic 3.0 now only uses the singular form that, when applicable, accepts a
|
||||
variadic type. E.g. in the case of the `SearchService`, you now only have
|
||||
one func with the following signature: `Index(indices ...string)`.
|
||||
|
||||
Notice this is only limited to `Index(...)` and `Type(...)`. There are other
|
||||
services with variadic functions. These have not been changed.
|
||||
|
||||
## Multiple calls to variadic functions
|
||||
|
||||
Some services with variadic functions have cleared the underlying slice when
|
||||
called while other services just add to the existing slice. This has now been
|
||||
normalized to always add to the underlying slice.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
// Would only cleared scroll id "two"
|
||||
// because ScrollId cleared the values when called multiple times
|
||||
client.ClearScroll().ScrollId("one").ScrollId("two").Do()
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
// Now (correctly) clears both scroll id "one" and "two"
|
||||
// because ScrollId no longer clears the values when called multiple times
|
||||
client.ClearScroll().ScrollId("one").ScrollId("two").Do()
|
||||
```
|
||||
|
||||
## Ping service requires URL
|
||||
|
||||
The `Ping` service raised some issues because it is different from all
|
||||
other services. If not explicitly given a URL, it always pings `127.0.0.1:9200`.
|
||||
|
||||
Users expected to ping the cluster, but that is not possible as the cluster
|
||||
can be a set of many nodes: So which node do we ping then?
|
||||
|
||||
To make it more clear, the `Ping` function on the client now requires users
|
||||
to explicitly set the URL of the node to ping.
|
||||
|
||||
## Meta fields
|
||||
|
||||
Many of the meta fields e.g. `_parent` or `_routing` are now
|
||||
[part of the top-level of a document](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_mapping_changes.html#migration-meta-fields)
|
||||
and are no longer returned as parts of the `fields` object. We had to change
|
||||
larger parts of e.g. the `Reindexer` to get it to work seamlessly with Elasticsearch 2.0.
|
||||
|
||||
Notice that all stored meta-fields are now [returned by default](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_crud_and_routing_changes.html#_all_stored_meta_fields_returned_by_default).
|
||||
|
||||
## HasParentQuery / HasChildQuery
|
||||
|
||||
`NewHasParentQuery` and `NewHasChildQuery` must now include both parent/child type and query. It is now in line with the Java API.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
allQ := elastic.NewMatchAllQuery()
|
||||
q := elastic.NewHasChildFilter("tweet").Query(&allQ)
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
q := elastic.NewHasChildQuery("tweet", elastic.NewMatchAllQuery())
|
||||
```
|
||||
|
||||
## SetBasicAuth client option
|
||||
|
||||
You can now tell Elastic to pass HTTP Basic Auth credentials with each request. In previous versions of Elastic you had to set up your own `http.Transport` to do this. This should make it more convenient to use Elastic in combination with [Shield](https://www.elastic.co/products/shield) in its [basic setup](https://www.elastic.co/guide/en/shield/current/enable-basic-auth.html).
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
client, err := elastic.NewClient(elastic.SetBasicAuth("user", "secret"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Delete-by-Query API
|
||||
|
||||
The Delete-by-Query API is [a plugin now](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_removed_features.html#_delete_by_query_is_now_a_plugin). It is no longer core part of Elasticsearch. You can [install it as a plugin as described here](https://www.elastic.co/guide/en/elasticsearch/plugins/2.0/plugins-delete-by-query.html).
|
||||
|
||||
Elastic 3.0 still contains the `DeleteByQueryService`, but you need to install the plugin first. If you don't install it and use `DeleteByQueryService` you will most probably get a 404.
|
||||
|
||||
An older version of this document stated the following:
|
||||
|
||||
> Elastic 3.0 still contains the `DeleteByQueryService` but it will fail with `ErrPluginNotFound` when the plugin is not installed.
|
||||
>
|
||||
> Example for Elastic 3.0 (new):
|
||||
>
|
||||
> ```go
|
||||
> _, err := client.DeleteByQuery().Query(elastic.NewTermQuery("client", "1")).Do()
|
||||
> if err == elastic.ErrPluginNotFound {
|
||||
> // Delete By Query API is not available
|
||||
> }
|
||||
> ```
|
||||
|
||||
I have decided that this is not a good way to handle the case of a missing plugin. The main reason is that with this logic, you'd always have to check if the plugin is missing in case of an error. This is not only slow, but it also puts logic into a service where it should really be just opaque and return the response of Elasticsearch.
|
||||
|
||||
If you rely on certain plugins to be installed, you should check on startup. That's where the following two helpers come into play.
|
||||
|
||||
## HasPlugin and SetRequiredPlugins
|
||||
|
||||
Some of the core functionality of Elasticsearch has now been moved into plugins. E.g. the Delete-by-Query API is [a plugin now](https://www.elastic.co/guide/en/elasticsearch/plugins/2.0/plugins-delete-by-query.html).
|
||||
|
||||
You need to make sure to add these plugins to your Elasticsearch installation to still be able to use the `DeleteByQueryService`. You can test this now with the `HasPlugin(name string)` helper in the client.
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
err, found := client.HasPlugin("delete-by-query")
|
||||
if err == nil && found {
|
||||
// ... Delete By Query API is available
|
||||
}
|
||||
```
|
||||
|
||||
To simplify this process, there is now a `SetRequiredPlugins` helper that can be passed as an option func when creating a new client. If the plugin is not installed, the client wouldn't be created in the first place.
|
||||
|
||||
```go
|
||||
// Will raise an error if the "delete-by-query" plugin is NOT installed
|
||||
client, err := elastic.NewClient(elastic.SetRequiredPlugins("delete-by-query"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
Notice that there also is a way to define [mandatory plugins](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-plugins.html#_mandatory_plugins) in the Elasticsearch configuration file.
|
||||
|
||||
## Common Query has been renamed to Common Terms Query
|
||||
|
||||
The `CommonQuery` has been renamed to `CommonTermsQuery` to be in line with the [Java API](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_java_api_changes.html#_query_filter_refactoring).
|
||||
|
||||
## Remove `MoreLikeThis` and `MoreLikeThisField`
|
||||
|
||||
The More Like This API and the More Like This Field query [have been removed](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_more_like_this) and replaced with the `MoreLikeThisQuery`.
|
||||
|
||||
## Remove Filtered Query
|
||||
|
||||
With the merge of queries and filters, the [filtered query became deprecated](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_literal_filtered_literal_query_and_literal_query_literal_filter_deprecated). While it is only deprecated and therefore still available in Elasticsearch 2.0, we have decided to remove it from Elastic 3.0. Why? Because we think that when you're already forced to rewrite many of your application code, it might be a good chance to get rid of things that are deprecated as well. So you might simply change your filtered query with a boolean query as [described here](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_literal_filtered_literal_query_and_literal_query_literal_filter_deprecated).
|
||||
|
||||
## Remove FuzzyLikeThis and FuzzyLikeThisField
|
||||
|
||||
Both have been removed from Elasticsearch 2.0 as well.
|
||||
|
||||
## Remove LimitFilter
|
||||
|
||||
The `limit` filter is [deprecated in Elasticsearch 2.0](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_literal_limit_literal_filter_deprecated) and becomes a no-op. Now is a good chance to remove it from your application as well. Use the `terminate_after` parameter in your search [as described here](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/search-request-body.html) to achieve similar effects.
|
||||
|
||||
## Remove `_cache` and `_cache_key` from filters
|
||||
|
||||
Both have been [removed from Elasticsearch 2.0 as well](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_query_dsl_changes.html#_filter_auto_caching).
|
||||
|
||||
## Partial fields are gone
|
||||
|
||||
Partial fields are [removed in Elasticsearch 2.0](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/_search_changes.html#_partial_fields) in favor of [source filtering](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/search-request-source-filtering.html).
|
||||
|
||||
## Scripting
|
||||
|
||||
A [`Script`](https://github.com/olivere/elastic/blob/release-branch.v3/script.go) type has been added to Elastic 3.0. In Elastic 2.0, there were various places (e.g. aggregations) where you could just add the script as a string, specify the scripting language, add parameters etc. With Elastic 3.0, you should now always use the `Script` type.
|
||||
|
||||
Example for Elastic 2.0 (old):
|
||||
|
||||
```go
|
||||
update, err := client.Update().Index("twitter").Type("tweet").Id("1").
|
||||
Script("ctx._source.retweets += num").
|
||||
ScriptParams(map[string]interface{}{"num": 1}).
|
||||
Upsert(map[string]interface{}{"retweets": 0}).
|
||||
Do()
|
||||
```
|
||||
|
||||
Example for Elastic 3.0 (new):
|
||||
|
||||
```go
|
||||
update, err := client.Update().Index("twitter").Type("tweet").Id("1").
|
||||
Script(elastic.NewScript("ctx._source.retweets += num").Param("num", 1)).
|
||||
Upsert(map[string]interface{}{"retweets": 0}).
|
||||
Do()
|
||||
```
|
||||
|
||||
## Cluster State
|
||||
|
||||
The combination of `Metric(string)` and `Metrics(...string)` has been replaced by a single func with the signature `Metric(...string)`.
|
||||
|
||||
## Unexported structs in response
|
||||
|
||||
Services generally return a typed response from a `Do` func. Those structs are exported so that they can be passed around in your own application. In Elastic 3.0 however, we changed that (most) sub-structs are now unexported, meaning: You can only pass around the whole response, not sub-structures of it. This makes it easier for restructuring responses according to the Elasticsearch API. See [`ClusterStateResponse`](https://github.com/olivere/elastic/blob/release-branch.v3/cluster_state.go#L182) as an example.
|
||||
|
||||
## Add offset to Histogram aggregation
|
||||
|
||||
Histogram aggregations now have an [offset](https://github.com/elastic/elasticsearch/pull/9505) option.
|
||||
|
||||
## Services
|
||||
|
||||
### REST API specification
|
||||
|
||||
As you might know, Elasticsearch comes with a REST API specification. The specification describes the endpoints in a JSON structure.
|
||||
|
||||
Most services in Elastic predated the REST API specification. We are in the process of bringing all these services in line with the specification. Services can be generated by `go generate` (not 100% automatic though). This is an ongoing process.
|
||||
|
||||
This probably doesn't mean a lot to you. However, you can now be more confident that Elastic supports all features that the REST API specification describes.
|
||||
|
||||
At the same time, the file names of the services are renamed to match the REST API specification naming.
|
||||
|
||||
### REST API Test Suite
|
||||
|
||||
The REST API specification of Elasticsearch comes along with a test suite that official clients typically use to test for conformance. Up until now, Elastic didn't run this test suite. However, we are in the process of setting up infrastructure and tests to match this suite as well.
|
||||
|
||||
This process in not completed though.
|
||||
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# How to contribute
|
||||
|
||||
Elastic is an open-source project and we are looking forward to each
|
||||
contribution.
|
||||
|
||||
Notice that while the [official Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) is rather good, it is a high-level
|
||||
overview of the features of Elasticsearch. However, Elastic tries to resemble
|
||||
the Java API of Elasticsearch which you can find [on GitHub](https://github.com/elastic/elasticsearch).
|
||||
|
||||
This explains why you might think that some options are strange or missing
|
||||
in Elastic, while often they're just different. Please check the Java API first.
|
||||
|
||||
Having said that: Elasticsearch is moving fast and it might be very likely
|
||||
that we missed some features or changes. Feel free to change that.
|
||||
|
||||
## Your Pull Request
|
||||
|
||||
To make it easy to review and understand your changes, please keep the
|
||||
following things in mind before submitting your pull request:
|
||||
|
||||
* You compared the existing implemenation with the Java API, did you?
|
||||
* Please work on the latest possible state of `olivere/elastic`.
|
||||
Use `release-branch.v2` for targeting Elasticsearch 1.x and
|
||||
`release-branch.v3` for targeting 2.x.
|
||||
* Create a branch dedicated to your change.
|
||||
* If possible, write a test case which confirms your change.
|
||||
* Make sure your changes and your tests work with all recent versions of
|
||||
Elasticsearch. We currently support Elasticsearch 1.7.x in the
|
||||
release-branch.v2 and Elasticsearch 2.x in the release-branch.v3.
|
||||
* Test your changes before creating a pull request (`go test ./...`).
|
||||
* Don't mix several features or bug fixes in one pull request.
|
||||
* Create a meaningful commit message.
|
||||
* Explain your change, e.g. provide a link to the issue you are fixing and
|
||||
probably a link to the Elasticsearch documentation and/or source code.
|
||||
* Format your source with `go fmt`.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
* [GitHub documentation](http://help.github.com/)
|
||||
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
# This is a list of people who have contributed code
|
||||
# to the Elastic repository.
|
||||
#
|
||||
# It is just my small "thank you" to all those that helped
|
||||
# making Elastic what it is.
|
||||
#
|
||||
# Please keep this list sorted.
|
||||
|
||||
Adam Alix [@adamalix](https://github.com/adamalix)
|
||||
Adam Weiner [@adamweiner](https://github.com/adamweiner)
|
||||
Alexey Sharov [@nizsheanez](https://github.com/nizsheanez)
|
||||
Benjamin Zarzycki [@kf6nux](https://github.com/kf6nux)
|
||||
Braden Bassingthwaite [@bbassingthwaite-va](https://github.com/bbassingthwaite-va)
|
||||
Brady Love [@bradylove](https://github.com/bradylove)
|
||||
Bruce Zhou [@brucez-isell](https://github.com/brucez-isell)
|
||||
Chris M [@tebriel](https://github.com/tebriel)
|
||||
Christophe Courtaut [@kri5](https://github.com/kri5)
|
||||
Conrad Pankoff [@deoxxa](https://github.com/deoxxa)
|
||||
Corey Scott [@corsc](https://github.com/corsc)
|
||||
Daniel Barrett [@shendaras](https://github.com/shendaras)
|
||||
Daniel Heckrath [@DanielHeckrath](https://github.com/DanielHeckrath)
|
||||
Daniel Imfeld [@dimfeld](https://github.com/dimfeld)
|
||||
Dwayne Schultz [@myshkin5](https://github.com/myshkin5)
|
||||
Faolan C-P [@fcheslack](https://github.com/fcheslack)
|
||||
Gerhard Häring [@ghaering](https://github.com/ghaering)
|
||||
Guilherme Silveira [@guilherme-santos](https://github.com/guilherme-santos)
|
||||
Guillaume J. Charmes [@creack](https://github.com/creack)
|
||||
Han Yu [@MoonighT](https://github.com/MoonighT)
|
||||
Harrison Wright [@wright8191](https://github.com/wright8191)
|
||||
Igor Dubinskiy [@idubinskiy](https://github.com/idubinskiy)
|
||||
Isaac Saldana [@isaldana](https://github.com/isaldana)
|
||||
Jack Lindamood [@cep21](https://github.com/cep21)
|
||||
Joe Buck [@four2five](https://github.com/four2five)
|
||||
John Barker [@j16r](https://github.com/j16r)
|
||||
John Goodall [@jgoodall](https://github.com/jgoodall)
|
||||
Junpei Tsuji [@jun06t](https://github.com/jun06t)
|
||||
Kenta SUZUKI [@suzuken](https://github.com/suzuken)
|
||||
Maciej Lisiewski [@c2h5oh](https://github.com/c2h5oh)
|
||||
Mara Kim [@autochthe](https://github.com/autochthe)
|
||||
Marcy Buccellato [@marcybuccellato](https://github.com/marcybuccellato)
|
||||
Medhi Bechina [@mdzor](https://github.com/mdzor)
|
||||
naimulhaider [@naimulhaider](https://github.com/naimulhaider)
|
||||
navins [@ishare](https://github.com/ishare)
|
||||
Naoya Tsutsumi [@tutuming](https://github.com/tutuming)
|
||||
Nicholas Wolff [@nwolff](https://github.com/nwolff)
|
||||
Nick Whyte [@nickw444](https://github.com/nickw444)
|
||||
Orne Brocaar [@brocaar](https://github.com/brocaar)
|
||||
Radoslaw Wesolowski [r--w](https://github.com/r--w)
|
||||
Ryan Schmukler [@rschmukler](https://github.com/rschmukler)
|
||||
Sacheendra talluri [@sacheendra](https://github.com/sacheendra)
|
||||
Sean DuBois [@Sean-Der](https://github.com/Sean-Der)
|
||||
Shalin LK [@shalinlk](https://github.com/shalinlk)
|
||||
Stephen Kubovic [@stephenkubovic](https://github.com/stephenkubovic)
|
||||
Stuart Warren [@Woz](https://github.com/stuart-warren)
|
||||
Sundar [@sundarv85](https://github.com/sundarv85)
|
||||
Tetsuya Morimoto [@t2y](https://github.com/t2y)
|
||||
TimeEmit [@TimeEmit](https://github.com/timeemit)
|
||||
TusharM [@tusharm](https://github.com/tusharm)
|
||||
wolfkdy [@wolfkdy](https://github.com/wolfkdy)
|
||||
zakthomas [@zakthomas](https://github.com/zakthomas)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
Please use the following questions as a guideline to help me answer
|
||||
your issue/question without further inquiry. Thank you.
|
||||
|
||||
### Which version of Elastic are you using?
|
||||
|
||||
[ ] elastic.v2 (for Elasticsearch 1.x)
|
||||
[ ] elastic.v3 (for Elasticsearch 2.x)
|
||||
|
||||
### Please describe the expected behavior
|
||||
|
||||
|
||||
### Please describe the actual behavior
|
||||
|
||||
|
||||
### Any steps to reproduce the behavior?
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
Copyright © 2012-2015 Oliver Eilhard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
# Elastic
|
||||
|
||||
Elastic is an [Elasticsearch](http://www.elasticsearch.org/) client for the
|
||||
[Go](http://www.golang.org/) programming language.
|
||||
|
||||
[](https://travis-ci.org/olivere/elastic)
|
||||
[](http://godoc.org/gopkg.in/olivere/elastic.v3)
|
||||
[](https://raw.githubusercontent.com/olivere/elastic/master/LICENSE)
|
||||
|
||||
See the [wiki](https://github.com/olivere/elastic/wiki) for additional information about Elastic.
|
||||
|
||||
|
||||
## Releases
|
||||
|
||||
**The release branches (e.g. [`release-branch.v3`](https://github.com/olivere/elastic/tree/release-branch.v3)) are actively being worked on and can break at any time. If you want to use stable versions of Elastic, please use the packages released via [gopkg.in](https://gopkg.in).**
|
||||
|
||||
Here's the version matrix:
|
||||
|
||||
Elasticsearch version | Elastic version -| Package URL
|
||||
----------------------|------------------|------------
|
||||
2.x | 3.0 | [`gopkg.in/olivere/elastic.v3`](https://gopkg.in/olivere/elastic.v3) ([source](https://github.com/olivere/elastic/tree/release-branch.v3) [doc](http://godoc.org/gopkg.in/olivere/elastic.v3))
|
||||
1.x | 2.0 | [`gopkg.in/olivere/elastic.v2`](https://gopkg.in/olivere/elastic.v2) ([source](https://github.com/olivere/elastic/tree/release-branch.v2) [doc](http://godoc.org/gopkg.in/olivere/elastic.v2))
|
||||
0.9-1.3 | 1.0 | [`gopkg.in/olivere/elastic.v1`](https://gopkg.in/olivere/elastic.v1) ([source](https://github.com/olivere/elastic/tree/release-branch.v1) [doc](http://godoc.org/gopkg.in/olivere/elastic.v1))
|
||||
|
||||
**Example:**
|
||||
|
||||
You have installed Elasticsearch 2.1.1 and want to use Elastic. As listed above, you should use Elastic 3.0. So you first install the stable release of Elastic 3.0 from gopkg.in.
|
||||
|
||||
```sh
|
||||
$ go get gopkg.in/olivere/elastic.v3
|
||||
```
|
||||
|
||||
You then import it with this import path:
|
||||
|
||||
```go
|
||||
import "gopkg.in/olivere/elastic.v3"
|
||||
```
|
||||
|
||||
### Elastic 3.0
|
||||
|
||||
Elastic 3.0 targets Elasticsearch 2.0 and later. Elasticsearch 2.0.0 was [released on 28th October 2015](https://www.elastic.co/blog/elasticsearch-2-0-0-released).
|
||||
|
||||
Notice that there are a lot of [breaking changes in Elasticsearch 2.0](https://www.elastic.co/guide/en/elasticsearch/reference/2.0/breaking-changes-2.0.html) and we used this as an opportunity to [clean up and refactor Elastic as well](https://github.com/olivere/elastic/blob/release-branch.v3/CHANGELOG-3.0.md).
|
||||
|
||||
### Elastic 2.0
|
||||
|
||||
Elastic 2.0 targets Elasticsearch 1.x and is published via [`gopkg.in/olivere/elastic.v2`](https://gopkg.in/olivere/elastic.v2).
|
||||
|
||||
### Elastic 1.0
|
||||
|
||||
Elastic 1.0 is deprecated. You should really update Elasticsearch and Elastic
|
||||
to a recent version.
|
||||
|
||||
However, if you cannot update for some reason, don't worry. Version 1.0 is
|
||||
still available. All you need to do is go-get it and change your import path
|
||||
as described above.
|
||||
|
||||
|
||||
## Status
|
||||
|
||||
We use Elastic in production since 2012. Elastic is stable but the API changes
|
||||
now and then. We strive for API compatibility.
|
||||
However, Elasticsearch sometimes introduces [breaking changes](https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes.html)
|
||||
and we sometimes have to adapt.
|
||||
|
||||
Having said that, there have been no big API changes that required you
|
||||
to rewrite your application big time. More often than not it's renaming APIs
|
||||
and adding/removing features so that Elastic is in sync with Elasticsearch.
|
||||
|
||||
Elastic has been used in production with the following Elasticsearch versions:
|
||||
0.90, 1.0-1.7. Furthermore, we use [Travis CI](https://travis-ci.org/)
|
||||
to test Elastic with the most recent versions of Elasticsearch and Go.
|
||||
See the [.travis.yml](https://github.com/olivere/elastic/blob/master/.travis.yml)
|
||||
file for the exact matrix and [Travis](https://travis-ci.org/olivere/elastic)
|
||||
for the results.
|
||||
|
||||
Elasticsearch has quite a few features. Most of them are implemented
|
||||
by Elastic. I add features and APIs as required. It's straightforward
|
||||
to implement missing pieces. I'm accepting pull requests :-)
|
||||
|
||||
Having said that, I hope you find the project useful.
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
The first thing you do is to create a [Client](https://github.com/olivere/elastic/blob/master/client.go). The client connects to Elasticsearch on `http://127.0.0.1:9200` by default.
|
||||
|
||||
You typically create one client for your app. Here's a complete example of
|
||||
creating a client, creating an index, adding a document, executing a search etc.
|
||||
|
||||
```go
|
||||
// Create a client
|
||||
client, err := elastic.NewClient()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Create an index
|
||||
_, err = client.CreateIndex("twitter").Do()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Add a document to the index
|
||||
tweet := Tweet{User: "olivere", Message: "Take Five"}
|
||||
_, err = client.Index().
|
||||
Index("twitter").
|
||||
Type("tweet").
|
||||
Id("1").
|
||||
BodyJson(tweet).
|
||||
Refresh(true).
|
||||
Do()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Search with a term query
|
||||
termQuery := elastic.NewTermQuery("user", "olivere")
|
||||
searchResult, err := client.Search().
|
||||
Index("twitter"). // search in index "twitter"
|
||||
Query(termQuery). // specify the query
|
||||
Sort("user", true). // sort by "user" field, ascending
|
||||
From(0).Size(10). // take documents 0-9
|
||||
Pretty(true). // pretty print request and response JSON
|
||||
Do() // execute
|
||||
if err != nil {
|
||||
// Handle error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// searchResult is of type SearchResult and returns hits, suggestions,
|
||||
// and all kinds of other information from Elasticsearch.
|
||||
fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)
|
||||
|
||||
// Each is a convenience function that iterates over hits in a search result.
|
||||
// It makes sure you don't need to check for nil values in the response.
|
||||
// However, it ignores errors in serialization. If you want full control
|
||||
// over iterating the hits, see below.
|
||||
var ttyp Tweet
|
||||
for _, item := range searchResult.Each(reflect.TypeOf(ttyp)) {
|
||||
if t, ok := item.(Tweet); ok {
|
||||
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
|
||||
}
|
||||
}
|
||||
// TotalHits is another convenience function that works even when something goes wrong.
|
||||
fmt.Printf("Found a total of %d tweets\n", searchResult.TotalHits())
|
||||
|
||||
// Here's how you iterate through results with full control over each step.
|
||||
if searchResult.Hits.TotalHits > 0 {
|
||||
fmt.Printf("Found a total of %d tweets\n", searchResult.Hits.TotalHits)
|
||||
|
||||
// Iterate through results
|
||||
for _, hit := range searchResult.Hits.Hits {
|
||||
// hit.Index contains the name of the index
|
||||
|
||||
// Deserialize hit.Source into a Tweet (could also be just a map[string]interface{}).
|
||||
var t Tweet
|
||||
err := json.Unmarshal(*hit.Source, &t)
|
||||
if err != nil {
|
||||
// Deserialization failed
|
||||
}
|
||||
|
||||
// Work with tweet
|
||||
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
|
||||
}
|
||||
} else {
|
||||
// No hits
|
||||
fmt.Print("Found no tweets\n")
|
||||
}
|
||||
|
||||
// Delete the index again
|
||||
_, err = client.DeleteIndex("twitter").Do()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
Here's a [link to a complete working example](https://gist.github.com/olivere/114347ff9d9cfdca7bdc0ecea8b82263).
|
||||
|
||||
See the [wiki](https://github.com/olivere/elastic/wiki) for more details.
|
||||
|
||||
|
||||
## API Status
|
||||
|
||||
### Document APIs
|
||||
|
||||
- [x] Index API
|
||||
- [x] Get API
|
||||
- [x] Delete API
|
||||
- [x] Delete By Query API
|
||||
- [x] Update API
|
||||
- [x] Update By Query API
|
||||
- [x] Multi Get API
|
||||
- [x] Bulk API
|
||||
- [x] Reindex API
|
||||
- [x] Term Vectors
|
||||
- [x] Multi termvectors API
|
||||
|
||||
### Search APIs
|
||||
|
||||
- [x] Search
|
||||
- [x] Search Template
|
||||
- [ ] Search Shards API
|
||||
- [x] Suggesters
|
||||
- [x] Term Suggester
|
||||
- [x] Phrase Suggester
|
||||
- [x] Completion Suggester
|
||||
- [x] Context Suggester
|
||||
- [x] Multi Search API
|
||||
- [x] Count API
|
||||
- [ ] Search Exists API
|
||||
- [ ] Validate API
|
||||
- [x] Explain API
|
||||
- [x] Percolator API
|
||||
- [x] Field Stats API
|
||||
|
||||
### Aggregations
|
||||
|
||||
- Metrics Aggregations
|
||||
- [x] Avg
|
||||
- [x] Cardinality
|
||||
- [x] Extended Stats
|
||||
- [x] Geo Bounds
|
||||
- [x] Max
|
||||
- [x] Min
|
||||
- [x] Percentiles
|
||||
- [x] Percentile Ranks
|
||||
- [ ] Scripted Metric
|
||||
- [x] Stats
|
||||
- [x] Sum
|
||||
- [x] Top Hits
|
||||
- [x] Value Count
|
||||
- Bucket Aggregations
|
||||
- [x] Children
|
||||
- [x] Date Histogram
|
||||
- [x] Date Range
|
||||
- [x] Filter
|
||||
- [x] Filters
|
||||
- [x] Geo Distance
|
||||
- [ ] GeoHash Grid
|
||||
- [x] Global
|
||||
- [x] Histogram
|
||||
- [x] IPv4 Range
|
||||
- [x] Missing
|
||||
- [x] Nested
|
||||
- [x] Range
|
||||
- [x] Reverse Nested
|
||||
- [x] Sampler
|
||||
- [x] Significant Terms
|
||||
- [x] Terms
|
||||
- Pipeline Aggregations
|
||||
- [x] Avg Bucket
|
||||
- [x] Derivative
|
||||
- [x] Max Bucket
|
||||
- [x] Min Bucket
|
||||
- [x] Sum Bucket
|
||||
- [x] Moving Average
|
||||
- [x] Cumulative Sum
|
||||
- [x] Bucket Script
|
||||
- [x] Bucket Selector
|
||||
- [x] Serial Differencing
|
||||
- [x] Aggregation Metadata
|
||||
|
||||
### Indices APIs
|
||||
|
||||
- [x] Create Index
|
||||
- [x] Delete Index
|
||||
- [x] Get Index
|
||||
- [x] Indices Exists
|
||||
- [x] Open / Close Index
|
||||
- [x] Put Mapping
|
||||
- [x] Get Mapping
|
||||
- [ ] Get Field Mapping
|
||||
- [ ] Types Exists
|
||||
- [x] Index Aliases
|
||||
- [x] Update Indices Settings
|
||||
- [x] Get Settings
|
||||
- [ ] Analyze
|
||||
- [x] Index Templates
|
||||
- [x] Warmers
|
||||
- [x] Indices Stats
|
||||
- [ ] Indices Segments
|
||||
- [ ] Indices Recovery
|
||||
- [ ] Clear Cache
|
||||
- [x] Flush
|
||||
- [x] Refresh
|
||||
- [x] Optimize
|
||||
- [ ] Shadow Replica Indices
|
||||
- [ ] Upgrade
|
||||
|
||||
### cat APIs
|
||||
|
||||
The cat APIs are not implemented as of now. We think they are better suited for operating with Elasticsearch on the command line.
|
||||
|
||||
- [ ] cat aliases
|
||||
- [ ] cat allocation
|
||||
- [ ] cat count
|
||||
- [ ] cat fielddata
|
||||
- [ ] cat health
|
||||
- [ ] cat indices
|
||||
- [ ] cat master
|
||||
- [ ] cat nodes
|
||||
- [ ] cat pending tasks
|
||||
- [ ] cat plugins
|
||||
- [ ] cat recovery
|
||||
- [ ] cat thread pool
|
||||
- [ ] cat shards
|
||||
- [ ] cat segments
|
||||
|
||||
### Cluster APIs
|
||||
|
||||
- [x] Cluster Health
|
||||
- [x] Cluster State
|
||||
- [x] Cluster Stats
|
||||
- [ ] Pending Cluster Tasks
|
||||
- [ ] Cluster Reroute
|
||||
- [ ] Cluster Update Settings
|
||||
- [ ] Nodes Stats
|
||||
- [x] Nodes Info
|
||||
- [x] Task Management API
|
||||
- [ ] Nodes hot_threads
|
||||
|
||||
### Query DSL
|
||||
|
||||
- [x] Match All Query
|
||||
- [x] Inner hits
|
||||
- Full text queries
|
||||
- [x] Match Query
|
||||
- [x] Multi Match Query
|
||||
- [x] Common Terms Query
|
||||
- [x] Query String Query
|
||||
- [x] Simple Query String Query
|
||||
- Term level queries
|
||||
- [x] Term Query
|
||||
- [x] Terms Query
|
||||
- [x] Range Query
|
||||
- [x] Exists Query
|
||||
- [x] Missing Query
|
||||
- [x] Prefix Query
|
||||
- [x] Wildcard Query
|
||||
- [x] Regexp Query
|
||||
- [x] Fuzzy Query
|
||||
- [x] Type Query
|
||||
- [x] Ids Query
|
||||
- Compound queries
|
||||
- [x] Constant Score Query
|
||||
- [x] Bool Query
|
||||
- [x] Dis Max Query
|
||||
- [x] Function Score Query
|
||||
- [x] Boosting Query
|
||||
- [x] Indices Query
|
||||
- [x] And Query (deprecated)
|
||||
- [x] Not Query
|
||||
- [x] Or Query (deprecated)
|
||||
- [ ] Filtered Query (deprecated)
|
||||
- [ ] Limit Query (deprecated)
|
||||
- Joining queries
|
||||
- [x] Nested Query
|
||||
- [x] Has Child Query
|
||||
- [x] Has Parent Query
|
||||
- Geo queries
|
||||
- [ ] GeoShape Query
|
||||
- [x] Geo Bounding Box Query
|
||||
- [x] Geo Distance Query
|
||||
- [ ] Geo Distance Range Query
|
||||
- [x] Geo Polygon Query
|
||||
- [ ] Geohash Cell Query
|
||||
- Specialized queries
|
||||
- [x] More Like This Query
|
||||
- [x] Template Query
|
||||
- [x] Script Query
|
||||
- Span queries
|
||||
- [ ] Span Term Query
|
||||
- [ ] Span Multi Term Query
|
||||
- [ ] Span First Query
|
||||
- [ ] Span Near Query
|
||||
- [ ] Span Or Query
|
||||
- [ ] Span Not Query
|
||||
- [ ] Span Containing Query
|
||||
- [ ] Span Within Query
|
||||
|
||||
### Modules
|
||||
|
||||
- [ ] Snapshot and Restore
|
||||
|
||||
### Sorting
|
||||
|
||||
- [x] Sort by score
|
||||
- [x] Sort by field
|
||||
- [x] Sort by geo distance
|
||||
- [x] Sort by script
|
||||
|
||||
### Scan
|
||||
|
||||
Scrolling through documents (e.g. `search_type=scan`) are implemented via
|
||||
the `Scroll` and `Scan` services. The `ClearScroll` API is implemented as well.
|
||||
|
||||
|
||||
## How to contribute
|
||||
|
||||
Read [the contribution guidelines](https://github.com/olivere/elastic/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks a lot for the great folks working hard on
|
||||
[Elasticsearch](http://www.elasticsearch.org/)
|
||||
and
|
||||
[Go](http://www.golang.org/).
|
||||
|
||||
Elastic uses portions of the
|
||||
[uritemplates](https://github.com/jtacoma/uritemplates) library
|
||||
by Joshua Tacoma and
|
||||
[backoff](https://github.com/cenkalti/backoff) by Cenk Altı.
|
||||
|
||||
## LICENSE
|
||||
|
||||
MIT-LICENSE. See [LICENSE](http://olivere.mit-license.org/)
|
||||
or the LICENSE file provided in the repository for details.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Portions of this code rely on this LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Cenk Altı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Backoff is an interface for different types of backoff algorithms.
|
||||
type Backoff interface {
|
||||
Next() time.Duration
|
||||
Reset()
|
||||
}
|
||||
|
||||
// Stop is used as a signal to indicate that no more retries should be made.
|
||||
const Stop time.Duration = -1
|
||||
|
||||
// -- Simple Backoff --
|
||||
|
||||
// SimpleBackoff takes a list of fixed values for backoff intervals.
|
||||
// Each call to Next returns the next value from that fixed list.
|
||||
// After each value is returned, subsequent calls to Next will only return
|
||||
// the last element. The caller may specify if the values are "jittered".
|
||||
type SimpleBackoff struct {
|
||||
sync.Mutex
|
||||
ticks []int
|
||||
index int
|
||||
jitter bool
|
||||
stop bool
|
||||
}
|
||||
|
||||
// NewSimpleBackoff creates a SimpleBackoff algorithm with the specified
|
||||
// list of fixed intervals in milliseconds.
|
||||
func NewSimpleBackoff(ticks ...int) *SimpleBackoff {
|
||||
return &SimpleBackoff{
|
||||
ticks: ticks,
|
||||
index: 0,
|
||||
jitter: false,
|
||||
stop: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Jitter, when set, randomizes to return a value of [0.5*value .. 1.5*value].
|
||||
func (b *SimpleBackoff) Jitter(doJitter bool) *SimpleBackoff {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.jitter = doJitter
|
||||
return b
|
||||
}
|
||||
|
||||
// SendStop, when enables, makes Next to return Stop once
|
||||
// the list of values is exhausted.
|
||||
func (b *SimpleBackoff) SendStop(doStop bool) *SimpleBackoff {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.stop = doStop
|
||||
return b
|
||||
}
|
||||
|
||||
// Next returns the next wait interval.
|
||||
func (b *SimpleBackoff) Next() time.Duration {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
i := b.index
|
||||
if i >= len(b.ticks) {
|
||||
if b.stop {
|
||||
return Stop
|
||||
}
|
||||
i = len(b.ticks) - 1
|
||||
b.index = i
|
||||
} else {
|
||||
b.index++
|
||||
}
|
||||
|
||||
ms := b.ticks[i]
|
||||
if b.jitter {
|
||||
ms = jitter(ms)
|
||||
}
|
||||
return time.Duration(ms) * time.Millisecond
|
||||
}
|
||||
|
||||
// Reset resets SimpleBackoff.
|
||||
func (b *SimpleBackoff) Reset() {
|
||||
b.Lock()
|
||||
b.index = 0
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
// jitter randomizes the interval to return a value of [0.5*millis .. 1.5*millis].
|
||||
func jitter(millis int) int {
|
||||
if millis <= 0 {
|
||||
return 0
|
||||
}
|
||||
return millis/2 + rand.Intn(millis)
|
||||
}
|
||||
|
||||
// -- Exponential --
|
||||
|
||||
// ExponentialBackoff implements the simple exponential backoff described by
|
||||
// Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html.
|
||||
type ExponentialBackoff struct {
|
||||
sync.Mutex
|
||||
t float64 // initial timeout (in msec)
|
||||
f float64 // exponential factor (e.g. 2)
|
||||
m float64 // maximum timeout (in msec)
|
||||
n int64 // number of retries
|
||||
stop bool // indicates whether Next should send "Stop" whan max timeout is reached
|
||||
}
|
||||
|
||||
// NewExponentialBackoff returns a ExponentialBackoff backoff policy.
|
||||
// Use initialTimeout to set the first/minimal interval
|
||||
// and maxTimeout to set the maximum wait interval.
|
||||
func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration) *ExponentialBackoff {
|
||||
return &ExponentialBackoff{
|
||||
t: float64(int64(initialTimeout / time.Millisecond)),
|
||||
f: 2.0,
|
||||
m: float64(int64(maxTimeout / time.Millisecond)),
|
||||
n: 0,
|
||||
stop: false,
|
||||
}
|
||||
}
|
||||
|
||||
// SendStop, when enables, makes Next to return Stop once
|
||||
// the maximum timeout is reached.
|
||||
func (b *ExponentialBackoff) SendStop(doStop bool) *ExponentialBackoff {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.stop = doStop
|
||||
return b
|
||||
}
|
||||
|
||||
// Next returns the next wait interval.
|
||||
func (t *ExponentialBackoff) Next() time.Duration {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
n := float64(atomic.AddInt64(&t.n, 1))
|
||||
r := 1.0 + rand.Float64() // random number in [1..2]
|
||||
m := math.Min(r*t.t*math.Pow(t.f, n), t.m)
|
||||
if t.stop && m >= t.m {
|
||||
return Stop
|
||||
}
|
||||
d := time.Duration(int64(m)) * time.Millisecond
|
||||
return d
|
||||
}
|
||||
|
||||
// Reset resets the backoff policy so that it can be reused.
|
||||
func (t *ExponentialBackoff) Reset() {
|
||||
t.Lock()
|
||||
t.n = 0
|
||||
t.Unlock()
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
// This file is (c) 2014 Cenk Altı and governed by the MIT license.
|
||||
// See https://github.com/cenkalti/backoff for original source.
|
||||
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
// An Operation is executing by Retry() or RetryNotify().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type Operation func() error
|
||||
|
||||
// Notify is a notify-on-error function. It receives an operation error and
|
||||
// backoff delay if the operation failed (with an error).
|
||||
//
|
||||
// NOTE that if the backoff policy stated to stop retrying,
|
||||
// the notify function isn't called.
|
||||
type Notify func(error, time.Duration)
|
||||
|
||||
// Retry the function f until it does not return error or BackOff stops.
|
||||
// f is guaranteed to be run at least once.
|
||||
// It is the caller's responsibility to reset b after Retry returns.
|
||||
//
|
||||
// Retry sleeps the goroutine for the duration returned by BackOff after a
|
||||
// failed operation returns.
|
||||
func Retry(o Operation, b Backoff) error { return RetryNotify(o, b, nil) }
|
||||
|
||||
// RetryNotify calls notify function with the error and wait duration
|
||||
// for each failed attempt before sleep.
|
||||
func RetryNotify(operation Operation, b Backoff, notify Notify) error {
|
||||
var err error
|
||||
var next time.Duration
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
if err = operation(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if next = b.Next(); next == Stop {
|
||||
return err
|
||||
}
|
||||
|
||||
if notify != nil {
|
||||
notify(err, next)
|
||||
}
|
||||
|
||||
time.Sleep(next)
|
||||
}
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// BulkService allows for batching bulk requests and sending them to
|
||||
// Elasticsearch in one roundtrip. Use the Add method with BulkIndexRequest,
|
||||
// BulkUpdateRequest, and BulkDeleteRequest to add bulk requests to a batch,
|
||||
// then use Do to send them to Elasticsearch.
|
||||
//
|
||||
// BulkService will be reset after each Do call. In other words, you can
|
||||
// reuse BulkService to send many batches. You do not have to create a new
|
||||
// BulkService for each batch.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.x/docs-bulk.html
|
||||
// for more details.
|
||||
type BulkService struct {
|
||||
client *Client
|
||||
|
||||
index string
|
||||
typ string
|
||||
requests []BulkableRequest
|
||||
timeout string
|
||||
refresh *bool
|
||||
pretty bool
|
||||
|
||||
sizeInBytes int64
|
||||
}
|
||||
|
||||
// NewBulkService initializes a new BulkService.
|
||||
func NewBulkService(client *Client) *BulkService {
|
||||
builder := &BulkService{
|
||||
client: client,
|
||||
requests: make([]BulkableRequest, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *BulkService) reset() {
|
||||
s.requests = make([]BulkableRequest, 0)
|
||||
s.sizeInBytes = 0
|
||||
}
|
||||
|
||||
// Index specifies the index to use for all batches. You may also leave
|
||||
// this blank and specify the index in the individual bulk requests.
|
||||
func (s *BulkService) Index(index string) *BulkService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type specifies the type to use for all batches. You may also leave
|
||||
// this blank and specify the type in the individual bulk requests.
|
||||
func (s *BulkService) Type(typ string) *BulkService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is a global timeout for processing bulk requests. This is a
|
||||
// server-side timeout, i.e. it tells Elasticsearch the time after which
|
||||
// it should stop processing.
|
||||
func (s *BulkService) Timeout(timeout string) *BulkService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh, when set to true, tells Elasticsearch to make the bulk requests
|
||||
// available to search immediately after being processed. Normally, this
|
||||
// only happens after a specified refresh interval.
|
||||
func (s *BulkService) Refresh(refresh bool) *BulkService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty tells Elasticsearch whether to return a formatted JSON response.
|
||||
func (s *BulkService) Pretty(pretty bool) *BulkService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds bulkable requests, i.e. BulkIndexRequest, BulkUpdateRequest,
|
||||
// and/or BulkDeleteRequest.
|
||||
func (s *BulkService) Add(requests ...BulkableRequest) *BulkService {
|
||||
for _, r := range requests {
|
||||
s.requests = append(s.requests, r)
|
||||
s.sizeInBytes += s.estimateSizeInBytes(r)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// EstimatedSizeInBytes returns the estimated size of all bulkable
|
||||
// requests added via Add.
|
||||
func (s *BulkService) EstimatedSizeInBytes() int64 {
|
||||
return s.sizeInBytes
|
||||
}
|
||||
|
||||
// estimateSizeInBytes returns the estimates size of the given
|
||||
// bulkable request, i.e. BulkIndexRequest, BulkUpdateRequest, and
|
||||
// BulkDeleteRequest.
|
||||
func (s *BulkService) estimateSizeInBytes(r BulkableRequest) int64 {
|
||||
lines, _ := r.Source()
|
||||
size := 0
|
||||
for _, line := range lines {
|
||||
// +1 for the \n
|
||||
size += len(line) + 1
|
||||
}
|
||||
return int64(size)
|
||||
}
|
||||
|
||||
// NumberOfActions returns the number of bulkable requests that need to
|
||||
// be sent to Elasticsearch on the next batch.
|
||||
func (s *BulkService) NumberOfActions() int {
|
||||
return len(s.requests)
|
||||
}
|
||||
|
||||
func (s *BulkService) bodyAsString() (string, error) {
|
||||
buf := bytes.NewBufferString("")
|
||||
|
||||
for _, req := range s.requests {
|
||||
source, err := req.Source()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, line := range source {
|
||||
_, err := buf.WriteString(fmt.Sprintf("%s\n", line))
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// Do sends the batched requests to Elasticsearch. Note that, when successful,
|
||||
// you can reuse the BulkService for the next batch as the list of bulk
|
||||
// requests is cleared on success.
|
||||
func (s *BulkService) Do() (*BulkResponse, error) {
|
||||
// No actions?
|
||||
if s.NumberOfActions() == 0 {
|
||||
return nil, errors.New("elastic: No bulk actions to commit")
|
||||
}
|
||||
|
||||
// Get body
|
||||
body, err := s.bodyAsString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build url
|
||||
path := "/"
|
||||
if s.index != "" {
|
||||
index, err := uritemplates.Expand("{index}", map[string]string{
|
||||
"index": s.index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path += index + "/"
|
||||
}
|
||||
if s.typ != "" {
|
||||
typ, err := uritemplates.Expand("{type}", map[string]string{
|
||||
"type": s.typ,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path += typ + "/"
|
||||
}
|
||||
path += "_bulk"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return results
|
||||
ret := new(BulkResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reset so the request can be reused
|
||||
s.reset()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// BulkResponse is a response to a bulk execution.
|
||||
//
|
||||
// Example:
|
||||
// {
|
||||
// "took":3,
|
||||
// "errors":false,
|
||||
// "items":[{
|
||||
// "index":{
|
||||
// "_index":"index1",
|
||||
// "_type":"tweet",
|
||||
// "_id":"1",
|
||||
// "_version":3,
|
||||
// "status":201
|
||||
// }
|
||||
// },{
|
||||
// "index":{
|
||||
// "_index":"index2",
|
||||
// "_type":"tweet",
|
||||
// "_id":"2",
|
||||
// "_version":3,
|
||||
// "status":200
|
||||
// }
|
||||
// },{
|
||||
// "delete":{
|
||||
// "_index":"index1",
|
||||
// "_type":"tweet",
|
||||
// "_id":"1",
|
||||
// "_version":4,
|
||||
// "status":200,
|
||||
// "found":true
|
||||
// }
|
||||
// },{
|
||||
// "update":{
|
||||
// "_index":"index2",
|
||||
// "_type":"tweet",
|
||||
// "_id":"2",
|
||||
// "_version":4,
|
||||
// "status":200
|
||||
// }
|
||||
// }]
|
||||
// }
|
||||
type BulkResponse struct {
|
||||
Took int `json:"took,omitempty"`
|
||||
Errors bool `json:"errors,omitempty"`
|
||||
Items []map[string]*BulkResponseItem `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// BulkResponseItem is the result of a single bulk request.
|
||||
type BulkResponseItem struct {
|
||||
Index string `json:"_index,omitempty"`
|
||||
Type string `json:"_type,omitempty"`
|
||||
Id string `json:"_id,omitempty"`
|
||||
Version int `json:"_version,omitempty"`
|
||||
Status int `json:"status,omitempty"`
|
||||
Found bool `json:"found,omitempty"`
|
||||
Error *ErrorDetails `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Indexed returns all bulk request results of "index" actions.
|
||||
func (r *BulkResponse) Indexed() []*BulkResponseItem {
|
||||
return r.ByAction("index")
|
||||
}
|
||||
|
||||
// Created returns all bulk request results of "create" actions.
|
||||
func (r *BulkResponse) Created() []*BulkResponseItem {
|
||||
return r.ByAction("create")
|
||||
}
|
||||
|
||||
// Updated returns all bulk request results of "update" actions.
|
||||
func (r *BulkResponse) Updated() []*BulkResponseItem {
|
||||
return r.ByAction("update")
|
||||
}
|
||||
|
||||
// Deleted returns all bulk request results of "delete" actions.
|
||||
func (r *BulkResponse) Deleted() []*BulkResponseItem {
|
||||
return r.ByAction("delete")
|
||||
}
|
||||
|
||||
// ByAction returns all bulk request results of a certain action,
|
||||
// e.g. "index" or "delete".
|
||||
func (r *BulkResponse) ByAction(action string) []*BulkResponseItem {
|
||||
if r.Items == nil {
|
||||
return nil
|
||||
}
|
||||
items := make([]*BulkResponseItem, 0)
|
||||
for _, item := range r.Items {
|
||||
if result, found := item[action]; found {
|
||||
items = append(items, result)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// ById returns all bulk request results of a given document id,
|
||||
// regardless of the action ("index", "delete" etc.).
|
||||
func (r *BulkResponse) ById(id string) []*BulkResponseItem {
|
||||
if r.Items == nil {
|
||||
return nil
|
||||
}
|
||||
items := make([]*BulkResponseItem, 0)
|
||||
for _, item := range r.Items {
|
||||
for _, result := range item {
|
||||
if result.Id == id {
|
||||
items = append(items, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Failed returns those items of a bulk response that have errors,
|
||||
// i.e. those that don't have a status code between 200 and 299.
|
||||
func (r *BulkResponse) Failed() []*BulkResponseItem {
|
||||
if r.Items == nil {
|
||||
return nil
|
||||
}
|
||||
errors := make([]*BulkResponseItem, 0)
|
||||
for _, item := range r.Items {
|
||||
for _, result := range item {
|
||||
if !(result.Status >= 200 && result.Status <= 299) {
|
||||
errors = append(errors, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// Succeeded returns those items of a bulk response that have no errors,
|
||||
// i.e. those have a status code between 200 and 299.
|
||||
func (r *BulkResponse) Succeeded() []*BulkResponseItem {
|
||||
if r.Items == nil {
|
||||
return nil
|
||||
}
|
||||
succeeded := make([]*BulkResponseItem, 0)
|
||||
for _, item := range r.Items {
|
||||
for _, result := range item {
|
||||
if result.Status >= 200 && result.Status <= 299 {
|
||||
succeeded = append(succeeded, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return succeeded
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- Bulk delete request --
|
||||
|
||||
// Bulk request to remove a document from Elasticsearch.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
type BulkDeleteRequest struct {
|
||||
BulkableRequest
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
parent string
|
||||
routing string
|
||||
refresh *bool
|
||||
version int64 // default is MATCH_ANY
|
||||
versionType string // default is "internal"
|
||||
|
||||
source []string
|
||||
}
|
||||
|
||||
// NewBulkDeleteRequest returns a new BulkDeleteRequest.
|
||||
func NewBulkDeleteRequest() *BulkDeleteRequest {
|
||||
return &BulkDeleteRequest{}
|
||||
}
|
||||
|
||||
// Index specifies the Elasticsearch index to use for this delete request.
|
||||
// If unspecified, the index set on the BulkService will be used.
|
||||
func (r *BulkDeleteRequest) Index(index string) *BulkDeleteRequest {
|
||||
r.index = index
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Type specifies the Elasticsearch type to use for this delete request.
|
||||
// If unspecified, the type set on the BulkService will be used.
|
||||
func (r *BulkDeleteRequest) Type(typ string) *BulkDeleteRequest {
|
||||
r.typ = typ
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Id specifies the identifier of the document to delete.
|
||||
func (r *BulkDeleteRequest) Id(id string) *BulkDeleteRequest {
|
||||
r.id = id
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Parent specifies the parent of the request, which is used in parent/child
|
||||
// mappings.
|
||||
func (r *BulkDeleteRequest) Parent(parent string) *BulkDeleteRequest {
|
||||
r.parent = parent
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Routing specifies a routing value for the request.
|
||||
func (r *BulkDeleteRequest) Routing(routing string) *BulkDeleteRequest {
|
||||
r.routing = routing
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Refresh indicates whether to update the shards immediately after
|
||||
// the delete has been processed. Deleted documents will disappear
|
||||
// in search immediately at the cost of slower bulk performance.
|
||||
func (r *BulkDeleteRequest) Refresh(refresh bool) *BulkDeleteRequest {
|
||||
r.refresh = &refresh
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Version indicates the version to be deleted as part of an optimistic
|
||||
// concurrency model.
|
||||
func (r *BulkDeleteRequest) Version(version int64) *BulkDeleteRequest {
|
||||
r.version = version
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// VersionType can be "internal" (default), "external", "external_gte",
|
||||
// "external_gt", or "force".
|
||||
func (r *BulkDeleteRequest) VersionType(versionType string) *BulkDeleteRequest {
|
||||
r.versionType = versionType
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the on-wire representation of the delete request,
|
||||
// concatenated as a single string.
|
||||
func (r *BulkDeleteRequest) String() string {
|
||||
lines, err := r.Source()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("error: %v", err)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// Source returns the on-wire representation of the delete request,
|
||||
// split into an action-and-meta-data line and an (optional) source line.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
func (r *BulkDeleteRequest) Source() ([]string, error) {
|
||||
if r.source != nil {
|
||||
return r.source, nil
|
||||
}
|
||||
lines := make([]string, 1)
|
||||
|
||||
source := make(map[string]interface{})
|
||||
deleteCommand := make(map[string]interface{})
|
||||
if r.index != "" {
|
||||
deleteCommand["_index"] = r.index
|
||||
}
|
||||
if r.typ != "" {
|
||||
deleteCommand["_type"] = r.typ
|
||||
}
|
||||
if r.id != "" {
|
||||
deleteCommand["_id"] = r.id
|
||||
}
|
||||
if r.parent != "" {
|
||||
deleteCommand["_parent"] = r.parent
|
||||
}
|
||||
if r.routing != "" {
|
||||
deleteCommand["_routing"] = r.routing
|
||||
}
|
||||
if r.version > 0 {
|
||||
deleteCommand["_version"] = r.version
|
||||
}
|
||||
if r.versionType != "" {
|
||||
deleteCommand["_version_type"] = r.versionType
|
||||
}
|
||||
if r.refresh != nil {
|
||||
deleteCommand["refresh"] = *r.refresh
|
||||
}
|
||||
source["delete"] = deleteCommand
|
||||
|
||||
body, err := json.Marshal(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines[0] = string(body)
|
||||
r.source = lines
|
||||
|
||||
return lines, nil
|
||||
}
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Bulk request to add a document to Elasticsearch.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
type BulkIndexRequest struct {
|
||||
BulkableRequest
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
opType string
|
||||
routing string
|
||||
parent string
|
||||
timestamp string
|
||||
ttl int64
|
||||
refresh *bool
|
||||
version int64 // default is MATCH_ANY
|
||||
versionType string // default is "internal"
|
||||
doc interface{}
|
||||
|
||||
source []string
|
||||
}
|
||||
|
||||
// NewBulkIndexRequest returns a new BulkIndexRequest.
|
||||
// The operation type is "index" by default.
|
||||
func NewBulkIndexRequest() *BulkIndexRequest {
|
||||
return &BulkIndexRequest{
|
||||
opType: "index",
|
||||
}
|
||||
}
|
||||
|
||||
// Index specifies the Elasticsearch index to use for this index request.
|
||||
// If unspecified, the index set on the BulkService will be used.
|
||||
func (r *BulkIndexRequest) Index(index string) *BulkIndexRequest {
|
||||
r.index = index
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Type specifies the Elasticsearch type to use for this index request.
|
||||
// If unspecified, the type set on the BulkService will be used.
|
||||
func (r *BulkIndexRequest) Type(typ string) *BulkIndexRequest {
|
||||
r.typ = typ
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Id specifies the identifier of the document to index.
|
||||
func (r *BulkIndexRequest) Id(id string) *BulkIndexRequest {
|
||||
r.id = id
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// OpType specifies if this request should follow create-only or upsert
|
||||
// behavior. This follows the OpType of the standard document index API.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#operation-type
|
||||
// for details.
|
||||
func (r *BulkIndexRequest) OpType(opType string) *BulkIndexRequest {
|
||||
r.opType = opType
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Routing specifies a routing value for the request.
|
||||
func (r *BulkIndexRequest) Routing(routing string) *BulkIndexRequest {
|
||||
r.routing = routing
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Parent specifies the identifier of the parent document (if available).
|
||||
func (r *BulkIndexRequest) Parent(parent string) *BulkIndexRequest {
|
||||
r.parent = parent
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Timestamp can be used to index a document with a timestamp.
|
||||
// This is deprecated as of 2.0.0-beta2; you should use a normal date field
|
||||
// and set its value explicitly.
|
||||
func (r *BulkIndexRequest) Timestamp(timestamp string) *BulkIndexRequest {
|
||||
r.timestamp = timestamp
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Ttl (time to live) sets an expiration date for the document. Expired
|
||||
// documents will be expunged automatically.
|
||||
// This is deprecated as of 2.0.0-beta2 and will be replaced by a different
|
||||
// implementation in a future version.
|
||||
func (r *BulkIndexRequest) Ttl(ttl int64) *BulkIndexRequest {
|
||||
r.ttl = ttl
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Refresh indicates whether to update the shards immediately after
|
||||
// the request has been processed. Newly added documents will appear
|
||||
// in search immediately at the cost of slower bulk performance.
|
||||
func (r *BulkIndexRequest) Refresh(refresh bool) *BulkIndexRequest {
|
||||
r.refresh = &refresh
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Version indicates the version of the document as part of an optimistic
|
||||
// concurrency model.
|
||||
func (r *BulkIndexRequest) Version(version int64) *BulkIndexRequest {
|
||||
r.version = version
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// VersionType specifies how versions are created. It can be e.g. internal,
|
||||
// external, external_gte, or force.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#index-versioning
|
||||
// for details.
|
||||
func (r *BulkIndexRequest) VersionType(versionType string) *BulkIndexRequest {
|
||||
r.versionType = versionType
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Doc specifies the document to index.
|
||||
func (r *BulkIndexRequest) Doc(doc interface{}) *BulkIndexRequest {
|
||||
r.doc = doc
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the on-wire representation of the index request,
|
||||
// concatenated as a single string.
|
||||
func (r *BulkIndexRequest) String() string {
|
||||
lines, err := r.Source()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("error: %v", err)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// Source returns the on-wire representation of the index request,
|
||||
// split into an action-and-meta-data line and an (optional) source line.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
func (r *BulkIndexRequest) Source() ([]string, error) {
|
||||
// { "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } }
|
||||
// { "field1" : "value1" }
|
||||
|
||||
if r.source != nil {
|
||||
return r.source, nil
|
||||
}
|
||||
|
||||
lines := make([]string, 2)
|
||||
|
||||
// "index" ...
|
||||
command := make(map[string]interface{})
|
||||
indexCommand := make(map[string]interface{})
|
||||
if r.index != "" {
|
||||
indexCommand["_index"] = r.index
|
||||
}
|
||||
if r.typ != "" {
|
||||
indexCommand["_type"] = r.typ
|
||||
}
|
||||
if r.id != "" {
|
||||
indexCommand["_id"] = r.id
|
||||
}
|
||||
if r.routing != "" {
|
||||
indexCommand["_routing"] = r.routing
|
||||
}
|
||||
if r.parent != "" {
|
||||
indexCommand["_parent"] = r.parent
|
||||
}
|
||||
if r.timestamp != "" {
|
||||
indexCommand["_timestamp"] = r.timestamp
|
||||
}
|
||||
if r.ttl > 0 {
|
||||
indexCommand["_ttl"] = r.ttl
|
||||
}
|
||||
if r.version > 0 {
|
||||
indexCommand["_version"] = r.version
|
||||
}
|
||||
if r.versionType != "" {
|
||||
indexCommand["_version_type"] = r.versionType
|
||||
}
|
||||
if r.refresh != nil {
|
||||
indexCommand["refresh"] = *r.refresh
|
||||
}
|
||||
command[r.opType] = indexCommand
|
||||
line, err := json.Marshal(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines[0] = string(line)
|
||||
|
||||
// "field1" ...
|
||||
if r.doc != nil {
|
||||
switch t := r.doc.(type) {
|
||||
default:
|
||||
body, err := json.Marshal(r.doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines[1] = string(body)
|
||||
case json.RawMessage:
|
||||
lines[1] = string(t)
|
||||
case *json.RawMessage:
|
||||
lines[1] = string(*t)
|
||||
case string:
|
||||
lines[1] = t
|
||||
case *string:
|
||||
lines[1] = *t
|
||||
}
|
||||
} else {
|
||||
lines[1] = "{}"
|
||||
}
|
||||
|
||||
r.source = lines
|
||||
return lines, nil
|
||||
}
|
||||
+541
@@ -0,0 +1,541 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/backoff"
|
||||
)
|
||||
|
||||
// BulkProcessorService allows to easily process bulk requests. It allows setting
|
||||
// policies when to flush new bulk requests, e.g. based on a number of actions,
|
||||
// on the size of the actions, and/or to flush periodically. It also allows
|
||||
// to control the number of concurrent bulk requests allowed to be executed
|
||||
// in parallel.
|
||||
//
|
||||
// BulkProcessorService, by default, commits either every 1000 requests or when the
|
||||
// (estimated) size of the bulk requests exceeds 5 MB. However, it does not
|
||||
// commit periodically. BulkProcessorService also does retry by default, using
|
||||
// an exponential backoff algorithm.
|
||||
//
|
||||
// The caller is responsible for setting the index and type on every
|
||||
// bulk request added to BulkProcessorService.
|
||||
//
|
||||
// BulkProcessorService takes ideas from the BulkProcessor of the
|
||||
// Elasticsearch Java API as documented in
|
||||
// https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-docs-bulk-processor.html.
|
||||
type BulkProcessorService struct {
|
||||
c *Client
|
||||
beforeFn BulkBeforeFunc
|
||||
afterFn BulkAfterFunc
|
||||
name string // name of processor
|
||||
numWorkers int // # of workers (>= 1)
|
||||
bulkActions int // # of requests after which to commit
|
||||
bulkSize int // # of bytes after which to commit
|
||||
flushInterval time.Duration // periodic flush interval
|
||||
wantStats bool // indicates whether to gather statistics
|
||||
initialTimeout time.Duration // initial wait time before retry on errors
|
||||
maxTimeout time.Duration // max time to wait for retry on errors
|
||||
}
|
||||
|
||||
// NewBulkProcessorService creates a new BulkProcessorService.
|
||||
func NewBulkProcessorService(client *Client) *BulkProcessorService {
|
||||
return &BulkProcessorService{
|
||||
c: client,
|
||||
numWorkers: 1,
|
||||
bulkActions: 1000,
|
||||
bulkSize: 5 << 20, // 5 MB
|
||||
initialTimeout: time.Duration(200) * time.Millisecond,
|
||||
maxTimeout: time.Duration(10000) * time.Millisecond,
|
||||
}
|
||||
}
|
||||
|
||||
// BulkBeforeFunc defines the signature of callbacks that are executed
|
||||
// before a commit to Elasticsearch.
|
||||
type BulkBeforeFunc func(executionId int64, requests []BulkableRequest)
|
||||
|
||||
// BulkAfterFunc defines the signature of callbacks that are executed
|
||||
// after a commit to Elasticsearch. The err parameter signals an error.
|
||||
type BulkAfterFunc func(executionId int64, requests []BulkableRequest, response *BulkResponse, err error)
|
||||
|
||||
// Before specifies a function to be executed before bulk requests get comitted
|
||||
// to Elasticsearch.
|
||||
func (s *BulkProcessorService) Before(fn BulkBeforeFunc) *BulkProcessorService {
|
||||
s.beforeFn = fn
|
||||
return s
|
||||
}
|
||||
|
||||
// After specifies a function to be executed when bulk requests have been
|
||||
// comitted to Elasticsearch. The After callback executes both when the
|
||||
// commit was successful as well as on failures.
|
||||
func (s *BulkProcessorService) After(fn BulkAfterFunc) *BulkProcessorService {
|
||||
s.afterFn = fn
|
||||
return s
|
||||
}
|
||||
|
||||
// Name is an optional name to identify this bulk processor.
|
||||
func (s *BulkProcessorService) Name(name string) *BulkProcessorService {
|
||||
s.name = name
|
||||
return s
|
||||
}
|
||||
|
||||
// Workers is the number of concurrent workers allowed to be
|
||||
// executed. Defaults to 1 and must be greater or equal to 1.
|
||||
func (s *BulkProcessorService) Workers(num int) *BulkProcessorService {
|
||||
s.numWorkers = num
|
||||
return s
|
||||
}
|
||||
|
||||
// BulkActions specifies when to flush based on the number of actions
|
||||
// currently added. Defaults to 1000 and can be set to -1 to be disabled.
|
||||
func (s *BulkProcessorService) BulkActions(bulkActions int) *BulkProcessorService {
|
||||
s.bulkActions = bulkActions
|
||||
return s
|
||||
}
|
||||
|
||||
// BulkSize specifies when to flush based on the size (in bytes) of the actions
|
||||
// currently added. Defaults to 5 MB and can be set to -1 to be disabled.
|
||||
func (s *BulkProcessorService) BulkSize(bulkSize int) *BulkProcessorService {
|
||||
s.bulkSize = bulkSize
|
||||
return s
|
||||
}
|
||||
|
||||
// FlushInterval specifies when to flush at the end of the given interval.
|
||||
// This is disabled by default. If you want the bulk processor to
|
||||
// operate completely asynchronously, set both BulkActions and BulkSize to
|
||||
// -1 and set the FlushInterval to a meaningful interval.
|
||||
func (s *BulkProcessorService) FlushInterval(interval time.Duration) *BulkProcessorService {
|
||||
s.flushInterval = interval
|
||||
return s
|
||||
}
|
||||
|
||||
// Stats tells bulk processor to gather stats while running.
|
||||
// Use Stats to return the stats. This is disabled by default.
|
||||
func (s *BulkProcessorService) Stats(wantStats bool) *BulkProcessorService {
|
||||
s.wantStats = wantStats
|
||||
return s
|
||||
}
|
||||
|
||||
// Do creates a new BulkProcessor and starts it.
|
||||
// Consider the BulkProcessor as a running instance that accepts bulk requests
|
||||
// and commits them to Elasticsearch, spreading the work across one or more
|
||||
// workers.
|
||||
//
|
||||
// You can interoperate with the BulkProcessor returned by Do, e.g. Start and
|
||||
// Stop (or Close) it.
|
||||
//
|
||||
// Calling Do several times returns new BulkProcessors. You probably don't
|
||||
// want to do this. BulkProcessorService implements just a builder pattern.
|
||||
func (s *BulkProcessorService) Do() (*BulkProcessor, error) {
|
||||
p := newBulkProcessor(
|
||||
s.c,
|
||||
s.beforeFn,
|
||||
s.afterFn,
|
||||
s.name,
|
||||
s.numWorkers,
|
||||
s.bulkActions,
|
||||
s.bulkSize,
|
||||
s.flushInterval,
|
||||
s.wantStats,
|
||||
s.initialTimeout,
|
||||
s.maxTimeout)
|
||||
|
||||
err := p.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// -- Bulk Processor Statistics --
|
||||
|
||||
// BulkProcessorStats contains various statistics of a bulk processor
|
||||
// while it is running. Use the Stats func to return it while running.
|
||||
type BulkProcessorStats struct {
|
||||
Flushed int64 // number of times the flush interval has been invoked
|
||||
Committed int64 // # of times workers committed bulk requests
|
||||
Indexed int64 // # of requests indexed
|
||||
Created int64 // # of requests that ES reported as creates (201)
|
||||
Updated int64 // # of requests that ES reported as updates
|
||||
Deleted int64 // # of requests that ES reported as deletes
|
||||
Succeeded int64 // # of requests that ES reported as successful
|
||||
Failed int64 // # of requests that ES reported as failed
|
||||
|
||||
Workers []*BulkProcessorWorkerStats // stats for each worker
|
||||
}
|
||||
|
||||
// BulkProcessorWorkerStats represents per-worker statistics.
|
||||
type BulkProcessorWorkerStats struct {
|
||||
Queued int64 // # of requests queued in this worker
|
||||
LastDuration time.Duration // duration of last commit
|
||||
}
|
||||
|
||||
// newBulkProcessorStats initializes and returns a BulkProcessorStats struct.
|
||||
func newBulkProcessorStats(workers int) *BulkProcessorStats {
|
||||
stats := &BulkProcessorStats{
|
||||
Workers: make([]*BulkProcessorWorkerStats, workers),
|
||||
}
|
||||
for i := 0; i < workers; i++ {
|
||||
stats.Workers[i] = &BulkProcessorWorkerStats{}
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
func (st *BulkProcessorStats) dup() *BulkProcessorStats {
|
||||
dst := new(BulkProcessorStats)
|
||||
dst.Flushed = st.Flushed
|
||||
dst.Committed = st.Committed
|
||||
dst.Indexed = st.Indexed
|
||||
dst.Created = st.Created
|
||||
dst.Updated = st.Updated
|
||||
dst.Deleted = st.Deleted
|
||||
dst.Succeeded = st.Succeeded
|
||||
dst.Failed = st.Failed
|
||||
for _, src := range st.Workers {
|
||||
dst.Workers = append(dst.Workers, src.dup())
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (st *BulkProcessorWorkerStats) dup() *BulkProcessorWorkerStats {
|
||||
dst := new(BulkProcessorWorkerStats)
|
||||
dst.Queued = st.Queued
|
||||
dst.LastDuration = st.LastDuration
|
||||
return dst
|
||||
}
|
||||
|
||||
// -- Bulk Processor --
|
||||
|
||||
// BulkProcessor encapsulates a task that accepts bulk requests and
|
||||
// orchestrates committing them to Elasticsearch via one or more workers.
|
||||
//
|
||||
// BulkProcessor is returned by setting up a BulkProcessorService and
|
||||
// calling the Do method.
|
||||
type BulkProcessor struct {
|
||||
c *Client
|
||||
beforeFn BulkBeforeFunc
|
||||
afterFn BulkAfterFunc
|
||||
name string
|
||||
bulkActions int
|
||||
bulkSize int
|
||||
numWorkers int
|
||||
executionId int64
|
||||
requestsC chan BulkableRequest
|
||||
workerWg sync.WaitGroup
|
||||
workers []*bulkWorker
|
||||
flushInterval time.Duration
|
||||
flusherStopC chan struct{}
|
||||
wantStats bool
|
||||
initialTimeout time.Duration // initial wait time before retry on errors
|
||||
maxTimeout time.Duration // max time to wait for retry on errors
|
||||
|
||||
startedMu sync.Mutex // guards the following block
|
||||
started bool
|
||||
|
||||
statsMu sync.Mutex // guards the following block
|
||||
stats *BulkProcessorStats
|
||||
}
|
||||
|
||||
func newBulkProcessor(
|
||||
client *Client,
|
||||
beforeFn BulkBeforeFunc,
|
||||
afterFn BulkAfterFunc,
|
||||
name string,
|
||||
numWorkers int,
|
||||
bulkActions int,
|
||||
bulkSize int,
|
||||
flushInterval time.Duration,
|
||||
wantStats bool,
|
||||
initialTimeout time.Duration,
|
||||
maxTimeout time.Duration) *BulkProcessor {
|
||||
return &BulkProcessor{
|
||||
c: client,
|
||||
beforeFn: beforeFn,
|
||||
afterFn: afterFn,
|
||||
name: name,
|
||||
numWorkers: numWorkers,
|
||||
bulkActions: bulkActions,
|
||||
bulkSize: bulkSize,
|
||||
flushInterval: flushInterval,
|
||||
wantStats: wantStats,
|
||||
initialTimeout: initialTimeout,
|
||||
maxTimeout: maxTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the bulk processor. If the processor is already started,
|
||||
// nil is returned.
|
||||
func (p *BulkProcessor) Start() error {
|
||||
p.startedMu.Lock()
|
||||
defer p.startedMu.Unlock()
|
||||
|
||||
if p.started {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We must have at least one worker.
|
||||
if p.numWorkers < 1 {
|
||||
p.numWorkers = 1
|
||||
}
|
||||
|
||||
p.requestsC = make(chan BulkableRequest)
|
||||
p.executionId = 0
|
||||
p.stats = newBulkProcessorStats(p.numWorkers)
|
||||
|
||||
// Create and start up workers.
|
||||
p.workers = make([]*bulkWorker, p.numWorkers)
|
||||
for i := 0; i < p.numWorkers; i++ {
|
||||
p.workerWg.Add(1)
|
||||
p.workers[i] = newBulkWorker(p, i)
|
||||
go p.workers[i].work()
|
||||
}
|
||||
|
||||
// Start the ticker for flush (if enabled)
|
||||
if int64(p.flushInterval) > 0 {
|
||||
p.flusherStopC = make(chan struct{})
|
||||
go p.flusher(p.flushInterval)
|
||||
}
|
||||
|
||||
p.started = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop is an alias for Close.
|
||||
func (p *BulkProcessor) Stop() error {
|
||||
return p.Close()
|
||||
}
|
||||
|
||||
// Close stops the bulk processor previously started with Do.
|
||||
// If it is already stopped, this is a no-op and nil is returned.
|
||||
//
|
||||
// By implementing Close, BulkProcessor implements the io.Closer interface.
|
||||
func (p *BulkProcessor) Close() error {
|
||||
p.startedMu.Lock()
|
||||
defer p.startedMu.Unlock()
|
||||
|
||||
// Already stopped? Do nothing.
|
||||
if !p.started {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop flusher (if enabled)
|
||||
if p.flusherStopC != nil {
|
||||
p.flusherStopC <- struct{}{}
|
||||
<-p.flusherStopC
|
||||
close(p.flusherStopC)
|
||||
p.flusherStopC = nil
|
||||
}
|
||||
|
||||
// Stop all workers.
|
||||
close(p.requestsC)
|
||||
p.workerWg.Wait()
|
||||
|
||||
p.started = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stats returns the latest bulk processor statistics.
|
||||
// Collecting stats must be enabled first by calling Stats(true) on
|
||||
// the service that created this processor.
|
||||
func (p *BulkProcessor) Stats() BulkProcessorStats {
|
||||
p.statsMu.Lock()
|
||||
defer p.statsMu.Unlock()
|
||||
return *p.stats.dup()
|
||||
}
|
||||
|
||||
// Add adds a single request to commit by the BulkProcessorService.
|
||||
//
|
||||
// The caller is responsible for setting the index and type on the request.
|
||||
func (p *BulkProcessor) Add(request BulkableRequest) {
|
||||
p.requestsC <- request
|
||||
}
|
||||
|
||||
// Flush manually asks all workers to commit their outstanding requests.
|
||||
// It returns only when all workers acknowledge completion.
|
||||
func (p *BulkProcessor) Flush() error {
|
||||
p.statsMu.Lock()
|
||||
p.stats.Flushed++
|
||||
p.statsMu.Unlock()
|
||||
|
||||
for _, w := range p.workers {
|
||||
w.flushC <- struct{}{}
|
||||
<-w.flushAckC // wait for completion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// flusher is a single goroutine that periodically asks all workers to
|
||||
// commit their outstanding bulk requests. It is only started if
|
||||
// FlushInterval is greater than 0.
|
||||
func (p *BulkProcessor) flusher(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C: // Periodic flush
|
||||
p.Flush() // TODO swallow errors here?
|
||||
|
||||
case <-p.flusherStopC:
|
||||
p.flusherStopC <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Bulk Worker --
|
||||
|
||||
// bulkWorker encapsulates a single worker, running in a goroutine,
|
||||
// receiving bulk requests and eventually committing them to Elasticsearch.
|
||||
// It is strongly bound to a BulkProcessor.
|
||||
type bulkWorker struct {
|
||||
p *BulkProcessor
|
||||
i int
|
||||
bulkActions int
|
||||
bulkSize int
|
||||
service *BulkService
|
||||
flushC chan struct{}
|
||||
flushAckC chan struct{}
|
||||
}
|
||||
|
||||
// newBulkWorker creates a new bulkWorker instance.
|
||||
func newBulkWorker(p *BulkProcessor, i int) *bulkWorker {
|
||||
return &bulkWorker{
|
||||
p: p,
|
||||
i: i,
|
||||
bulkActions: p.bulkActions,
|
||||
bulkSize: p.bulkSize,
|
||||
service: NewBulkService(p.c),
|
||||
flushC: make(chan struct{}),
|
||||
flushAckC: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// work waits for bulk requests and manual flush calls on the respective
|
||||
// channels and is invoked as a goroutine when the bulk processor is started.
|
||||
func (w *bulkWorker) work() {
|
||||
defer func() {
|
||||
w.p.workerWg.Done()
|
||||
close(w.flushAckC)
|
||||
close(w.flushC)
|
||||
}()
|
||||
|
||||
var stop bool
|
||||
for !stop {
|
||||
select {
|
||||
case req, open := <-w.p.requestsC:
|
||||
if open {
|
||||
// Received a new request
|
||||
w.service.Add(req)
|
||||
if w.commitRequired() {
|
||||
w.commit() // TODO swallow errors here?
|
||||
}
|
||||
} else {
|
||||
// Channel closed: Stop.
|
||||
stop = true
|
||||
if w.service.NumberOfActions() > 0 {
|
||||
w.commit() // TODO swallow errors here?
|
||||
}
|
||||
}
|
||||
|
||||
case <-w.flushC:
|
||||
// Commit outstanding requests
|
||||
if w.service.NumberOfActions() > 0 {
|
||||
w.commit() // TODO swallow errors here?
|
||||
}
|
||||
w.flushAckC <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// commit commits the bulk requests in the given service,
|
||||
// invoking callbacks as specified.
|
||||
func (w *bulkWorker) commit() error {
|
||||
var res *BulkResponse
|
||||
|
||||
// commitFunc will commit bulk requests and, on failure, be retried
|
||||
// via exponential backoff
|
||||
commitFunc := func() error {
|
||||
var err error
|
||||
res, err = w.service.Do()
|
||||
return err
|
||||
}
|
||||
// notifyFunc will be called if retry fails
|
||||
notifyFunc := func(err error, d time.Duration) {
|
||||
w.p.c.errorf("elastic: bulk processor %q failed but will retry in %v: %v", w.p.name, d, err)
|
||||
}
|
||||
|
||||
id := atomic.AddInt64(&w.p.executionId, 1)
|
||||
|
||||
// Update # documents in queue before eventual retries
|
||||
w.p.statsMu.Lock()
|
||||
if w.p.wantStats {
|
||||
w.p.stats.Workers[w.i].Queued = int64(len(w.service.requests))
|
||||
}
|
||||
w.p.statsMu.Unlock()
|
||||
|
||||
// Save requests because they will be reset in commitFunc
|
||||
reqs := w.service.requests
|
||||
|
||||
// Invoke before callback
|
||||
if w.p.beforeFn != nil {
|
||||
w.p.beforeFn(id, reqs)
|
||||
}
|
||||
|
||||
// Commit bulk requests
|
||||
policy := backoff.NewExponentialBackoff(w.p.initialTimeout, w.p.maxTimeout).SendStop(true)
|
||||
err := backoff.RetryNotify(commitFunc, policy, notifyFunc)
|
||||
w.updateStats(res)
|
||||
if err != nil {
|
||||
w.p.c.errorf("elastic: bulk processor %q failed: %v", w.p.name, err)
|
||||
}
|
||||
|
||||
// Invoke after callback
|
||||
if w.p.afterFn != nil {
|
||||
w.p.afterFn(id, reqs, res, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *bulkWorker) updateStats(res *BulkResponse) {
|
||||
// Update stats
|
||||
if res != nil {
|
||||
w.p.statsMu.Lock()
|
||||
if w.p.wantStats {
|
||||
w.p.stats.Committed++
|
||||
if res != nil {
|
||||
w.p.stats.Indexed += int64(len(res.Indexed()))
|
||||
w.p.stats.Created += int64(len(res.Created()))
|
||||
w.p.stats.Updated += int64(len(res.Updated()))
|
||||
w.p.stats.Deleted += int64(len(res.Deleted()))
|
||||
w.p.stats.Succeeded += int64(len(res.Succeeded()))
|
||||
w.p.stats.Failed += int64(len(res.Failed()))
|
||||
}
|
||||
w.p.stats.Workers[w.i].Queued = int64(len(w.service.requests))
|
||||
w.p.stats.Workers[w.i].LastDuration = time.Duration(int64(res.Took)) * time.Millisecond
|
||||
}
|
||||
w.p.statsMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// commitRequired returns true if the service has to commit its
|
||||
// bulk requests. This can be either because the number of actions
|
||||
// or the estimated size in bytes is larger than specified in the
|
||||
// BulkProcessorService.
|
||||
func (w *bulkWorker) commitRequired() bool {
|
||||
if w.bulkActions >= 0 && w.service.NumberOfActions() >= w.bulkActions {
|
||||
return true
|
||||
}
|
||||
if w.bulkSize >= 0 && w.service.EstimatedSizeInBytes() >= int64(w.bulkSize) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// -- Bulkable request (index/update/delete) --
|
||||
|
||||
// Generic interface to bulkable requests.
|
||||
type BulkableRequest interface {
|
||||
fmt.Stringer
|
||||
Source() ([]string, error)
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Bulk request to update a document in Elasticsearch.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
type BulkUpdateRequest struct {
|
||||
BulkableRequest
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
|
||||
routing string
|
||||
parent string
|
||||
script *Script
|
||||
version int64 // default is MATCH_ANY
|
||||
versionType string // default is "internal"
|
||||
retryOnConflict *int
|
||||
refresh *bool
|
||||
upsert interface{}
|
||||
docAsUpsert *bool
|
||||
doc interface{}
|
||||
ttl int64
|
||||
timestamp string
|
||||
|
||||
source []string
|
||||
}
|
||||
|
||||
// NewBulkUpdateRequest returns a new BulkUpdateRequest.
|
||||
func NewBulkUpdateRequest() *BulkUpdateRequest {
|
||||
return &BulkUpdateRequest{}
|
||||
}
|
||||
|
||||
// Index specifies the Elasticsearch index to use for this update request.
|
||||
// If unspecified, the index set on the BulkService will be used.
|
||||
func (r *BulkUpdateRequest) Index(index string) *BulkUpdateRequest {
|
||||
r.index = index
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Type specifies the Elasticsearch type to use for this update request.
|
||||
// If unspecified, the type set on the BulkService will be used.
|
||||
func (r *BulkUpdateRequest) Type(typ string) *BulkUpdateRequest {
|
||||
r.typ = typ
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Id specifies the identifier of the document to update.
|
||||
func (r *BulkUpdateRequest) Id(id string) *BulkUpdateRequest {
|
||||
r.id = id
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Routing specifies a routing value for the request.
|
||||
func (r *BulkUpdateRequest) Routing(routing string) *BulkUpdateRequest {
|
||||
r.routing = routing
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Parent specifies the identifier of the parent document (if available).
|
||||
func (r *BulkUpdateRequest) Parent(parent string) *BulkUpdateRequest {
|
||||
r.parent = parent
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Script specifies an update script.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.x/docs-bulk.html#bulk-update
|
||||
// and https://www.elastic.co/guide/en/elasticsearch/reference/2.x/modules-scripting.html
|
||||
// for details.
|
||||
func (r *BulkUpdateRequest) Script(script *Script) *BulkUpdateRequest {
|
||||
r.script = script
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// RetryOnConflict specifies how often to retry in case of a version conflict.
|
||||
func (r *BulkUpdateRequest) RetryOnConflict(retryOnConflict int) *BulkUpdateRequest {
|
||||
r.retryOnConflict = &retryOnConflict
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Version indicates the version of the document as part of an optimistic
|
||||
// concurrency model.
|
||||
func (r *BulkUpdateRequest) Version(version int64) *BulkUpdateRequest {
|
||||
r.version = version
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// VersionType can be "internal" (default), "external", "external_gte",
|
||||
// "external_gt", or "force".
|
||||
func (r *BulkUpdateRequest) VersionType(versionType string) *BulkUpdateRequest {
|
||||
r.versionType = versionType
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Refresh indicates whether to update the shards immediately after
|
||||
// the request has been processed. Updated documents will appear
|
||||
// in search immediately at the cost of slower bulk performance.
|
||||
func (r *BulkUpdateRequest) Refresh(refresh bool) *BulkUpdateRequest {
|
||||
r.refresh = &refresh
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Doc specifies the updated document.
|
||||
func (r *BulkUpdateRequest) Doc(doc interface{}) *BulkUpdateRequest {
|
||||
r.doc = doc
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// DocAsUpsert indicates whether the contents of Doc should be used as
|
||||
// the Upsert value.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.x/docs-update.html#_literal_doc_as_upsert_literal
|
||||
// for details.
|
||||
func (r *BulkUpdateRequest) DocAsUpsert(docAsUpsert bool) *BulkUpdateRequest {
|
||||
r.docAsUpsert = &docAsUpsert
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Upsert specifies the document to use for upserts. It will be used for
|
||||
// create if the original document does not exist.
|
||||
func (r *BulkUpdateRequest) Upsert(doc interface{}) *BulkUpdateRequest {
|
||||
r.upsert = doc
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Ttl specifies the time to live, and optional expiry time.
|
||||
// This is deprecated as of 2.0.0-beta2.
|
||||
func (r *BulkUpdateRequest) Ttl(ttl int64) *BulkUpdateRequest {
|
||||
r.ttl = ttl
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Timestamp specifies a timestamp for the document.
|
||||
// This is deprecated as of 2.0.0-beta2.
|
||||
func (r *BulkUpdateRequest) Timestamp(timestamp string) *BulkUpdateRequest {
|
||||
r.timestamp = timestamp
|
||||
r.source = nil
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the on-wire representation of the update request,
|
||||
// concatenated as a single string.
|
||||
func (r *BulkUpdateRequest) String() string {
|
||||
lines, err := r.Source()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("error: %v", err)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (r *BulkUpdateRequest) getSourceAsString(data interface{}) (string, error) {
|
||||
switch t := data.(type) {
|
||||
default:
|
||||
body, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
case json.RawMessage:
|
||||
return string(t), nil
|
||||
case *json.RawMessage:
|
||||
return string(*t), nil
|
||||
case string:
|
||||
return t, nil
|
||||
case *string:
|
||||
return *t, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Source returns the on-wire representation of the update request,
|
||||
// split into an action-and-meta-data line and an (optional) source line.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html
|
||||
// for details.
|
||||
func (r BulkUpdateRequest) Source() ([]string, error) {
|
||||
// { "update" : { "_index" : "test", "_type" : "type1", "_id" : "1", ... } }
|
||||
// { "doc" : { "field1" : "value1", ... } }
|
||||
// or
|
||||
// { "update" : { "_index" : "test", "_type" : "type1", "_id" : "1", ... } }
|
||||
// { "script" : { ... } }
|
||||
|
||||
if r.source != nil {
|
||||
return r.source, nil
|
||||
}
|
||||
|
||||
lines := make([]string, 2)
|
||||
|
||||
// "update" ...
|
||||
command := make(map[string]interface{})
|
||||
updateCommand := make(map[string]interface{})
|
||||
if r.index != "" {
|
||||
updateCommand["_index"] = r.index
|
||||
}
|
||||
if r.typ != "" {
|
||||
updateCommand["_type"] = r.typ
|
||||
}
|
||||
if r.id != "" {
|
||||
updateCommand["_id"] = r.id
|
||||
}
|
||||
if r.routing != "" {
|
||||
updateCommand["_routing"] = r.routing
|
||||
}
|
||||
if r.parent != "" {
|
||||
updateCommand["_parent"] = r.parent
|
||||
}
|
||||
if r.timestamp != "" {
|
||||
updateCommand["_timestamp"] = r.timestamp
|
||||
}
|
||||
if r.ttl > 0 {
|
||||
updateCommand["_ttl"] = r.ttl
|
||||
}
|
||||
if r.version > 0 {
|
||||
updateCommand["_version"] = r.version
|
||||
}
|
||||
if r.versionType != "" {
|
||||
updateCommand["_version_type"] = r.versionType
|
||||
}
|
||||
if r.refresh != nil {
|
||||
updateCommand["refresh"] = *r.refresh
|
||||
}
|
||||
if r.retryOnConflict != nil {
|
||||
updateCommand["_retry_on_conflict"] = *r.retryOnConflict
|
||||
}
|
||||
command["update"] = updateCommand
|
||||
line, err := json.Marshal(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines[0] = string(line)
|
||||
|
||||
// 2nd line: {"doc" : { ... }} or {"script": {...}}
|
||||
source := make(map[string]interface{})
|
||||
if r.docAsUpsert != nil {
|
||||
source["doc_as_upsert"] = *r.docAsUpsert
|
||||
}
|
||||
if r.upsert != nil {
|
||||
source["upsert"] = r.upsert
|
||||
}
|
||||
if r.doc != nil {
|
||||
// {"doc":{...}}
|
||||
source["doc"] = r.doc
|
||||
} else if r.script != nil {
|
||||
// {"script":...}
|
||||
src, err := r.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["script"] = src
|
||||
}
|
||||
lines[1], err = r.getSourceAsString(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.source = lines
|
||||
return lines, nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import "net/url"
|
||||
|
||||
// canonicalize takes a list of URLs and returns its canonicalized form, i.e.
|
||||
// remove anything but scheme, userinfo, host, path, and port.
|
||||
// It also removes all trailing slashes. It also skips invalid URLs or
|
||||
// URLs that do not use protocol http or https.
|
||||
//
|
||||
// Example:
|
||||
// http://127.0.0.1:9200/?query=1 -> http://127.0.0.1:9200
|
||||
// http://127.0.0.1:9200/db1/ -> http://127.0.0.1:9200/db1
|
||||
// 127.0.0.1:9200 -> http://127.0.0.1:9200
|
||||
func canonicalize(rawurls ...string) []string {
|
||||
var canonicalized []string
|
||||
for _, rawurl := range rawurls {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err == nil {
|
||||
if len(u.Scheme) == 0 {
|
||||
u.Scheme = DefaultScheme
|
||||
}
|
||||
if u.Scheme == "http" || u.Scheme == "https" {
|
||||
// Trim trailing slashes
|
||||
for len(u.Path) > 0 && u.Path[len(u.Path)-1] == '/' {
|
||||
u.Path = u.Path[0 : len(u.Path)-1]
|
||||
}
|
||||
u.Fragment = ""
|
||||
u.RawQuery = ""
|
||||
canonicalized = append(canonicalized, u.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return canonicalized
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ClearScrollService clears one or more scroll contexts by their ids.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html#_clear_scroll_api
|
||||
// for details.
|
||||
type ClearScrollService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
scrollId []string
|
||||
}
|
||||
|
||||
// NewClearScrollService creates a new ClearScrollService.
|
||||
func NewClearScrollService(client *Client) *ClearScrollService {
|
||||
return &ClearScrollService{
|
||||
client: client,
|
||||
scrollId: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// ScrollId is a list of scroll IDs to clear.
|
||||
// Use _all to clear all search contexts.
|
||||
func (s *ClearScrollService) ScrollId(scrollIds ...string) *ClearScrollService {
|
||||
s.scrollId = append(s.scrollId, scrollIds...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ClearScrollService) Pretty(pretty bool) *ClearScrollService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ClearScrollService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path := "/_search/scroll/"
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ClearScrollService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.scrollId) == 0 {
|
||||
invalid = append(invalid, "ScrollId")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ClearScrollService) Do() (*ClearScrollResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
body := strings.Join(s.scrollId, ",")
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ClearScrollResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ClearScrollResponse is the response of ClearScrollService.Do.
|
||||
type ClearScrollResponse struct {
|
||||
}
|
||||
+1603
File diff suppressed because it is too large
Load Diff
+243
@@ -0,0 +1,243 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// ClusterHealthService allows to get a very simple status on the health of the cluster.
|
||||
//
|
||||
// See http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-health.html
|
||||
// for details.
|
||||
type ClusterHealthService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
indices []string
|
||||
level string
|
||||
local *bool
|
||||
masterTimeout string
|
||||
timeout string
|
||||
waitForActiveShards *int
|
||||
waitForNodes string
|
||||
waitForRelocatingShards *int
|
||||
waitForStatus string
|
||||
}
|
||||
|
||||
// NewClusterHealthService creates a new ClusterHealthService.
|
||||
func NewClusterHealthService(client *Client) *ClusterHealthService {
|
||||
return &ClusterHealthService{
|
||||
client: client,
|
||||
indices: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index limits the information returned to specific indices.
|
||||
func (s *ClusterHealthService) Index(indices ...string) *ClusterHealthService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Level specifies the level of detail for returned information.
|
||||
func (s *ClusterHealthService) Level(level string) *ClusterHealthService {
|
||||
s.level = level
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information. If it is true,
|
||||
// we do not retrieve the state from master node (default: false).
|
||||
func (s *ClusterHealthService) Local(local bool) *ClusterHealthService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies an explicit operation timeout for connection to master node.
|
||||
func (s *ClusterHealthService) MasterTimeout(masterTimeout string) *ClusterHealthService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout specifies an explicit operation timeout.
|
||||
func (s *ClusterHealthService) Timeout(timeout string) *ClusterHealthService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForActiveShards can be used to wait until the specified number of shards are active.
|
||||
func (s *ClusterHealthService) WaitForActiveShards(waitForActiveShards int) *ClusterHealthService {
|
||||
s.waitForActiveShards = &waitForActiveShards
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForNodes can be used to wait until the specified number of nodes are available.
|
||||
// Example: "12" to wait for exact values, ">12" and "<12" for ranges.
|
||||
func (s *ClusterHealthService) WaitForNodes(waitForNodes string) *ClusterHealthService {
|
||||
s.waitForNodes = waitForNodes
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForRelocatingShards can be used to wait until the specified number of relocating shards is finished.
|
||||
func (s *ClusterHealthService) WaitForRelocatingShards(waitForRelocatingShards int) *ClusterHealthService {
|
||||
s.waitForRelocatingShards = &waitForRelocatingShards
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForStatus can be used to wait until the cluster is in a specific state.
|
||||
// Valid values are: green, yellow, or red.
|
||||
func (s *ClusterHealthService) WaitForStatus(waitForStatus string) *ClusterHealthService {
|
||||
s.waitForStatus = waitForStatus
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForGreenStatus will wait for the "green" state.
|
||||
func (s *ClusterHealthService) WaitForGreenStatus() *ClusterHealthService {
|
||||
return s.WaitForStatus("green")
|
||||
}
|
||||
|
||||
// WaitForYellowStatus will wait for the "yellow" state.
|
||||
func (s *ClusterHealthService) WaitForYellowStatus() *ClusterHealthService {
|
||||
return s.WaitForStatus("yellow")
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ClusterHealthService) Pretty(pretty bool) *ClusterHealthService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ClusterHealthService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
if len(s.indices) > 0 {
|
||||
path, err = uritemplates.Expand("/_cluster/health/{index}", map[string]string{
|
||||
"index": strings.Join(s.indices, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_cluster/health"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.level != "" {
|
||||
params.Set("level", s.level)
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.waitForActiveShards != nil {
|
||||
params.Set("wait_for_active_shards", fmt.Sprintf("%v", s.waitForActiveShards))
|
||||
}
|
||||
if s.waitForNodes != "" {
|
||||
params.Set("wait_for_nodes", s.waitForNodes)
|
||||
}
|
||||
if s.waitForRelocatingShards != nil {
|
||||
params.Set("wait_for_relocating_shards", fmt.Sprintf("%v", s.waitForRelocatingShards))
|
||||
}
|
||||
if s.waitForStatus != "" {
|
||||
params.Set("wait_for_status", s.waitForStatus)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ClusterHealthService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ClusterHealthService) Do() (*ClusterHealthResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ClusterHealthResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ClusterHealthResponse is the response of ClusterHealthService.Do.
|
||||
type ClusterHealthResponse struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Status string `json:"status"`
|
||||
TimedOut bool `json:"timed_out"`
|
||||
NumberOfNodes int `json:"number_of_nodes"`
|
||||
NumberOfDataNodes int `json:"number_of_data_nodes"`
|
||||
ActivePrimaryShards int `json:"active_primary_shards"`
|
||||
ActiveShards int `json:"active_shards"`
|
||||
RelocatingShards int `json:"relocating_shards"`
|
||||
InitializingShards int `json:"initializing_shards"`
|
||||
UnassignedShards int `json:"unassigned_shards"`
|
||||
DelayedUnassignedShards int `json:"delayed_unassigned_shards"`
|
||||
NumberOfPendingTasks int `json:"number_of_pending_tasks"`
|
||||
NumberOfInFlightFetch int `json:"number_of_in_flight_fetch"`
|
||||
TaskMaxWaitTimeInQueueInMillis int `json:"task_max_waiting_in_queue_millis"`
|
||||
ActiveShardsPercentAsNumber float64 `json:"active_shards_percent_as_number"`
|
||||
|
||||
// Validation failures -> index name -> array of validation failures
|
||||
ValidationFailures []map[string][]string `json:"validation_failures"`
|
||||
|
||||
// Index name -> index health
|
||||
Indices map[string]*ClusterIndexHealth `json:"indices"`
|
||||
}
|
||||
|
||||
// ClusterIndexHealth will be returned as part of ClusterHealthResponse.
|
||||
type ClusterIndexHealth struct {
|
||||
Status string `json:"status"`
|
||||
NumberOfShards int `json:"number_of_shards"`
|
||||
NumberOfReplicas int `json:"number_of_replicas"`
|
||||
ActivePrimaryShards int `json:"active_primary_shards"`
|
||||
ActiveShards int `json:"active_shards"`
|
||||
RelocatingShards int `json:"relocating_shards"`
|
||||
InitializingShards int `json:"initializing_shards"`
|
||||
UnassignedShards int `json:"unassigned_shards"`
|
||||
// Validation failures
|
||||
ValidationFailures []string `json:"validation_failures"`
|
||||
// Shards by id, e.g. "0" or "1"
|
||||
Shards map[string]*ClusterShardHealth `json:"shards"`
|
||||
}
|
||||
|
||||
// ClusterShardHealth will be returned as part of ClusterHealthResponse.
|
||||
type ClusterShardHealth struct {
|
||||
Status string `json:"status"`
|
||||
PrimaryActive bool `json:"primary_active"`
|
||||
ActiveShards int `json:"active_shards"`
|
||||
RelocatingShards int `json:"relocating_shards"`
|
||||
InitializingShards int `json:"initializing_shards"`
|
||||
UnassignedShards int `json:"unassigned_shards"`
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// ClusterStateService allows to get a comprehensive state information of the whole cluster.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-state.html
|
||||
// for details.
|
||||
type ClusterStateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
indices []string
|
||||
metrics []string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
flatSettings *bool
|
||||
ignoreUnavailable *bool
|
||||
local *bool
|
||||
masterTimeout string
|
||||
}
|
||||
|
||||
// NewClusterStateService creates a new ClusterStateService.
|
||||
func NewClusterStateService(client *Client) *ClusterStateService {
|
||||
return &ClusterStateService{
|
||||
client: client,
|
||||
indices: make([]string, 0),
|
||||
metrics: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names. Use _all or an empty string to
|
||||
// perform the operation on all indices.
|
||||
func (s *ClusterStateService) Index(indices ...string) *ClusterStateService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Metric limits the information returned to the specified metric.
|
||||
// It can be one of: version, master_node, nodes, routing_table, metadata,
|
||||
// blocks, or customs.
|
||||
func (s *ClusterStateService) Metric(metrics ...string) *ClusterStateService {
|
||||
s.metrics = append(s.metrics, metrics...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *ClusterStateService) AllowNoIndices(allowNoIndices bool) *ClusterStateService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both..
|
||||
func (s *ClusterStateService) ExpandWildcards(expandWildcards string) *ClusterStateService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings, when set, returns settings in flat format (default: false).
|
||||
func (s *ClusterStateService) FlatSettings(flatSettings bool) *ClusterStateService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *ClusterStateService) IgnoreUnavailable(ignoreUnavailable bool) *ClusterStateService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information. When set, it does not
|
||||
// retrieve the state from master node (default: false).
|
||||
func (s *ClusterStateService) Local(local bool) *ClusterStateService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies timeout for connection to master.
|
||||
func (s *ClusterStateService) MasterTimeout(masterTimeout string) *ClusterStateService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ClusterStateService) Pretty(pretty bool) *ClusterStateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ClusterStateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
metrics := strings.Join(s.metrics, ",")
|
||||
if metrics == "" {
|
||||
metrics = "_all"
|
||||
}
|
||||
indices := strings.Join(s.indices, ",")
|
||||
if indices == "" {
|
||||
indices = "_all"
|
||||
}
|
||||
path, err := uritemplates.Expand("/_cluster/state/{metrics}/{indices}", map[string]string{
|
||||
"metrics": metrics,
|
||||
"indices": indices,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ClusterStateService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ClusterStateService) Do() (*ClusterStateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ClusterStateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ClusterStateResponse is the response of ClusterStateService.Do.
|
||||
type ClusterStateResponse struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Version int64 `json:"version"`
|
||||
StateUUID string `json:"state_uuid"`
|
||||
MasterNode string `json:"master_node"`
|
||||
Blocks map[string]*clusterBlocks `json:"blocks"`
|
||||
Nodes map[string]*discoveryNode `json:"nodes"`
|
||||
Metadata *clusterStateMetadata `json:"metadata"`
|
||||
RoutingTable map[string]*clusterStateRoutingTable `json:"routing_table"`
|
||||
RoutingNodes *clusterStateRoutingNode `json:"routing_nodes"`
|
||||
Customs map[string]interface{} `json:"customs"`
|
||||
}
|
||||
|
||||
type clusterBlocks struct {
|
||||
Global map[string]*clusterBlock `json:"global"` // id -> cluster block
|
||||
Indices map[string]*clusterBlock `json:"indices"` // index name -> cluster block
|
||||
}
|
||||
|
||||
type clusterBlock struct {
|
||||
Description string `json:"description"`
|
||||
Retryable bool `json:"retryable"`
|
||||
DisableStatePersistence bool `json:"disable_state_persistence"`
|
||||
Levels []string `json:"levels"`
|
||||
}
|
||||
|
||||
type clusterStateMetadata struct {
|
||||
ClusterUUID string `json:"cluster_uuid"`
|
||||
Templates map[string]*indexTemplateMetaData `json:"templates"` // template name -> index template metadata
|
||||
Indices map[string]*indexMetaData `json:"indices"` // index name _> meta data
|
||||
RoutingTable struct {
|
||||
Indices map[string]*indexRoutingTable `json:"indices"` // index name -> routing table
|
||||
} `json:"routing_table"`
|
||||
RoutingNodes struct {
|
||||
Unassigned []*shardRouting `json:"unassigned"`
|
||||
Nodes []*shardRouting `json:"nodes"`
|
||||
} `json:"routing_nodes"`
|
||||
Customs map[string]interface{} `json:"customs"`
|
||||
}
|
||||
|
||||
type discoveryNode struct {
|
||||
Name string `json:"name"` // server name, e.g. "es1"
|
||||
TransportAddress string `json:"transport_address"` // e.g. inet[/1.2.3.4:9300]
|
||||
Attributes map[string]interface{} `json:"attributes"` // e.g. { "data": true, "master": true }
|
||||
}
|
||||
|
||||
type clusterStateRoutingTable struct {
|
||||
Indices map[string]interface{} `json:"indices"`
|
||||
}
|
||||
|
||||
type clusterStateRoutingNode struct {
|
||||
Unassigned []*shardRouting `json:"unassigned"`
|
||||
// Node Id -> shardRouting
|
||||
Nodes map[string][]*shardRouting `json:"nodes"`
|
||||
}
|
||||
|
||||
type indexTemplateMetaData struct {
|
||||
Template string `json:"template"` // e.g. "store-*"
|
||||
Order int `json:"order"`
|
||||
Settings map[string]interface{} `json:"settings"` // index settings
|
||||
Mappings map[string]interface{} `json:"mappings"` // type name -> mapping
|
||||
}
|
||||
|
||||
type indexMetaData struct {
|
||||
State string `json:"state"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
Mappings map[string]interface{} `json:"mappings"`
|
||||
Aliases []string `json:"aliases"` // e.g. [ "alias1", "alias2" ]
|
||||
}
|
||||
|
||||
type indexRoutingTable struct {
|
||||
Shards map[string]*shardRouting `json:"shards"`
|
||||
}
|
||||
|
||||
type shardRouting struct {
|
||||
State string `json:"state"`
|
||||
Primary bool `json:"primary"`
|
||||
Node string `json:"node"`
|
||||
RelocatingNode string `json:"relocating_node"`
|
||||
Shard int `json:"shard"`
|
||||
Index string `json:"index"`
|
||||
Version int64 `json:"state"`
|
||||
RestoreSource *RestoreSource `json:"restore_source"`
|
||||
AllocationId *allocationId `json:"allocation_id"`
|
||||
UnassignedInfo *unassignedInfo `json:"unassigned_info"`
|
||||
}
|
||||
|
||||
type RestoreSource struct {
|
||||
Repository string `json:"repository"`
|
||||
Snapshot string `json:"snapshot"`
|
||||
Version string `json:"version"`
|
||||
Index string `json:"index"`
|
||||
}
|
||||
|
||||
type allocationId struct {
|
||||
Id string `json:"id"`
|
||||
RelocationId string `json:"relocation_id"`
|
||||
}
|
||||
|
||||
type unassignedInfo struct {
|
||||
Reason string `json:"reason"`
|
||||
At string `json:"at"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// ClusterStatsService is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/cluster-stats.html.
|
||||
type ClusterStatsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
nodeId []string
|
||||
flatSettings *bool
|
||||
human *bool
|
||||
}
|
||||
|
||||
// NewClusterStatsService creates a new ClusterStatsService.
|
||||
func NewClusterStatsService(client *Client) *ClusterStatsService {
|
||||
return &ClusterStatsService{
|
||||
client: client,
|
||||
nodeId: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// NodeId is documented as: A comma-separated list of node IDs or names to limit the returned information; use `_local` to return information from the node you're connecting to, leave empty to get information from all nodes.
|
||||
func (s *ClusterStatsService) NodeId(nodeId []string) *ClusterStatsService {
|
||||
s.nodeId = nodeId
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings is documented as: Return settings in flat format (default: false).
|
||||
func (s *ClusterStatsService) FlatSettings(flatSettings bool) *ClusterStatsService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// Human is documented as: Whether to return time and byte values in human-readable format..
|
||||
func (s *ClusterStatsService) Human(human bool) *ClusterStatsService {
|
||||
s.human = &human
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ClusterStatsService) Pretty(pretty bool) *ClusterStatsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ClusterStatsService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.nodeId) > 0 {
|
||||
path, err = uritemplates.Expand("/_cluster/stats/nodes/{node_id}", map[string]string{
|
||||
"node_id": strings.Join(s.nodeId, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
} else {
|
||||
path, err = uritemplates.Expand("/_cluster/stats", map[string]string{})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.human != nil {
|
||||
params.Set("human", fmt.Sprintf("%v", *s.human))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ClusterStatsService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ClusterStatsService) Do() (*ClusterStatsResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ClusterStatsResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ClusterStatsResponse is the response of ClusterStatsService.Do.
|
||||
type ClusterStatsResponse struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
ClusterName string `json:"cluster_name"`
|
||||
ClusterUUID string `json:"uuid"`
|
||||
Status string `json:"status"`
|
||||
Indices *ClusterStatsIndices `json:"indices"`
|
||||
Nodes *ClusterStatsNodes `json:"nodes"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndices struct {
|
||||
Count int `json:"count"`
|
||||
Shards *ClusterStatsIndicesShards `json:"shards"`
|
||||
Docs *ClusterStatsIndicesDocs `json:"docs"`
|
||||
Store *ClusterStatsIndicesStore `json:"store"`
|
||||
FieldData *ClusterStatsIndicesFieldData `json:"fielddata"`
|
||||
FilterCache *ClusterStatsIndicesFilterCache `json:"filter_cache"`
|
||||
IdCache *ClusterStatsIndicesIdCache `json:"id_cache"`
|
||||
Completion *ClusterStatsIndicesCompletion `json:"completion"`
|
||||
Segments *ClusterStatsIndicesSegments `json:"segments"`
|
||||
Percolate *ClusterStatsIndicesPercolate `json:"percolate"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesShards struct {
|
||||
Total int `json:"total"`
|
||||
Primaries int `json:"primaries"`
|
||||
Replication float64 `json:"replication"`
|
||||
Index *ClusterStatsIndicesShardsIndex `json:"index"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesShardsIndex struct {
|
||||
Shards *ClusterStatsIndicesShardsIndexIntMinMax `json:"shards"`
|
||||
Primaries *ClusterStatsIndicesShardsIndexIntMinMax `json:"primaries"`
|
||||
Replication *ClusterStatsIndicesShardsIndexFloat64MinMax `json:"replication"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesShardsIndexIntMinMax struct {
|
||||
Min int `json:"min"`
|
||||
Max int `json:"max"`
|
||||
Avg float64 `json:"avg"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesShardsIndexFloat64MinMax struct {
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
Avg float64 `json:"avg"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesDocs struct {
|
||||
Count int `json:"count"`
|
||||
Deleted int `json:"deleted"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesStore struct {
|
||||
Size string `json:"size"` // e.g. "5.3gb"
|
||||
SizeInBytes int64 `json:"size_in_bytes"`
|
||||
ThrottleTime string `json:"throttle_time"` // e.g. "0s"
|
||||
ThrottleTimeInMillis int64 `json:"throttle_time_in_millis"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesFieldData struct {
|
||||
MemorySize string `json:"memory_size"` // e.g. "61.3kb"
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
|
||||
Evictions int64 `json:"evictions"`
|
||||
Fields map[string]struct {
|
||||
MemorySize string `json:"memory_size"` // e.g. "61.3kb"
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
|
||||
} `json:"fields"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesFilterCache struct {
|
||||
MemorySize string `json:"memory_size"` // e.g. "61.3kb"
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
|
||||
Evictions int64 `json:"evictions"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesIdCache struct {
|
||||
MemorySize string `json:"memory_size"` // e.g. "61.3kb"
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesCompletion struct {
|
||||
Size string `json:"size"` // e.g. "61.3kb"
|
||||
SizeInBytes int64 `json:"size_in_bytes"`
|
||||
Fields map[string]struct {
|
||||
Size string `json:"size"` // e.g. "61.3kb"
|
||||
SizeInBytes int64 `json:"size_in_bytes"`
|
||||
} `json:"fields"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesSegments struct {
|
||||
Count int64 `json:"count"`
|
||||
Memory string `json:"memory"` // e.g. "61.3kb"
|
||||
MemoryInBytes int64 `json:"memory_in_bytes"`
|
||||
IndexWriterMemory string `json:"index_writer_memory"` // e.g. "61.3kb"
|
||||
IndexWriterMemoryInBytes int64 `json:"index_writer_memory_in_bytes"`
|
||||
IndexWriterMaxMemory string `json:"index_writer_max_memory"` // e.g. "61.3kb"
|
||||
IndexWriterMaxMemoryInBytes int64 `json:"index_writer_max_memory_in_bytes"`
|
||||
VersionMapMemory string `json:"version_map_memory"` // e.g. "61.3kb"
|
||||
VersionMapMemoryInBytes int64 `json:"version_map_memory_in_bytes"`
|
||||
FixedBitSet string `json:"fixed_bit_set"` // e.g. "61.3kb"
|
||||
FixedBitSetInBytes int64 `json:"fixed_bit_set_memory_in_bytes"`
|
||||
}
|
||||
|
||||
type ClusterStatsIndicesPercolate struct {
|
||||
Total int64 `json:"total"`
|
||||
// TODO(oe) The JSON tag here is wrong as of ES 1.5.2 it seems
|
||||
Time string `json:"get_time"` // e.g. "1s"
|
||||
TimeInBytes int64 `json:"time_in_millis"`
|
||||
Current int64 `json:"current"`
|
||||
MemorySize string `json:"memory_size"` // e.g. "61.3kb"
|
||||
MemorySizeInBytes int64 `json:"memory_sitze_in_bytes"`
|
||||
Queries int64 `json:"queries"`
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
type ClusterStatsNodes struct {
|
||||
Count *ClusterStatsNodesCount `json:"count"`
|
||||
Versions []string `json:"versions"`
|
||||
OS *ClusterStatsNodesOsStats `json:"os"`
|
||||
Process *ClusterStatsNodesProcessStats `json:"process"`
|
||||
JVM *ClusterStatsNodesJvmStats `json:"jvm"`
|
||||
FS *ClusterStatsNodesFsStats `json:"fs"`
|
||||
Plugins []*ClusterStatsNodesPlugin `json:"plugins"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesCount struct {
|
||||
Total int `json:"total"`
|
||||
MasterOnly int `json:"master_only"`
|
||||
DataOnly int `json:"data_only"`
|
||||
MasterData int `json:"master_data"`
|
||||
Client int `json:"client"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesOsStats struct {
|
||||
AvailableProcessors int `json:"available_processors"`
|
||||
Mem *ClusterStatsNodesOsStatsMem `json:"mem"`
|
||||
CPU []*ClusterStatsNodesOsStatsCPU `json:"cpu"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesOsStatsMem struct {
|
||||
Total string `json:"total"` // e.g. "16gb"
|
||||
TotalInBytes int64 `json:"total_in_bytes"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesOsStatsCPU struct {
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
MHz int `json:"mhz"`
|
||||
TotalCores int `json:"total_cores"`
|
||||
TotalSockets int `json:"total_sockets"`
|
||||
CoresPerSocket int `json:"cores_per_socket"`
|
||||
CacheSize string `json:"cache_size"` // e.g. "256b"
|
||||
CacheSizeInBytes int64 `json:"cache_size_in_bytes"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesProcessStats struct {
|
||||
CPU *ClusterStatsNodesProcessStatsCPU `json:"cpu"`
|
||||
OpenFileDescriptors *ClusterStatsNodesProcessStatsOpenFileDescriptors `json:"open_file_descriptors"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesProcessStatsCPU struct {
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesProcessStatsOpenFileDescriptors struct {
|
||||
Min int64 `json:"min"`
|
||||
Max int64 `json:"max"`
|
||||
Avg int64 `json:"avg"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesJvmStats struct {
|
||||
MaxUptime string `json:"max_uptime"` // e.g. "5h"
|
||||
MaxUptimeInMillis int64 `json:"max_uptime_in_millis"`
|
||||
Versions []*ClusterStatsNodesJvmStatsVersion `json:"versions"`
|
||||
Mem *ClusterStatsNodesJvmStatsMem `json:"mem"`
|
||||
Threads int64 `json:"threads"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesJvmStatsVersion struct {
|
||||
Version string `json:"version"` // e.g. "1.8.0_45"
|
||||
VMName string `json:"vm_name"` // e.g. "Java HotSpot(TM) 64-Bit Server VM"
|
||||
VMVersion string `json:"vm_version"` // e.g. "25.45-b02"
|
||||
VMVendor string `json:"vm_vendor"` // e.g. "Oracle Corporation"
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesJvmStatsMem struct {
|
||||
HeapUsed string `json:"heap_used"`
|
||||
HeapUsedInBytes int64 `json:"heap_used_in_bytes"`
|
||||
HeapMax string `json:"heap_max"`
|
||||
HeapMaxInBytes int64 `json:"heap_max_in_bytes"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesFsStats struct {
|
||||
Path string `json:"path"`
|
||||
Mount string `json:"mount"`
|
||||
Dev string `json:"dev"`
|
||||
Total string `json:"total"` // e.g. "930.7gb"`
|
||||
TotalInBytes int64 `json:"total_in_bytes"`
|
||||
Free string `json:"free"` // e.g. "930.7gb"`
|
||||
FreeInBytes int64 `json:"free_in_bytes"`
|
||||
Available string `json:"available"` // e.g. "930.7gb"`
|
||||
AvailableInBytes int64 `json:"available_in_bytes"`
|
||||
DiskReads int64 `json:"disk_reads"`
|
||||
DiskWrites int64 `json:"disk_writes"`
|
||||
DiskIOOp int64 `json:"disk_io_op"`
|
||||
DiskReadSize string `json:"disk_read_size"` // e.g. "0b"`
|
||||
DiskReadSizeInBytes int64 `json:"disk_read_size_in_bytes"`
|
||||
DiskWriteSize string `json:"disk_write_size"` // e.g. "0b"`
|
||||
DiskWriteSizeInBytes int64 `json:"disk_write_size_in_bytes"`
|
||||
DiskIOSize string `json:"disk_io_size"` // e.g. "0b"`
|
||||
DiskIOSizeInBytes int64 `json:"disk_io_size_in_bytes"`
|
||||
DiskQueue string `json:"disk_queue"`
|
||||
DiskServiceTime string `json:"disk_service_time"`
|
||||
}
|
||||
|
||||
type ClusterStatsNodesPlugin struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
JVM bool `json:"jvm"`
|
||||
Site bool `json:"site"`
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// conn represents a single connection to a node in a cluster.
|
||||
type conn struct {
|
||||
sync.RWMutex
|
||||
nodeID string // node ID
|
||||
url string
|
||||
failures int
|
||||
dead bool
|
||||
deadSince *time.Time
|
||||
}
|
||||
|
||||
// newConn creates a new connection to the given URL.
|
||||
func newConn(nodeID, url string) *conn {
|
||||
c := &conn{
|
||||
nodeID: nodeID,
|
||||
url: url,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// String returns a representation of the connection status.
|
||||
func (c *conn) String() string {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return fmt.Sprintf("%s [dead=%v,failures=%d,deadSince=%v]", c.url, c.dead, c.failures, c.deadSince)
|
||||
}
|
||||
|
||||
// NodeID returns the ID of the node of this connection.
|
||||
func (c *conn) NodeID() string {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.nodeID
|
||||
}
|
||||
|
||||
// URL returns the URL of this connection.
|
||||
func (c *conn) URL() string {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.url
|
||||
}
|
||||
|
||||
// IsDead returns true if this connection is marked as dead, i.e. a previous
|
||||
// request to the URL has been unsuccessful.
|
||||
func (c *conn) IsDead() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.dead
|
||||
}
|
||||
|
||||
// MarkAsDead marks this connection as dead, increments the failures
|
||||
// counter and stores the current time in dead since.
|
||||
func (c *conn) MarkAsDead() {
|
||||
c.Lock()
|
||||
c.dead = true
|
||||
if c.deadSince == nil {
|
||||
utcNow := time.Now().UTC()
|
||||
c.deadSince = &utcNow
|
||||
}
|
||||
c.failures += 1
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// MarkAsAlive marks this connection as eligible to be returned from the
|
||||
// pool of connections by the selector.
|
||||
func (c *conn) MarkAsAlive() {
|
||||
c.Lock()
|
||||
c.dead = false
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// MarkAsHealthy marks this connection as healthy, i.e. a request has been
|
||||
// successfully performed with it.
|
||||
func (c *conn) MarkAsHealthy() {
|
||||
c.Lock()
|
||||
c.dead = false
|
||||
c.deadSince = nil
|
||||
c.failures = 0
|
||||
c.Unlock()
|
||||
}
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// CountService is a convenient service for determining the
|
||||
// number of documents in an index. Use SearchService with
|
||||
// a SearchType of count for counting with queries etc.
|
||||
type CountService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
typ []string
|
||||
allowNoIndices *bool
|
||||
analyzeWildcard *bool
|
||||
analyzer string
|
||||
defaultOperator string
|
||||
df string
|
||||
expandWildcards string
|
||||
ignoreUnavailable *bool
|
||||
lenient *bool
|
||||
lowercaseExpandedTerms *bool
|
||||
minScore interface{}
|
||||
preference string
|
||||
q string
|
||||
query Query
|
||||
routing string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewCountService creates a new CountService.
|
||||
func NewCountService(client *Client) *CountService {
|
||||
return &CountService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Index sets the names of the indices to restrict the results.
|
||||
func (s *CountService) Index(index ...string) *CountService {
|
||||
if s.index == nil {
|
||||
s.index = make([]string, 0)
|
||||
}
|
||||
s.index = append(s.index, index...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type sets the types to use to restrict the results.
|
||||
func (s *CountService) Type(typ ...string) *CountService {
|
||||
if s.typ == nil {
|
||||
s.typ = make([]string, 0)
|
||||
}
|
||||
s.typ = append(s.typ, typ...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices. (This includes "_all" string
|
||||
// or when no indices have been specified).
|
||||
func (s *CountService) AllowNoIndices(allowNoIndices bool) *CountService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// AnalyzeWildcard specifies whether wildcard and prefix queries should be
|
||||
// analyzed (default: false).
|
||||
func (s *CountService) AnalyzeWildcard(analyzeWildcard bool) *CountService {
|
||||
s.analyzeWildcard = &analyzeWildcard
|
||||
return s
|
||||
}
|
||||
|
||||
// Analyzer specifies the analyzer to use for the query string.
|
||||
func (s *CountService) Analyzer(analyzer string) *CountService {
|
||||
s.analyzer = analyzer
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultOperator specifies the default operator for query string query (AND or OR).
|
||||
func (s *CountService) DefaultOperator(defaultOperator string) *CountService {
|
||||
s.defaultOperator = defaultOperator
|
||||
return s
|
||||
}
|
||||
|
||||
// Df specifies the field to use as default where no field prefix is given
|
||||
// in the query string.
|
||||
func (s *CountService) Df(df string) *CountService {
|
||||
s.df = df
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *CountService) ExpandWildcards(expandWildcards string) *CountService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *CountService) IgnoreUnavailable(ignoreUnavailable bool) *CountService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Lenient specifies whether format-based query failures (such as
|
||||
// providing text to a numeric field) should be ignored.
|
||||
func (s *CountService) Lenient(lenient bool) *CountService {
|
||||
s.lenient = &lenient
|
||||
return s
|
||||
}
|
||||
|
||||
// LowercaseExpandedTerms specifies whether query terms should be lowercased.
|
||||
func (s *CountService) LowercaseExpandedTerms(lowercaseExpandedTerms bool) *CountService {
|
||||
s.lowercaseExpandedTerms = &lowercaseExpandedTerms
|
||||
return s
|
||||
}
|
||||
|
||||
// MinScore indicates to include only documents with a specific `_score`
|
||||
// value in the result.
|
||||
func (s *CountService) MinScore(minScore interface{}) *CountService {
|
||||
s.minScore = minScore
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be
|
||||
// performed on (default: random).
|
||||
func (s *CountService) Preference(preference string) *CountService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Q in the Lucene query string syntax. You can also use Query to pass
|
||||
// a Query struct.
|
||||
func (s *CountService) Q(q string) *CountService {
|
||||
s.q = q
|
||||
return s
|
||||
}
|
||||
|
||||
// Query specifies the query to pass. You can also pass a query string with Q.
|
||||
func (s *CountService) Query(query Query) *CountService {
|
||||
s.query = query
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing specifies the routing value.
|
||||
func (s *CountService) Routing(routing string) *CountService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *CountService) Pretty(pretty bool) *CountService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson specifies the query to restrict the results specified with the
|
||||
// Query DSL (optional). The interface{} will be serialized to a JSON document,
|
||||
// so use a map[string]interface{}.
|
||||
func (s *CountService) BodyJson(body interface{}) *CountService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// Body specifies a query to restrict the results specified with
|
||||
// the Query DSL (optional).
|
||||
func (s *CountService) BodyString(body string) *CountService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *CountService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) > 0 && len(s.typ) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_count", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_count", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else if len(s.typ) > 0 {
|
||||
path, err = uritemplates.Expand("/_all/{type}/_count", map[string]string{
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_all/_count"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.analyzeWildcard != nil {
|
||||
params.Set("analyze_wildcard", fmt.Sprintf("%v", *s.analyzeWildcard))
|
||||
}
|
||||
if s.analyzer != "" {
|
||||
params.Set("analyzer", s.analyzer)
|
||||
}
|
||||
if s.defaultOperator != "" {
|
||||
params.Set("default_operator", s.defaultOperator)
|
||||
}
|
||||
if s.df != "" {
|
||||
params.Set("df", s.df)
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.lenient != nil {
|
||||
params.Set("lenient", fmt.Sprintf("%v", *s.lenient))
|
||||
}
|
||||
if s.lowercaseExpandedTerms != nil {
|
||||
params.Set("lowercase_expanded_terms", fmt.Sprintf("%v", *s.lowercaseExpandedTerms))
|
||||
}
|
||||
if s.minScore != nil {
|
||||
params.Set("min_score", fmt.Sprintf("%v", s.minScore))
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if s.q != "" {
|
||||
params.Set("q", s.q)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *CountService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *CountService) Do() (int64, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.query != nil {
|
||||
src, err := s.query.Source()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
query := make(map[string]interface{})
|
||||
query["query"] = src
|
||||
body = query
|
||||
} else if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else if s.bodyString != "" {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(CountResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ret != nil {
|
||||
return ret.Count, nil
|
||||
}
|
||||
|
||||
return int64(0), nil
|
||||
}
|
||||
|
||||
// CountResponse is the response of using the Count API.
|
||||
type CountResponse struct {
|
||||
Count int64 `json:"count"`
|
||||
Shards shardsInfo `json:"_shards,omitempty"`
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Decoder is used to decode responses from Elasticsearch.
|
||||
// Users of elastic can implement their own marshaler for advanced purposes
|
||||
// and set them per Client (see SetDecoder). If none is specified,
|
||||
// DefaultDecoder is used.
|
||||
type Decoder interface {
|
||||
Decode(data []byte, v interface{}) error
|
||||
}
|
||||
|
||||
// DefaultDecoder uses json.Unmarshal from the Go standard library
|
||||
// to decode JSON data.
|
||||
type DefaultDecoder struct{}
|
||||
|
||||
// Decode decodes with json.Unmarshal from the Go standard library.
|
||||
func (u *DefaultDecoder) Decode(data []byte, v interface{}) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// DeleteService allows to delete a typed JSON document from a specified
|
||||
// index based on its id.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html
|
||||
// for details.
|
||||
type DeleteService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
index string
|
||||
typ string
|
||||
routing string
|
||||
timeout string
|
||||
version interface{}
|
||||
versionType string
|
||||
consistency string
|
||||
parent string
|
||||
refresh *bool
|
||||
replication string
|
||||
}
|
||||
|
||||
// NewDeleteService creates a new DeleteService.
|
||||
func NewDeleteService(client *Client) *DeleteService {
|
||||
return &DeleteService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Type is the type of the document.
|
||||
func (s *DeleteService) Type(typ string) *DeleteService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Id is the document ID.
|
||||
func (s *DeleteService) Id(id string) *DeleteService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Index is the name of the index.
|
||||
func (s *DeleteService) Index(index string) *DeleteService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Replication specifies a replication type.
|
||||
func (s *DeleteService) Replication(replication string) *DeleteService {
|
||||
s.replication = replication
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a specific routing value.
|
||||
func (s *DeleteService) Routing(routing string) *DeleteService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *DeleteService) Timeout(timeout string) *DeleteService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is an explicit version number for concurrency control.
|
||||
func (s *DeleteService) Version(version interface{}) *DeleteService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is a specific version type.
|
||||
func (s *DeleteService) VersionType(versionType string) *DeleteService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// Consistency defines a specific write consistency setting for the operation.
|
||||
func (s *DeleteService) Consistency(consistency string) *DeleteService {
|
||||
s.consistency = consistency
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent is the ID of parent document.
|
||||
func (s *DeleteService) Parent(parent string) *DeleteService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh the index after performing the operation.
|
||||
func (s *DeleteService) Refresh(refresh bool) *DeleteService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *DeleteService) Pretty(pretty bool) *DeleteService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *DeleteService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
"id": s.id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.replication != "" {
|
||||
params.Set("replication", s.replication)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
if s.consistency != "" {
|
||||
params.Set("consistency", s.consistency)
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *DeleteService) Validate() error {
|
||||
var invalid []string
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *DeleteService) Do() (*DeleteResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(DeleteResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a delete request.
|
||||
|
||||
// DeleteResponse is the outcome of running DeleteService.Do.
|
||||
type DeleteResponse struct {
|
||||
// TODO _shards { total, failed, successful }
|
||||
Found bool `json:"found"`
|
||||
Index string `json:"_index"`
|
||||
Type string `json:"_type"`
|
||||
Id string `json:"_id"`
|
||||
Version int64 `json:"_version"`
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// DeleteByQueryService deletes documents that match a query.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/docs-delete-by-query.html.
|
||||
type DeleteByQueryService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
types []string
|
||||
analyzer string
|
||||
consistency string
|
||||
defaultOper string
|
||||
df string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
replication string
|
||||
routing string
|
||||
timeout string
|
||||
pretty bool
|
||||
q string
|
||||
query Query
|
||||
}
|
||||
|
||||
// NewDeleteByQueryService creates a new DeleteByQueryService.
|
||||
// You typically use the client's DeleteByQuery to get a reference to
|
||||
// the service.
|
||||
func NewDeleteByQueryService(client *Client) *DeleteByQueryService {
|
||||
builder := &DeleteByQueryService{
|
||||
client: client,
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
// Index sets the indices on which to perform the delete operation.
|
||||
func (s *DeleteByQueryService) Index(indices ...string) *DeleteByQueryService {
|
||||
if s.indices == nil {
|
||||
s.indices = make([]string, 0)
|
||||
}
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type limits the delete operation to the given types.
|
||||
func (s *DeleteByQueryService) Type(types ...string) *DeleteByQueryService {
|
||||
if s.types == nil {
|
||||
s.types = make([]string, 0)
|
||||
}
|
||||
s.types = append(s.types, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Analyzer to use for the query string.
|
||||
func (s *DeleteByQueryService) Analyzer(analyzer string) *DeleteByQueryService {
|
||||
s.analyzer = analyzer
|
||||
return s
|
||||
}
|
||||
|
||||
// Consistency represents the specific write consistency setting for the operation.
|
||||
// It can be one, quorum, or all.
|
||||
func (s *DeleteByQueryService) Consistency(consistency string) *DeleteByQueryService {
|
||||
s.consistency = consistency
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultOperator for query string query (AND or OR).
|
||||
func (s *DeleteByQueryService) DefaultOperator(defaultOperator string) *DeleteByQueryService {
|
||||
s.defaultOper = defaultOperator
|
||||
return s
|
||||
}
|
||||
|
||||
// DF is the field to use as default where no field prefix is given in the query string.
|
||||
func (s *DeleteByQueryService) DF(defaultField string) *DeleteByQueryService {
|
||||
s.df = defaultField
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultField is the field to use as default where no field prefix is given in the query string.
|
||||
// It is an alias to the DF func.
|
||||
func (s *DeleteByQueryService) DefaultField(defaultField string) *DeleteByQueryService {
|
||||
s.df = defaultField
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *DeleteByQueryService) IgnoreUnavailable(ignore bool) *DeleteByQueryService {
|
||||
s.ignoreUnavailable = &ignore
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices (including the _all string
|
||||
// or when no indices have been specified).
|
||||
func (s *DeleteByQueryService) AllowNoIndices(allow bool) *DeleteByQueryService {
|
||||
s.allowNoIndices = &allow
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both. It can be "open" or "closed".
|
||||
func (s *DeleteByQueryService) ExpandWildcards(expand string) *DeleteByQueryService {
|
||||
s.expandWildcards = expand
|
||||
return s
|
||||
}
|
||||
|
||||
// Replication sets a specific replication type (sync or async).
|
||||
func (s *DeleteByQueryService) Replication(replication string) *DeleteByQueryService {
|
||||
s.replication = replication
|
||||
return s
|
||||
}
|
||||
|
||||
// Q specifies the query in Lucene query string syntax. You can also use
|
||||
// Query to programmatically specify the query.
|
||||
func (s *DeleteByQueryService) Q(query string) *DeleteByQueryService {
|
||||
s.q = query
|
||||
return s
|
||||
}
|
||||
|
||||
// QueryString is an alias to Q. Notice that you can also use Query to
|
||||
// programmatically set the query.
|
||||
func (s *DeleteByQueryService) QueryString(query string) *DeleteByQueryService {
|
||||
s.q = query
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing sets a specific routing value.
|
||||
func (s *DeleteByQueryService) Routing(routing string) *DeleteByQueryService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout sets an explicit operation timeout, e.g. "1s" or "10000ms".
|
||||
func (s *DeleteByQueryService) Timeout(timeout string) *DeleteByQueryService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indents the JSON output from Elasticsearch.
|
||||
func (s *DeleteByQueryService) Pretty(pretty bool) *DeleteByQueryService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Query sets the query programmatically.
|
||||
func (s *DeleteByQueryService) Query(query Query) *DeleteByQueryService {
|
||||
s.query = query
|
||||
return s
|
||||
}
|
||||
|
||||
// Do executes the delete-by-query operation.
|
||||
func (s *DeleteByQueryService) Do() (*DeleteByQueryResult, error) {
|
||||
var err error
|
||||
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err = uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
if len(indexPart) > 0 {
|
||||
path += strings.Join(indexPart, ",")
|
||||
}
|
||||
|
||||
// Types part
|
||||
typesPart := make([]string, 0)
|
||||
for _, typ := range s.types {
|
||||
typ, err = uritemplates.Expand("{type}", map[string]string{
|
||||
"type": typ,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesPart = append(typesPart, typ)
|
||||
}
|
||||
if len(typesPart) > 0 {
|
||||
path += "/" + strings.Join(typesPart, ",")
|
||||
}
|
||||
|
||||
// Search
|
||||
path += "/_query"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.analyzer != "" {
|
||||
params.Set("analyzer", s.analyzer)
|
||||
}
|
||||
if s.consistency != "" {
|
||||
params.Set("consistency", s.consistency)
|
||||
}
|
||||
if s.defaultOper != "" {
|
||||
params.Set("default_operator", s.defaultOper)
|
||||
}
|
||||
if s.df != "" {
|
||||
params.Set("df", s.df)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.replication != "" {
|
||||
params.Set("replication", s.replication)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.q != "" {
|
||||
params.Set("q", s.q)
|
||||
}
|
||||
|
||||
// Set body if there is a query set
|
||||
var body interface{}
|
||||
if s.query != nil {
|
||||
src, err := s.query.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := make(map[string]interface{})
|
||||
query["query"] = src
|
||||
body = query
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(DeleteByQueryResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// DeleteByQueryResult is the outcome of executing Do with DeleteByQueryService.
|
||||
type DeleteByQueryResult struct {
|
||||
Took int64 `json:"took"`
|
||||
TimedOut bool `json:"timed_out"`
|
||||
Indices map[string]IndexDeleteByQueryResult `json:"_indices"`
|
||||
Failures []shardOperationFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// IndexNames returns the names of the indices the DeleteByQuery touched.
|
||||
func (res DeleteByQueryResult) IndexNames() []string {
|
||||
var indices []string
|
||||
for index, _ := range res.Indices {
|
||||
indices = append(indices, index)
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// All returns the index delete-by-query result of all indices.
|
||||
func (res DeleteByQueryResult) All() IndexDeleteByQueryResult {
|
||||
all, _ := res.Indices["_all"]
|
||||
return all
|
||||
}
|
||||
|
||||
// IndexDeleteByQueryResult is the result of a delete-by-query for a specific
|
||||
// index.
|
||||
type IndexDeleteByQueryResult struct {
|
||||
// Found documents, matching the query.
|
||||
Found int `json:"found"`
|
||||
// Deleted documents, successfully, from the given index.
|
||||
Deleted int `json:"deleted"`
|
||||
// Missing documents when trying to delete them.
|
||||
Missing int `json:"missing"`
|
||||
// Failed documents to be deleted for the given index.
|
||||
Failed int `json:"failed"`
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// DeleteTemplateService deletes a search template. More information can
|
||||
// be found at http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-template.html.
|
||||
type DeleteTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
version *int
|
||||
versionType string
|
||||
}
|
||||
|
||||
// NewDeleteTemplateService creates a new DeleteTemplateService.
|
||||
func NewDeleteTemplateService(client *Client) *DeleteTemplateService {
|
||||
return &DeleteTemplateService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Id is the template ID.
|
||||
func (s *DeleteTemplateService) Id(id string) *DeleteTemplateService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Version an explicit version number for concurrency control.
|
||||
func (s *DeleteTemplateService) Version(version int) *DeleteTemplateService {
|
||||
s.version = &version
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType specifies a version type.
|
||||
func (s *DeleteTemplateService) VersionType(versionType string) *DeleteTemplateService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *DeleteTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_search/template/{id}", map[string]string{
|
||||
"id": s.id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%d", *s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *DeleteTemplateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *DeleteTemplateService) Do() (*DeleteTemplateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(DeleteTemplateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// DeleteTemplateResponse is the response of DeleteTemplateService.Do.
|
||||
type DeleteTemplateResponse struct {
|
||||
Found bool `json:"found"`
|
||||
Index string `json:"_index"`
|
||||
Type string `json:"_type"`
|
||||
Id string `json:"_id"`
|
||||
Version int `json:"_version"`
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
/*
|
||||
Package elastic provides an interface to the Elasticsearch server
|
||||
(http://www.elasticsearch.org/).
|
||||
|
||||
The first thing you do is to create a Client. If you have Elasticsearch
|
||||
installed and running with its default settings
|
||||
(i.e. available at http://127.0.0.1:9200), all you need to do is:
|
||||
|
||||
client, err := elastic.NewClient()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
If your Elasticsearch server is running on a different IP and/or port,
|
||||
just provide a URL to NewClient:
|
||||
|
||||
// Create a client and connect to http://192.168.2.10:9201
|
||||
client, err := elastic.NewClient(elastic.SetURL("http://192.168.2.10:9201"))
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
You can pass many more configuration parameters to NewClient. Review the
|
||||
documentation of NewClient for more information.
|
||||
|
||||
If no Elasticsearch server is available, services will fail when creating
|
||||
a new request and will return ErrNoClient.
|
||||
|
||||
A Client provides services. The services usually come with a variety of
|
||||
methods to prepare the query and a Do function to execute it against the
|
||||
Elasticsearch REST interface and return a response. Here is an example
|
||||
of the IndexExists service that checks if a given index already exists.
|
||||
|
||||
exists, err := client.IndexExists("twitter").Do()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
if !exists {
|
||||
// Index does not exist yet.
|
||||
}
|
||||
|
||||
Look up the documentation for Client to get an idea of the services provided
|
||||
and what kinds of responses you get when executing the Do function of a service.
|
||||
Also see the wiki on Github for more details.
|
||||
|
||||
*/
|
||||
package elastic
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// checkResponse will return an error if the request/response indicates
|
||||
// an error returned from Elasticsearch.
|
||||
//
|
||||
// HTTP status codes between in the range [200..299] are considered successful.
|
||||
// All other errors are considered errors except they are specified in
|
||||
// ignoreErrors. This is necessary because for some services, HTTP status 404
|
||||
// is a valid response from Elasticsearch (e.g. the Exists service).
|
||||
//
|
||||
// The func tries to parse error details as returned from Elasticsearch
|
||||
// and encapsulates them in type elastic.Error.
|
||||
func checkResponse(req *http.Request, res *http.Response, ignoreErrors ...int) error {
|
||||
// 200-299 are valid status codes
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
// Ignore certain errors?
|
||||
for _, code := range ignoreErrors {
|
||||
if code == res.StatusCode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return createResponseError(res)
|
||||
}
|
||||
|
||||
// createResponseError creates an Error structure from the HTTP response,
|
||||
// its status code and the error information sent by Elasticsearch.
|
||||
func createResponseError(res *http.Response) error {
|
||||
if res.Body == nil {
|
||||
return &Error{Status: res.StatusCode}
|
||||
}
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return &Error{Status: res.StatusCode}
|
||||
}
|
||||
errReply := new(Error)
|
||||
err = json.Unmarshal(data, errReply)
|
||||
if err != nil {
|
||||
return &Error{Status: res.StatusCode}
|
||||
}
|
||||
if errReply != nil {
|
||||
if errReply.Status == 0 {
|
||||
errReply.Status = res.StatusCode
|
||||
}
|
||||
return errReply
|
||||
}
|
||||
return &Error{Status: res.StatusCode}
|
||||
}
|
||||
|
||||
// Error encapsulates error details as returned from Elasticsearch.
|
||||
type Error struct {
|
||||
Status int `json:"status"`
|
||||
Details *ErrorDetails `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// ErrorDetails encapsulate error details from Elasticsearch.
|
||||
// It is used in e.g. elastic.Error and elastic.BulkResponseItem.
|
||||
type ErrorDetails struct {
|
||||
Type string `json:"type"`
|
||||
Reason string `json:"reason"`
|
||||
ResourceType string `json:"resource.type,omitempty"`
|
||||
ResourceId string `json:"resource.id,omitempty"`
|
||||
Index string `json:"index,omitempty"`
|
||||
Phase string `json:"phase,omitempty"`
|
||||
Grouped bool `json:"grouped,omitempty"`
|
||||
CausedBy map[string]interface{} `json:"caused_by,omitempty"`
|
||||
RootCause []*ErrorDetails `json:"root_cause,omitempty"`
|
||||
FailedShards []map[string]interface{} `json:"failed_shards,omitempty"`
|
||||
}
|
||||
|
||||
// Error returns a string representation of the error.
|
||||
func (e *Error) Error() string {
|
||||
if e.Details != nil && e.Details.Reason != "" {
|
||||
return fmt.Sprintf("elastic: Error %d (%s): %s [type=%s]", e.Status, http.StatusText(e.Status), e.Details.Reason, e.Details.Type)
|
||||
} else {
|
||||
return fmt.Sprintf("elastic: Error %d (%s)", e.Status, http.StatusText(e.Status))
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotFound returns true if the given error indicates that Elasticsearch
|
||||
// returned HTTP status 404. The err parameter can be of type *elastic.Error,
|
||||
// elastic.Error, *http.Response or int (indicating the HTTP status code).
|
||||
func IsNotFound(err interface{}) bool {
|
||||
switch e := err.(type) {
|
||||
case *http.Response:
|
||||
return e.StatusCode == http.StatusNotFound
|
||||
case *Error:
|
||||
return e.Status == http.StatusNotFound
|
||||
case Error:
|
||||
return e.Status == http.StatusNotFound
|
||||
case int:
|
||||
return e == http.StatusNotFound
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTimeout returns true if the given error indicates that Elasticsearch
|
||||
// returned HTTP status 408. The err parameter can be of type *elastic.Error,
|
||||
// elastic.Error, *http.Response or int (indicating the HTTP status code).
|
||||
func IsTimeout(err interface{}) bool {
|
||||
switch e := err.(type) {
|
||||
case *http.Response:
|
||||
return e.StatusCode == http.StatusRequestTimeout
|
||||
case *Error:
|
||||
return e.Status == http.StatusRequestTimeout
|
||||
case Error:
|
||||
return e.Status == http.StatusRequestTimeout
|
||||
case int:
|
||||
return e == http.StatusRequestTimeout
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// -- General errors --
|
||||
|
||||
// shardsInfo represents information from a shard.
|
||||
type shardsInfo struct {
|
||||
Total int `json:"total"`
|
||||
Successful int `json:"successful"`
|
||||
Failed int `json:"failed"`
|
||||
}
|
||||
|
||||
// shardOperationFailure represents a shard failure.
|
||||
type shardOperationFailure struct {
|
||||
Shard int `json:"shard"`
|
||||
Index string `json:"index"`
|
||||
Status string `json:"status"`
|
||||
// "reason"
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// ExistsService checks for the existence of a document using HEAD.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html
|
||||
// for details.
|
||||
type ExistsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
index string
|
||||
typ string
|
||||
preference string
|
||||
realtime *bool
|
||||
refresh *bool
|
||||
routing string
|
||||
parent string
|
||||
}
|
||||
|
||||
// NewExistsService creates a new ExistsService.
|
||||
func NewExistsService(client *Client) *ExistsService {
|
||||
return &ExistsService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Id is the document ID.
|
||||
func (s *ExistsService) Id(id string) *ExistsService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Index is the name of the index.
|
||||
func (s *ExistsService) Index(index string) *ExistsService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the type of the document (use `_all` to fetch the first document
|
||||
// matching the ID across all types).
|
||||
func (s *ExistsService) Type(typ string) *ExistsService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be performed on (default: random).
|
||||
func (s *ExistsService) Preference(preference string) *ExistsService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Realtime specifies whether to perform the operation in realtime or search mode.
|
||||
func (s *ExistsService) Realtime(realtime bool) *ExistsService {
|
||||
s.realtime = &realtime
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh the shard containing the document before performing the operation.
|
||||
func (s *ExistsService) Refresh(refresh bool) *ExistsService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a specific routing value.
|
||||
func (s *ExistsService) Routing(routing string) *ExistsService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent is the ID of the parent document.
|
||||
func (s *ExistsService) Parent(parent string) *ExistsService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ExistsService) Pretty(pretty bool) *ExistsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ExistsService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{
|
||||
"id": s.id,
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.realtime != nil {
|
||||
params.Set("realtime", fmt.Sprintf("%v", *s.realtime))
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ExistsService) Validate() error {
|
||||
var invalid []string
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ExistsService) Do() (bool, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("HEAD", path, params, nil, 404)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusNotFound:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode)
|
||||
}
|
||||
}
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = fmt.Print
|
||||
_ = log.Print
|
||||
_ = strings.Index
|
||||
_ = uritemplates.Expand
|
||||
_ = url.Parse
|
||||
)
|
||||
|
||||
// ExplainService computes a score explanation for a query and
|
||||
// a specific document.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-explain.html.
|
||||
type ExplainService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
index string
|
||||
typ string
|
||||
q string
|
||||
routing string
|
||||
lenient *bool
|
||||
analyzer string
|
||||
df string
|
||||
fields []string
|
||||
lowercaseExpandedTerms *bool
|
||||
xSourceInclude []string
|
||||
analyzeWildcard *bool
|
||||
parent string
|
||||
preference string
|
||||
xSource []string
|
||||
defaultOperator string
|
||||
xSourceExclude []string
|
||||
source string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewExplainService creates a new ExplainService.
|
||||
func NewExplainService(client *Client) *ExplainService {
|
||||
return &ExplainService{
|
||||
client: client,
|
||||
xSource: make([]string, 0),
|
||||
xSourceExclude: make([]string, 0),
|
||||
fields: make([]string, 0),
|
||||
xSourceInclude: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Id is the document ID.
|
||||
func (s *ExplainService) Id(id string) *ExplainService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Index is the name of the index.
|
||||
func (s *ExplainService) Index(index string) *ExplainService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the type of the document.
|
||||
func (s *ExplainService) Type(typ string) *ExplainService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Source is the URL-encoded query definition (instead of using the request body).
|
||||
func (s *ExplainService) Source(source string) *ExplainService {
|
||||
s.source = source
|
||||
return s
|
||||
}
|
||||
|
||||
// XSourceExclude is a list of fields to exclude from the returned _source field.
|
||||
func (s *ExplainService) XSourceExclude(xSourceExclude ...string) *ExplainService {
|
||||
s.xSourceExclude = append(s.xSourceExclude, xSourceExclude...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Lenient specifies whether format-based query failures
|
||||
// (such as providing text to a numeric field) should be ignored.
|
||||
func (s *ExplainService) Lenient(lenient bool) *ExplainService {
|
||||
s.lenient = &lenient
|
||||
return s
|
||||
}
|
||||
|
||||
// Query in the Lucene query string syntax.
|
||||
func (s *ExplainService) Q(q string) *ExplainService {
|
||||
s.q = q
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing sets a specific routing value.
|
||||
func (s *ExplainService) Routing(routing string) *ExplainService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// AnalyzeWildcard specifies whether wildcards and prefix queries
|
||||
// in the query string query should be analyzed (default: false).
|
||||
func (s *ExplainService) AnalyzeWildcard(analyzeWildcard bool) *ExplainService {
|
||||
s.analyzeWildcard = &analyzeWildcard
|
||||
return s
|
||||
}
|
||||
|
||||
// Analyzer is the analyzer for the query string query.
|
||||
func (s *ExplainService) Analyzer(analyzer string) *ExplainService {
|
||||
s.analyzer = analyzer
|
||||
return s
|
||||
}
|
||||
|
||||
// Df is the default field for query string query (default: _all).
|
||||
func (s *ExplainService) Df(df string) *ExplainService {
|
||||
s.df = df
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields is a list of fields to return in the response.
|
||||
func (s *ExplainService) Fields(fields ...string) *ExplainService {
|
||||
s.fields = append(s.fields, fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// LowercaseExpandedTerms specifies whether query terms should be lowercased.
|
||||
func (s *ExplainService) LowercaseExpandedTerms(lowercaseExpandedTerms bool) *ExplainService {
|
||||
s.lowercaseExpandedTerms = &lowercaseExpandedTerms
|
||||
return s
|
||||
}
|
||||
|
||||
// XSourceInclude is a list of fields to extract and return from the _source field.
|
||||
func (s *ExplainService) XSourceInclude(xSourceInclude ...string) *ExplainService {
|
||||
s.xSourceInclude = append(s.xSourceInclude, xSourceInclude...)
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultOperator is the default operator for query string query (AND or OR).
|
||||
func (s *ExplainService) DefaultOperator(defaultOperator string) *ExplainService {
|
||||
s.defaultOperator = defaultOperator
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent is the ID of the parent document.
|
||||
func (s *ExplainService) Parent(parent string) *ExplainService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be performed on (default: random).
|
||||
func (s *ExplainService) Preference(preference string) *ExplainService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// XSource is true or false to return the _source field or not, or a list of fields to return.
|
||||
func (s *ExplainService) XSource(xSource ...string) *ExplainService {
|
||||
s.xSource = append(s.xSource, xSource...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ExplainService) Pretty(pretty bool) *ExplainService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Query sets a query definition using the Query DSL.
|
||||
func (s *ExplainService) Query(query Query) *ExplainService {
|
||||
src, err := query.Source()
|
||||
if err != nil {
|
||||
// Do nothing in case of an error
|
||||
return s
|
||||
}
|
||||
body := make(map[string]interface{})
|
||||
body["query"] = src
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson sets the query definition using the Query DSL.
|
||||
func (s *ExplainService) BodyJson(body interface{}) *ExplainService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString sets the query definition using the Query DSL as a string.
|
||||
func (s *ExplainService) BodyString(body string) *ExplainService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ExplainService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/{type}/{id}/_explain", map[string]string{
|
||||
"id": s.id,
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if len(s.xSource) > 0 {
|
||||
params.Set("_source", strings.Join(s.xSource, ","))
|
||||
}
|
||||
if s.defaultOperator != "" {
|
||||
params.Set("default_operator", s.defaultOperator)
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if s.source != "" {
|
||||
params.Set("source", s.source)
|
||||
}
|
||||
if len(s.xSourceExclude) > 0 {
|
||||
params.Set("_source_exclude", strings.Join(s.xSourceExclude, ","))
|
||||
}
|
||||
if s.lenient != nil {
|
||||
params.Set("lenient", fmt.Sprintf("%v", *s.lenient))
|
||||
}
|
||||
if s.q != "" {
|
||||
params.Set("q", s.q)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if len(s.fields) > 0 {
|
||||
params.Set("fields", strings.Join(s.fields, ","))
|
||||
}
|
||||
if s.lowercaseExpandedTerms != nil {
|
||||
params.Set("lowercase_expanded_terms", fmt.Sprintf("%v", *s.lowercaseExpandedTerms))
|
||||
}
|
||||
if len(s.xSourceInclude) > 0 {
|
||||
params.Set("_source_include", strings.Join(s.xSourceInclude, ","))
|
||||
}
|
||||
if s.analyzeWildcard != nil {
|
||||
params.Set("analyze_wildcard", fmt.Sprintf("%v", *s.analyzeWildcard))
|
||||
}
|
||||
if s.analyzer != "" {
|
||||
params.Set("analyzer", s.analyzer)
|
||||
}
|
||||
if s.df != "" {
|
||||
params.Set("df", s.df)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ExplainService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ExplainService) Do() (*ExplainResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ExplainResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ExplainResponse is the response of ExplainService.Do.
|
||||
type ExplainResponse struct {
|
||||
Index string `json:"_index"`
|
||||
Type string `json:"_type"`
|
||||
Id string `json:"_id"`
|
||||
Matched bool `json:"matched"`
|
||||
Explanation map[string]interface{} `json:"explanation"`
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FetchSourceContext struct {
|
||||
fetchSource bool
|
||||
transformSource bool
|
||||
includes []string
|
||||
excludes []string
|
||||
}
|
||||
|
||||
func NewFetchSourceContext(fetchSource bool) *FetchSourceContext {
|
||||
return &FetchSourceContext{
|
||||
fetchSource: fetchSource,
|
||||
includes: make([]string, 0),
|
||||
excludes: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) FetchSource() bool {
|
||||
return fsc.fetchSource
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) SetFetchSource(fetchSource bool) {
|
||||
fsc.fetchSource = fetchSource
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) Include(includes ...string) *FetchSourceContext {
|
||||
fsc.includes = append(fsc.includes, includes...)
|
||||
return fsc
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) Exclude(excludes ...string) *FetchSourceContext {
|
||||
fsc.excludes = append(fsc.excludes, excludes...)
|
||||
return fsc
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) TransformSource(transformSource bool) *FetchSourceContext {
|
||||
fsc.transformSource = transformSource
|
||||
return fsc
|
||||
}
|
||||
|
||||
func (fsc *FetchSourceContext) Source() (interface{}, error) {
|
||||
if !fsc.fetchSource {
|
||||
return false, nil
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"includes": fsc.includes,
|
||||
"excludes": fsc.excludes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Query returns the parameters in a form suitable for a URL query string.
|
||||
func (fsc *FetchSourceContext) Query() url.Values {
|
||||
params := url.Values{}
|
||||
if !fsc.fetchSource {
|
||||
params.Add("_source", "false")
|
||||
return params
|
||||
}
|
||||
if len(fsc.includes) > 0 {
|
||||
params.Add("_source_include", strings.Join(fsc.includes, ","))
|
||||
}
|
||||
if len(fsc.excludes) > 0 {
|
||||
params.Add("_source_exclude", strings.Join(fsc.excludes, ","))
|
||||
}
|
||||
return params
|
||||
}
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
const (
|
||||
FieldStatsClusterLevel = "cluster"
|
||||
FieldStatsIndicesLevel = "indices"
|
||||
)
|
||||
|
||||
// FieldStatsService allows finding statistical properties of a field without executing a search,
|
||||
// but looking up measurements that are natively available in the Lucene index.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-stats.html
|
||||
// for details
|
||||
type FieldStatsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
level string
|
||||
index []string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
fields []string
|
||||
ignoreUnavailable *bool
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewFieldStatsService creates a new FieldStatsService
|
||||
func NewFieldStatsService(client *Client) *FieldStatsService {
|
||||
return &FieldStatsService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
fields: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names; use `_all` or empty string to perform
|
||||
// the operation on all indices.
|
||||
func (s *FieldStatsService) Index(index ...string) *FieldStatsService {
|
||||
s.index = append(s.index, index...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices expression
|
||||
// resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *FieldStatsService) AllowNoIndices(allowNoIndices bool) *FieldStatsService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *FieldStatsService) ExpandWildcards(expandWildcards string) *FieldStatsService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields is a list of fields for to get field statistics
|
||||
// for (min value, max value, and more).
|
||||
func (s *FieldStatsService) Fields(fields ...string) *FieldStatsService {
|
||||
s.fields = append(s.fields, fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable is documented as: Whether specified concrete indices should be ignored when unavailable (missing or closed).
|
||||
func (s *FieldStatsService) IgnoreUnavailable(ignoreUnavailable bool) *FieldStatsService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Level sets if stats should be returned on a per index level or on a cluster wide level;
|
||||
// should be one of 'cluster' or 'indices'; defaults to former
|
||||
func (s *FieldStatsService) Level(level string) *FieldStatsService {
|
||||
s.level = level
|
||||
return s
|
||||
}
|
||||
|
||||
// ClusterLevel is a helper that sets Level to "cluster".
|
||||
func (s *FieldStatsService) ClusterLevel() *FieldStatsService {
|
||||
s.level = FieldStatsClusterLevel
|
||||
return s
|
||||
}
|
||||
|
||||
// IndicesLevel is a helper that sets Level to "indices".
|
||||
func (s *FieldStatsService) IndicesLevel() *FieldStatsService {
|
||||
s.level = FieldStatsIndicesLevel
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *FieldStatsService) Pretty(pretty bool) *FieldStatsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson is documented as: Field json objects containing the name and optionally a range to filter out indices result, that have results outside the defined bounds.
|
||||
func (s *FieldStatsService) BodyJson(body interface{}) *FieldStatsService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is documented as: Field json objects containing the name and optionally a range to filter out indices result, that have results outside the defined bounds.
|
||||
func (s *FieldStatsService) BodyString(body string) *FieldStatsService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *FieldStatsService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_field_stats", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_field_stats"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if len(s.fields) > 0 {
|
||||
params.Set("fields", strings.Join(s.fields, ","))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.level != "" {
|
||||
params.Set("level", s.level)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *FieldStatsService) Validate() error {
|
||||
var invalid []string
|
||||
if s.level != "" && (s.level != FieldStatsIndicesLevel && s.level != FieldStatsClusterLevel) {
|
||||
invalid = append(invalid, "Level")
|
||||
}
|
||||
if len(invalid) != 0 {
|
||||
return fmt.Errorf("missing or invalid required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *FieldStatsService) Do() (*FieldStatsResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body, http.StatusNotFound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(oe): Is 404 really a valid response here?
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
return &FieldStatsResponse{make(map[string]IndexFieldStats)}, nil
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(FieldStatsResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Request --
|
||||
|
||||
// FieldStatsRequest can be used to set up the body to be used in the
|
||||
// Field Stats API.
|
||||
type FieldStatsRequest struct {
|
||||
Fields []string `json:"fields"`
|
||||
IndexConstraints map[string]*FieldStatsConstraints `json:"index_constraints,omitempty"`
|
||||
}
|
||||
|
||||
// FieldStatsConstraints is a constraint on a field.
|
||||
type FieldStatsConstraints struct {
|
||||
Min *FieldStatsComparison `json:"min_value,omitempty"`
|
||||
Max *FieldStatsComparison `json:"max_value,omitempty"`
|
||||
}
|
||||
|
||||
// FieldStatsComparison contain all comparison operations that can be used
|
||||
// in FieldStatsConstraints.
|
||||
type FieldStatsComparison struct {
|
||||
Lte interface{} `json:"lte,omitempty"`
|
||||
Lt interface{} `json:"lt,omitempty"`
|
||||
Gte interface{} `json:"gte,omitempty"`
|
||||
Gt interface{} `json:"gt,omitempty"`
|
||||
}
|
||||
|
||||
// -- Response --
|
||||
|
||||
// FieldStatsResponse is the response body content
|
||||
type FieldStatsResponse struct {
|
||||
Indices map[string]IndexFieldStats `json:"indices,omitempty"`
|
||||
}
|
||||
|
||||
// IndexFieldStats contains field stats for an index
|
||||
type IndexFieldStats struct {
|
||||
Fields map[string]FieldStats `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// FieldStats contains stats of an individual field
|
||||
type FieldStats struct {
|
||||
MaxDoc int64 `json:"max_doc"`
|
||||
DocCount int64 `json:"doc_count"`
|
||||
Density int64 `json:"density"`
|
||||
SumDocFrequeny int64 `json:"sum_doc_freq"`
|
||||
SumTotalTermFrequency int64 `json:"sum_total_term_freq"`
|
||||
MinValue interface{} `json:"min_value"`
|
||||
MinValueAsString string `json:"min_value_as_string"`
|
||||
MaxValue interface{} `json:"max_value"`
|
||||
MaxValueAsString string `json:"max_value_as_string"`
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GeoPoint is a geographic position described via latitude and longitude.
|
||||
type GeoPoint struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
}
|
||||
|
||||
// Source returns the object to be serialized in Elasticsearch DSL.
|
||||
func (pt *GeoPoint) Source() map[string]float64 {
|
||||
return map[string]float64{
|
||||
"lat": pt.Lat,
|
||||
"lon": pt.Lon,
|
||||
}
|
||||
}
|
||||
|
||||
// GeoPointFromLatLon initializes a new GeoPoint by latitude and longitude.
|
||||
func GeoPointFromLatLon(lat, lon float64) *GeoPoint {
|
||||
return &GeoPoint{Lat: lat, Lon: lon}
|
||||
}
|
||||
|
||||
// GeoPointFromString initializes a new GeoPoint by a string that is
|
||||
// formatted as "{latitude},{longitude}", e.g. "40.10210,-70.12091".
|
||||
func GeoPointFromString(latLon string) (*GeoPoint, error) {
|
||||
latlon := strings.SplitN(latLon, ",", 2)
|
||||
if len(latlon) != 2 {
|
||||
return nil, fmt.Errorf("elastic: %s is not a valid geo point string", latLon)
|
||||
}
|
||||
lat, err := strconv.ParseFloat(latlon[0], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lon, err := strconv.ParseFloat(latlon[1], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GeoPoint{Lat: lat, Lon: lon}, nil
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// GetService allows to get a typed JSON document from the index based
|
||||
// on its id.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html
|
||||
// for details.
|
||||
type GetService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
routing string
|
||||
preference string
|
||||
fields []string
|
||||
refresh *bool
|
||||
realtime *bool
|
||||
fsc *FetchSourceContext
|
||||
version interface{}
|
||||
versionType string
|
||||
parent string
|
||||
ignoreErrorsOnGeneratedFields *bool
|
||||
}
|
||||
|
||||
// NewGetService creates a new GetService.
|
||||
func NewGetService(client *Client) *GetService {
|
||||
return &GetService{
|
||||
client: client,
|
||||
typ: "_all",
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// String returns a string representation of the GetService request.
|
||||
func (s *GetService) String() string {
|
||||
return fmt.Sprintf("[%v][%v][%v]: routing [%v]",
|
||||
s.index,
|
||||
s.typ,
|
||||
s.id,
|
||||
s.routing)
|
||||
}
|
||||
*/
|
||||
|
||||
// Index is the name of the index.
|
||||
func (s *GetService) Index(index string) *GetService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the type of the document (use `_all` to fetch the first document
|
||||
// matching the ID across all types).
|
||||
func (s *GetService) Type(typ string) *GetService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Id is the document ID.
|
||||
func (s *GetService) Id(id string) *GetService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent is the ID of the parent document.
|
||||
func (s *GetService) Parent(parent string) *GetService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is the specific routing value.
|
||||
func (s *GetService) Routing(routing string) *GetService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be performed on (default: random).
|
||||
func (s *GetService) Preference(preference string) *GetService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields is a list of fields to return in the response.
|
||||
func (s *GetService) Fields(fields ...string) *GetService {
|
||||
if s.fields == nil {
|
||||
s.fields = make([]string, 0)
|
||||
}
|
||||
s.fields = append(s.fields, fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *GetService) FetchSource(fetchSource bool) *GetService {
|
||||
if s.fsc == nil {
|
||||
s.fsc = NewFetchSourceContext(fetchSource)
|
||||
} else {
|
||||
s.fsc.SetFetchSource(fetchSource)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *GetService) FetchSourceContext(fetchSourceContext *FetchSourceContext) *GetService {
|
||||
s.fsc = fetchSourceContext
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh the shard containing the document before performing the operation.
|
||||
func (s *GetService) Refresh(refresh bool) *GetService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Realtime specifies whether to perform the operation in realtime or search mode.
|
||||
func (s *GetService) Realtime(realtime bool) *GetService {
|
||||
s.realtime = &realtime
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is the specific version type.
|
||||
func (s *GetService) VersionType(versionType string) *GetService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is an explicit version number for concurrency control.
|
||||
func (s *GetService) Version(version interface{}) *GetService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreErrorsOnGeneratedFields indicates whether to ignore fields that
|
||||
// are generated if the transaction log is accessed.
|
||||
func (s *GetService) IgnoreErrorsOnGeneratedFields(ignore bool) *GetService {
|
||||
s.ignoreErrorsOnGeneratedFields = &ignore
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *GetService) Pretty(pretty bool) *GetService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *GetService) Validate() error {
|
||||
var invalid []string
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *GetService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{
|
||||
"id": s.id,
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if len(s.fields) > 0 {
|
||||
params.Set("fields", strings.Join(s.fields, ","))
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
if s.realtime != nil {
|
||||
params.Set("realtime", fmt.Sprintf("%v", *s.realtime))
|
||||
}
|
||||
if s.ignoreErrorsOnGeneratedFields != nil {
|
||||
params.Add("ignore_errors_on_generated_fields", fmt.Sprintf("%v", *s.ignoreErrorsOnGeneratedFields))
|
||||
}
|
||||
if s.fsc != nil {
|
||||
for k, values := range s.fsc.Query() {
|
||||
params.Add(k, strings.Join(values, ","))
|
||||
}
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *GetService) Do() (*GetResult, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(GetResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a get request.
|
||||
|
||||
// GetResult is the outcome of GetService.Do.
|
||||
type GetResult struct {
|
||||
Index string `json:"_index"` // index meta field
|
||||
Type string `json:"_type"` // type meta field
|
||||
Id string `json:"_id"` // id meta field
|
||||
Uid string `json:"_uid"` // uid meta field (see MapperService.java for all meta fields)
|
||||
Timestamp int64 `json:"_timestamp"` // timestamp meta field
|
||||
TTL int64 `json:"_ttl"` // ttl meta field
|
||||
Routing string `json:"_routing"` // routing meta field
|
||||
Parent string `json:"_parent"` // parent meta field
|
||||
Version *int64 `json:"_version"` // version number, when Version is set to true in SearchService
|
||||
Source *json.RawMessage `json:"_source,omitempty"`
|
||||
Found bool `json:"found,omitempty"`
|
||||
Fields map[string]interface{} `json:"fields,omitempty"`
|
||||
//Error string `json:"error,omitempty"` // used only in MultiGet
|
||||
// TODO double-check that MultiGet now returns details error information
|
||||
Error *ErrorDetails `json:"error,omitempty"` // only used in MultiGet
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// GetTemplateService reads a search template.
|
||||
// It is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-template.html.
|
||||
type GetTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
version interface{}
|
||||
versionType string
|
||||
}
|
||||
|
||||
// NewGetTemplateService creates a new GetTemplateService.
|
||||
func NewGetTemplateService(client *Client) *GetTemplateService {
|
||||
return &GetTemplateService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Id is the template ID.
|
||||
func (s *GetTemplateService) Id(id string) *GetTemplateService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is an explicit version number for concurrency control.
|
||||
func (s *GetTemplateService) Version(version interface{}) *GetTemplateService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is a specific version type.
|
||||
func (s *GetTemplateService) VersionType(versionType string) *GetTemplateService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *GetTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_search/template/{id}", map[string]string{
|
||||
"id": s.id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *GetTemplateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.id == "" {
|
||||
invalid = append(invalid, "Id")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation and returns the template.
|
||||
func (s *GetTemplateService) Do() (*GetTemplateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(GetTemplateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type GetTemplateResponse struct {
|
||||
Template string `json:"template"`
|
||||
}
|
||||
+455
@@ -0,0 +1,455 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// Highlight allows highlighting search results on one or more fields.
|
||||
// For details, see:
|
||||
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-highlighting.html
|
||||
type Highlight struct {
|
||||
fields []*HighlighterField
|
||||
tagsSchema *string
|
||||
highlightFilter *bool
|
||||
fragmentSize *int
|
||||
numOfFragments *int
|
||||
preTags []string
|
||||
postTags []string
|
||||
order *string
|
||||
encoder *string
|
||||
requireFieldMatch *bool
|
||||
boundaryMaxScan *int
|
||||
boundaryChars []rune
|
||||
highlighterType *string
|
||||
fragmenter *string
|
||||
highlightQuery Query
|
||||
noMatchSize *int
|
||||
phraseLimit *int
|
||||
options map[string]interface{}
|
||||
forceSource *bool
|
||||
useExplicitFieldOrder bool
|
||||
}
|
||||
|
||||
func NewHighlight() *Highlight {
|
||||
hl := &Highlight{
|
||||
fields: make([]*HighlighterField, 0),
|
||||
preTags: make([]string, 0),
|
||||
postTags: make([]string, 0),
|
||||
boundaryChars: make([]rune, 0),
|
||||
options: make(map[string]interface{}),
|
||||
}
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Fields(fields ...*HighlighterField) *Highlight {
|
||||
hl.fields = append(hl.fields, fields...)
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Field(name string) *Highlight {
|
||||
field := NewHighlighterField(name)
|
||||
hl.fields = append(hl.fields, field)
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) TagsSchema(schemaName string) *Highlight {
|
||||
hl.tagsSchema = &schemaName
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) HighlightFilter(highlightFilter bool) *Highlight {
|
||||
hl.highlightFilter = &highlightFilter
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) FragmentSize(fragmentSize int) *Highlight {
|
||||
hl.fragmentSize = &fragmentSize
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) NumOfFragments(numOfFragments int) *Highlight {
|
||||
hl.numOfFragments = &numOfFragments
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Encoder(encoder string) *Highlight {
|
||||
hl.encoder = &encoder
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) PreTags(preTags ...string) *Highlight {
|
||||
hl.preTags = append(hl.preTags, preTags...)
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) PostTags(postTags ...string) *Highlight {
|
||||
hl.postTags = append(hl.postTags, postTags...)
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Order(order string) *Highlight {
|
||||
hl.order = &order
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) RequireFieldMatch(requireFieldMatch bool) *Highlight {
|
||||
hl.requireFieldMatch = &requireFieldMatch
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) BoundaryMaxScan(boundaryMaxScan int) *Highlight {
|
||||
hl.boundaryMaxScan = &boundaryMaxScan
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) BoundaryChars(boundaryChars ...rune) *Highlight {
|
||||
hl.boundaryChars = append(hl.boundaryChars, boundaryChars...)
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) HighlighterType(highlighterType string) *Highlight {
|
||||
hl.highlighterType = &highlighterType
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Fragmenter(fragmenter string) *Highlight {
|
||||
hl.fragmenter = &fragmenter
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) HighlighQuery(highlightQuery Query) *Highlight {
|
||||
hl.highlightQuery = highlightQuery
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) NoMatchSize(noMatchSize int) *Highlight {
|
||||
hl.noMatchSize = &noMatchSize
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) Options(options map[string]interface{}) *Highlight {
|
||||
hl.options = options
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) ForceSource(forceSource bool) *Highlight {
|
||||
hl.forceSource = &forceSource
|
||||
return hl
|
||||
}
|
||||
|
||||
func (hl *Highlight) UseExplicitFieldOrder(useExplicitFieldOrder bool) *Highlight {
|
||||
hl.useExplicitFieldOrder = useExplicitFieldOrder
|
||||
return hl
|
||||
}
|
||||
|
||||
// Creates the query source for the bool query.
|
||||
func (hl *Highlight) Source() (interface{}, error) {
|
||||
// Returns the map inside of "highlight":
|
||||
// "highlight":{
|
||||
// ... this ...
|
||||
// }
|
||||
source := make(map[string]interface{})
|
||||
if hl.tagsSchema != nil {
|
||||
source["tags_schema"] = *hl.tagsSchema
|
||||
}
|
||||
if hl.preTags != nil && len(hl.preTags) > 0 {
|
||||
source["pre_tags"] = hl.preTags
|
||||
}
|
||||
if hl.postTags != nil && len(hl.postTags) > 0 {
|
||||
source["post_tags"] = hl.postTags
|
||||
}
|
||||
if hl.order != nil {
|
||||
source["order"] = *hl.order
|
||||
}
|
||||
if hl.highlightFilter != nil {
|
||||
source["highlight_filter"] = *hl.highlightFilter
|
||||
}
|
||||
if hl.fragmentSize != nil {
|
||||
source["fragment_size"] = *hl.fragmentSize
|
||||
}
|
||||
if hl.numOfFragments != nil {
|
||||
source["number_of_fragments"] = *hl.numOfFragments
|
||||
}
|
||||
if hl.encoder != nil {
|
||||
source["encoder"] = *hl.encoder
|
||||
}
|
||||
if hl.requireFieldMatch != nil {
|
||||
source["require_field_match"] = *hl.requireFieldMatch
|
||||
}
|
||||
if hl.boundaryMaxScan != nil {
|
||||
source["boundary_max_scan"] = *hl.boundaryMaxScan
|
||||
}
|
||||
if hl.boundaryChars != nil && len(hl.boundaryChars) > 0 {
|
||||
source["boundary_chars"] = hl.boundaryChars
|
||||
}
|
||||
if hl.highlighterType != nil {
|
||||
source["type"] = *hl.highlighterType
|
||||
}
|
||||
if hl.fragmenter != nil {
|
||||
source["fragmenter"] = *hl.fragmenter
|
||||
}
|
||||
if hl.highlightQuery != nil {
|
||||
src, err := hl.highlightQuery.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["highlight_query"] = src
|
||||
}
|
||||
if hl.noMatchSize != nil {
|
||||
source["no_match_size"] = *hl.noMatchSize
|
||||
}
|
||||
if hl.phraseLimit != nil {
|
||||
source["phrase_limit"] = *hl.phraseLimit
|
||||
}
|
||||
if hl.options != nil && len(hl.options) > 0 {
|
||||
source["options"] = hl.options
|
||||
}
|
||||
if hl.forceSource != nil {
|
||||
source["force_source"] = *hl.forceSource
|
||||
}
|
||||
|
||||
if hl.fields != nil && len(hl.fields) > 0 {
|
||||
if hl.useExplicitFieldOrder {
|
||||
// Use a slice for the fields
|
||||
fields := make([]map[string]interface{}, 0)
|
||||
for _, field := range hl.fields {
|
||||
src, err := field.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmap := make(map[string]interface{})
|
||||
fmap[field.Name] = src
|
||||
fields = append(fields, fmap)
|
||||
}
|
||||
source["fields"] = fields
|
||||
} else {
|
||||
// Use a map for the fields
|
||||
fields := make(map[string]interface{}, 0)
|
||||
for _, field := range hl.fields {
|
||||
src, err := field.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields[field.Name] = src
|
||||
}
|
||||
source["fields"] = fields
|
||||
}
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// HighlighterField specifies a highlighted field.
|
||||
type HighlighterField struct {
|
||||
Name string
|
||||
|
||||
preTags []string
|
||||
postTags []string
|
||||
fragmentSize int
|
||||
fragmentOffset int
|
||||
numOfFragments int
|
||||
highlightFilter *bool
|
||||
order *string
|
||||
requireFieldMatch *bool
|
||||
boundaryMaxScan int
|
||||
boundaryChars []rune
|
||||
highlighterType *string
|
||||
fragmenter *string
|
||||
highlightQuery Query
|
||||
noMatchSize *int
|
||||
matchedFields []string
|
||||
phraseLimit *int
|
||||
options map[string]interface{}
|
||||
forceSource *bool
|
||||
|
||||
/*
|
||||
Name string
|
||||
preTags []string
|
||||
postTags []string
|
||||
fragmentSize int
|
||||
numOfFragments int
|
||||
fragmentOffset int
|
||||
highlightFilter *bool
|
||||
order string
|
||||
requireFieldMatch *bool
|
||||
boundaryMaxScan int
|
||||
boundaryChars []rune
|
||||
highlighterType string
|
||||
fragmenter string
|
||||
highlightQuery Query
|
||||
noMatchSize *int
|
||||
matchedFields []string
|
||||
options map[string]interface{}
|
||||
forceSource *bool
|
||||
*/
|
||||
}
|
||||
|
||||
func NewHighlighterField(name string) *HighlighterField {
|
||||
return &HighlighterField{
|
||||
Name: name,
|
||||
preTags: make([]string, 0),
|
||||
postTags: make([]string, 0),
|
||||
fragmentSize: -1,
|
||||
fragmentOffset: -1,
|
||||
numOfFragments: -1,
|
||||
boundaryMaxScan: -1,
|
||||
boundaryChars: make([]rune, 0),
|
||||
matchedFields: make([]string, 0),
|
||||
options: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *HighlighterField) PreTags(preTags ...string) *HighlighterField {
|
||||
f.preTags = append(f.preTags, preTags...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) PostTags(postTags ...string) *HighlighterField {
|
||||
f.postTags = append(f.postTags, postTags...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) FragmentSize(fragmentSize int) *HighlighterField {
|
||||
f.fragmentSize = fragmentSize
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) FragmentOffset(fragmentOffset int) *HighlighterField {
|
||||
f.fragmentOffset = fragmentOffset
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) NumOfFragments(numOfFragments int) *HighlighterField {
|
||||
f.numOfFragments = numOfFragments
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) HighlightFilter(highlightFilter bool) *HighlighterField {
|
||||
f.highlightFilter = &highlightFilter
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) Order(order string) *HighlighterField {
|
||||
f.order = &order
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) RequireFieldMatch(requireFieldMatch bool) *HighlighterField {
|
||||
f.requireFieldMatch = &requireFieldMatch
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) BoundaryMaxScan(boundaryMaxScan int) *HighlighterField {
|
||||
f.boundaryMaxScan = boundaryMaxScan
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) BoundaryChars(boundaryChars ...rune) *HighlighterField {
|
||||
f.boundaryChars = append(f.boundaryChars, boundaryChars...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) HighlighterType(highlighterType string) *HighlighterField {
|
||||
f.highlighterType = &highlighterType
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) Fragmenter(fragmenter string) *HighlighterField {
|
||||
f.fragmenter = &fragmenter
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) HighlightQuery(highlightQuery Query) *HighlighterField {
|
||||
f.highlightQuery = highlightQuery
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) NoMatchSize(noMatchSize int) *HighlighterField {
|
||||
f.noMatchSize = &noMatchSize
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) Options(options map[string]interface{}) *HighlighterField {
|
||||
f.options = options
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) MatchedFields(matchedFields ...string) *HighlighterField {
|
||||
f.matchedFields = append(f.matchedFields, matchedFields...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) PhraseLimit(phraseLimit int) *HighlighterField {
|
||||
f.phraseLimit = &phraseLimit
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) ForceSource(forceSource bool) *HighlighterField {
|
||||
f.forceSource = &forceSource
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *HighlighterField) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
|
||||
if f.preTags != nil && len(f.preTags) > 0 {
|
||||
source["pre_tags"] = f.preTags
|
||||
}
|
||||
if f.postTags != nil && len(f.postTags) > 0 {
|
||||
source["post_tags"] = f.postTags
|
||||
}
|
||||
if f.fragmentSize != -1 {
|
||||
source["fragment_size"] = f.fragmentSize
|
||||
}
|
||||
if f.numOfFragments != -1 {
|
||||
source["number_of_fragments"] = f.numOfFragments
|
||||
}
|
||||
if f.fragmentOffset != -1 {
|
||||
source["fragment_offset"] = f.fragmentOffset
|
||||
}
|
||||
if f.highlightFilter != nil {
|
||||
source["highlight_filter"] = *f.highlightFilter
|
||||
}
|
||||
if f.order != nil {
|
||||
source["order"] = *f.order
|
||||
}
|
||||
if f.requireFieldMatch != nil {
|
||||
source["require_field_match"] = *f.requireFieldMatch
|
||||
}
|
||||
if f.boundaryMaxScan != -1 {
|
||||
source["boundary_max_scan"] = f.boundaryMaxScan
|
||||
}
|
||||
if f.boundaryChars != nil && len(f.boundaryChars) > 0 {
|
||||
source["boundary_chars"] = f.boundaryChars
|
||||
}
|
||||
if f.highlighterType != nil {
|
||||
source["type"] = *f.highlighterType
|
||||
}
|
||||
if f.fragmenter != nil {
|
||||
source["fragmenter"] = *f.fragmenter
|
||||
}
|
||||
if f.highlightQuery != nil {
|
||||
src, err := f.highlightQuery.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["highlight_query"] = src
|
||||
}
|
||||
if f.noMatchSize != nil {
|
||||
source["no_match_size"] = *f.noMatchSize
|
||||
}
|
||||
if f.matchedFields != nil && len(f.matchedFields) > 0 {
|
||||
source["matched_fields"] = f.matchedFields
|
||||
}
|
||||
if f.phraseLimit != nil {
|
||||
source["phrase_limit"] = *f.phraseLimit
|
||||
}
|
||||
if f.options != nil && len(f.options) > 0 {
|
||||
source["options"] = f.options
|
||||
}
|
||||
if f.forceSource != nil {
|
||||
source["force_source"] = *f.forceSource
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndexService adds or updates a typed JSON document in a specified index,
|
||||
// making it searchable.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html
|
||||
// for details.
|
||||
type IndexService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
id string
|
||||
index string
|
||||
typ string
|
||||
parent string
|
||||
replication string
|
||||
routing string
|
||||
timeout string
|
||||
timestamp string
|
||||
ttl string
|
||||
version interface{}
|
||||
opType string
|
||||
versionType string
|
||||
refresh *bool
|
||||
consistency string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewIndexService creates a new IndexService.
|
||||
func NewIndexService(client *Client) *IndexService {
|
||||
return &IndexService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Id is the document ID.
|
||||
func (s *IndexService) Id(id string) *IndexService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Index is the name of the index.
|
||||
func (s *IndexService) Index(index string) *IndexService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the type of the document.
|
||||
func (s *IndexService) Type(typ string) *IndexService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Consistency is an explicit write consistency setting for the operation.
|
||||
func (s *IndexService) Consistency(consistency string) *IndexService {
|
||||
s.consistency = consistency
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh the index after performing the operation.
|
||||
func (s *IndexService) Refresh(refresh bool) *IndexService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Ttl is an expiration time for the document.
|
||||
func (s *IndexService) Ttl(ttl string) *IndexService {
|
||||
s.ttl = ttl
|
||||
return s
|
||||
}
|
||||
|
||||
// TTL is an expiration time for the document (alias for Ttl).
|
||||
func (s *IndexService) TTL(ttl string) *IndexService {
|
||||
s.ttl = ttl
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is an explicit version number for concurrency control.
|
||||
func (s *IndexService) Version(version interface{}) *IndexService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// OpType is an explicit operation type, i.e. "create" or "index" (default).
|
||||
func (s *IndexService) OpType(opType string) *IndexService {
|
||||
s.opType = opType
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent is the ID of the parent document.
|
||||
func (s *IndexService) Parent(parent string) *IndexService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Replication is a specific replication type.
|
||||
func (s *IndexService) Replication(replication string) *IndexService {
|
||||
s.replication = replication
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a specific routing value.
|
||||
func (s *IndexService) Routing(routing string) *IndexService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndexService) Timeout(timeout string) *IndexService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Timestamp is an explicit timestamp for the document.
|
||||
func (s *IndexService) Timestamp(timestamp string) *IndexService {
|
||||
s.timestamp = timestamp
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is a specific version type.
|
||||
func (s *IndexService) VersionType(versionType string) *IndexService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndexService) Pretty(pretty bool) *IndexService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson is the document as a serializable JSON interface.
|
||||
func (s *IndexService) BodyJson(body interface{}) *IndexService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is the document encoded as a string.
|
||||
func (s *IndexService) BodyString(body string) *IndexService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndexService) buildURL() (string, string, url.Values, error) {
|
||||
var err error
|
||||
var method, path string
|
||||
|
||||
if s.id != "" {
|
||||
// Create document with manual id
|
||||
method = "PUT"
|
||||
path, err = uritemplates.Expand("/{index}/{type}/{id}", map[string]string{
|
||||
"id": s.id,
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
} else {
|
||||
// Automatic ID generation
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#index-creation
|
||||
method = "POST"
|
||||
path, err = uritemplates.Expand("/{index}/{type}/", map[string]string{
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.consistency != "" {
|
||||
params.Set("consistency", s.consistency)
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.opType != "" {
|
||||
params.Set("op_type", s.opType)
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
if s.replication != "" {
|
||||
params.Set("replication", s.replication)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.timestamp != "" {
|
||||
params.Set("timestamp", s.timestamp)
|
||||
}
|
||||
if s.ttl != "" {
|
||||
params.Set("ttl", s.ttl)
|
||||
}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
return method, path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndexService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if s.bodyString == "" && s.bodyJson == nil {
|
||||
invalid = append(invalid, "BodyJson")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndexService) Do() (*IndexResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
method, path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest(method, path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndexResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndexResponse is the result of indexing a document in Elasticsearch.
|
||||
type IndexResponse struct {
|
||||
// TODO _shards { total, failed, successful }
|
||||
Index string `json:"_index"`
|
||||
Type string `json:"_type"`
|
||||
Id string `json:"_id"`
|
||||
Version int `json:"_version"`
|
||||
Created bool `json:"created"`
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesCloseService closes an index.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
|
||||
// for details.
|
||||
type IndicesCloseService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
timeout string
|
||||
masterTimeout string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
}
|
||||
|
||||
// NewIndicesCloseService creates and initializes a new IndicesCloseService.
|
||||
func NewIndicesCloseService(client *Client) *IndicesCloseService {
|
||||
return &IndicesCloseService{client: client}
|
||||
}
|
||||
|
||||
// Index is the name of the index to close.
|
||||
func (s *IndicesCloseService) Index(index string) *IndicesCloseService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesCloseService) Timeout(timeout string) *IndicesCloseService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesCloseService) MasterTimeout(masterTimeout string) *IndicesCloseService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesCloseService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesCloseService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified).
|
||||
func (s *IndicesCloseService) AllowNoIndices(allowNoIndices bool) *IndicesCloseService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesCloseService) ExpandWildcards(expandWildcards string) *IndicesCloseService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesCloseService) Pretty(pretty bool) *IndicesCloseService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesCloseService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/_close", map[string]string{
|
||||
"index": s.index,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesCloseService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesCloseService) Do() (*IndicesCloseResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesCloseResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesCloseResponse is the response of IndicesCloseService.Do.
|
||||
type IndicesCloseResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesCreateService creates a new index.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
|
||||
// for details.
|
||||
type IndicesCreateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
timeout string
|
||||
masterTimeout string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewIndicesCreateService returns a new IndicesCreateService.
|
||||
func NewIndicesCreateService(client *Client) *IndicesCreateService {
|
||||
return &IndicesCreateService{client: client}
|
||||
}
|
||||
|
||||
// Index is the name of the index to create.
|
||||
func (b *IndicesCreateService) Index(index string) *IndicesCreateService {
|
||||
b.index = index
|
||||
return b
|
||||
}
|
||||
|
||||
// Timeout the explicit operation timeout, e.g. "5s".
|
||||
func (s *IndicesCreateService) Timeout(timeout string) *IndicesCreateService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesCreateService) MasterTimeout(masterTimeout string) *IndicesCreateService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Body specifies the configuration of the index as a string.
|
||||
// It is an alias for BodyString.
|
||||
func (b *IndicesCreateService) Body(body string) *IndicesCreateService {
|
||||
b.bodyString = body
|
||||
return b
|
||||
}
|
||||
|
||||
// BodyString specifies the configuration of the index as a string.
|
||||
func (b *IndicesCreateService) BodyString(body string) *IndicesCreateService {
|
||||
b.bodyString = body
|
||||
return b
|
||||
}
|
||||
|
||||
// BodyJson specifies the configuration of the index. The interface{} will
|
||||
// be serializes as a JSON document, so use a map[string]interface{}.
|
||||
func (b *IndicesCreateService) BodyJson(body interface{}) *IndicesCreateService {
|
||||
b.bodyJson = body
|
||||
return b
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (b *IndicesCreateService) Pretty(pretty bool) *IndicesCreateService {
|
||||
b.pretty = pretty
|
||||
return b
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (b *IndicesCreateService) Do() (*IndicesCreateResult, error) {
|
||||
if b.index == "" {
|
||||
return nil, errors.New("missing index name")
|
||||
}
|
||||
|
||||
// Build url
|
||||
path, err := uritemplates.Expand("/{index}", map[string]string{
|
||||
"index": b.index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := make(url.Values)
|
||||
if b.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if b.masterTimeout != "" {
|
||||
params.Set("master_timeout", b.masterTimeout)
|
||||
}
|
||||
if b.timeout != "" {
|
||||
params.Set("timeout", b.timeout)
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if b.bodyJson != nil {
|
||||
body = b.bodyJson
|
||||
} else {
|
||||
body = b.bodyString
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := b.client.PerformRequest("PUT", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := new(IndicesCreateResult)
|
||||
if err := b.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a create index request.
|
||||
|
||||
// IndicesCreateResult is the outcome of creating a new index.
|
||||
type IndicesCreateResult struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesDeleteService allows to delete existing indices.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-delete-index.html
|
||||
// for details.
|
||||
type IndicesDeleteService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
timeout string
|
||||
masterTimeout string
|
||||
}
|
||||
|
||||
// NewIndicesDeleteService creates and initializes a new IndicesDeleteService.
|
||||
func NewIndicesDeleteService(client *Client) *IndicesDeleteService {
|
||||
return &IndicesDeleteService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index adds the list of indices to delete.
|
||||
// Use `_all` or `*` string to delete all indices.
|
||||
func (s *IndicesDeleteService) Index(index []string) *IndicesDeleteService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesDeleteService) Timeout(timeout string) *IndicesDeleteService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesDeleteService) MasterTimeout(masterTimeout string) *IndicesDeleteService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesDeleteService) Pretty(pretty bool) *IndicesDeleteService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesDeleteService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesDeleteService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.index) == 0 {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesDeleteService) Do() (*IndicesDeleteResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesDeleteResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a delete index request.
|
||||
|
||||
// IndicesDeleteResponse is the response of IndicesDeleteService.Do.
|
||||
type IndicesDeleteResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesDeleteTemplateService deletes index templates.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/indices-templates.html.
|
||||
type IndicesDeleteTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
name string
|
||||
timeout string
|
||||
masterTimeout string
|
||||
}
|
||||
|
||||
// NewIndicesDeleteTemplateService creates a new IndicesDeleteTemplateService.
|
||||
func NewIndicesDeleteTemplateService(client *Client) *IndicesDeleteTemplateService {
|
||||
return &IndicesDeleteTemplateService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Name is the name of the template.
|
||||
func (s *IndicesDeleteTemplateService) Name(name string) *IndicesDeleteTemplateService {
|
||||
s.name = name
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesDeleteTemplateService) Timeout(timeout string) *IndicesDeleteTemplateService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesDeleteTemplateService) MasterTimeout(masterTimeout string) *IndicesDeleteTemplateService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesDeleteTemplateService) Pretty(pretty bool) *IndicesDeleteTemplateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesDeleteTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_template/{name}", map[string]string{
|
||||
"name": s.name,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesDeleteTemplateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.name == "" {
|
||||
invalid = append(invalid, "Name")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesDeleteTemplateService) Do() (*IndicesDeleteTemplateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesDeleteTemplateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesDeleteTemplateResponse is the response of IndicesDeleteTemplateService.Do.
|
||||
type IndicesDeleteTemplateResponse struct {
|
||||
Acknowledged bool `json:"acknowledged,omitempty"`
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesDeleteWarmerService allows to delete a warmer.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-warmers.html.
|
||||
type IndicesDeleteWarmerService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
name []string
|
||||
masterTimeout string
|
||||
}
|
||||
|
||||
// NewIndicesDeleteWarmerService creates a new IndicesDeleteWarmerService.
|
||||
func NewIndicesDeleteWarmerService(client *Client) *IndicesDeleteWarmerService {
|
||||
return &IndicesDeleteWarmerService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
name: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all indices.
|
||||
func (s *IndicesDeleteWarmerService) Index(indices ...string) *IndicesDeleteWarmerService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Name is a list of warmer names to delete (supports wildcards);
|
||||
// use `_all` to delete all warmers in the specified indices.
|
||||
func (s *IndicesDeleteWarmerService) Name(name ...string) *IndicesDeleteWarmerService {
|
||||
s.name = append(s.name, name...)
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesDeleteWarmerService) MasterTimeout(masterTimeout string) *IndicesDeleteWarmerService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesDeleteWarmerService) Pretty(pretty bool) *IndicesDeleteWarmerService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesDeleteWarmerService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/_warmer/{name}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
if len(s.name) > 0 {
|
||||
params.Set("name", strings.Join(s.name, ","))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesDeleteWarmerService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.index) == 0 {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(s.name) == 0 {
|
||||
invalid = append(invalid, "Name")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesDeleteWarmerService) Do() (*DeleteWarmerResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("DELETE", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(DeleteWarmerResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// DeleteWarmerResponse is the response of IndicesDeleteWarmerService.Do.
|
||||
type DeleteWarmerResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesExistsService checks if an index or indices exist or not.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-exists.html
|
||||
// for details.
|
||||
type IndicesExistsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
local *bool
|
||||
}
|
||||
|
||||
// NewIndicesExistsService creates and initializes a new IndicesExistsService.
|
||||
func NewIndicesExistsService(client *Client) *IndicesExistsService {
|
||||
return &IndicesExistsService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of one or more indices to check.
|
||||
func (s *IndicesExistsService) Index(index []string) *IndicesExistsService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices expression
|
||||
// resolves into no concrete indices. (This includes `_all` string or
|
||||
// when no indices have been specified).
|
||||
func (s *IndicesExistsService) AllowNoIndices(allowNoIndices bool) *IndicesExistsService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesExistsService) ExpandWildcards(expandWildcards string) *IndicesExistsService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Local, when set, returns local information and does not retrieve the state
|
||||
// from master node (default: false).
|
||||
func (s *IndicesExistsService) Local(local bool) *IndicesExistsService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesExistsService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesExistsService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesExistsService) Pretty(pretty bool) *IndicesExistsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesExistsService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesExistsService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.index) == 0 {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesExistsService) Do() (bool, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("HEAD", path, params, nil, 404)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusNotFound:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode)
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesExistsTemplateService checks if a given template exists.
|
||||
// See http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html#indices-templates-exists
|
||||
// for documentation.
|
||||
type IndicesExistsTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
name string
|
||||
local *bool
|
||||
}
|
||||
|
||||
// NewIndicesExistsTemplateService creates a new IndicesExistsTemplateService.
|
||||
func NewIndicesExistsTemplateService(client *Client) *IndicesExistsTemplateService {
|
||||
return &IndicesExistsTemplateService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Name is the name of the template.
|
||||
func (s *IndicesExistsTemplateService) Name(name string) *IndicesExistsTemplateService {
|
||||
s.name = name
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information, i.e. do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesExistsTemplateService) Local(local bool) *IndicesExistsTemplateService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesExistsTemplateService) Pretty(pretty bool) *IndicesExistsTemplateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesExistsTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_template/{name}", map[string]string{
|
||||
"name": s.name,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesExistsTemplateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.name == "" {
|
||||
invalid = append(invalid, "Name")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesExistsTemplateService) Do() (bool, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("HEAD", path, params, nil, 404)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusNotFound:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode)
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesExistsTypeService checks if one or more types exist in one or more indices.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-types-exists.html
|
||||
// for details.
|
||||
type IndicesExistsTypeService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
typ []string
|
||||
index []string
|
||||
expandWildcards string
|
||||
local *bool
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
}
|
||||
|
||||
// NewIndicesExistsTypeService creates a new IndicesExistsTypeService.
|
||||
func NewIndicesExistsTypeService(client *Client) *IndicesExistsTypeService {
|
||||
return &IndicesExistsTypeService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
typ: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names; use `_all` to check the types across all indices.
|
||||
func (s *IndicesExistsTypeService) Index(indices ...string) *IndicesExistsTypeService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is a list of document types to check.
|
||||
func (s *IndicesExistsTypeService) Type(types ...string) *IndicesExistsTypeService {
|
||||
s.typ = append(s.typ, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesExistsTypeService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesExistsTypeService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *IndicesExistsTypeService) AllowNoIndices(allowNoIndices bool) *IndicesExistsTypeService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesExistsTypeService) ExpandWildcards(expandWildcards string) *IndicesExistsTypeService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Local specifies whether to return local information, i.e. do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesExistsTypeService) Local(local bool) *IndicesExistsTypeService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesExistsTypeService) Pretty(pretty bool) *IndicesExistsTypeService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesExistsTypeService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/{type}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesExistsTypeService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.index) == 0 {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(s.typ) == 0 {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesExistsTypeService) Do() (bool, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("HEAD", path, params, nil, 404)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusNotFound:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode)
|
||||
}
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// Flush allows to flush one or more indices. The flush process of an index
|
||||
// basically frees memory from the index by flushing data to the index
|
||||
// storage and clearing the internal transaction log.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html
|
||||
// for details.
|
||||
type IndicesFlushService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
force *bool
|
||||
waitIfOngoing *bool
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
}
|
||||
|
||||
// NewIndicesFlushService creates a new IndicesFlushService.
|
||||
func NewIndicesFlushService(client *Client) *IndicesFlushService {
|
||||
return &IndicesFlushService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names; use `_all` or empty string for all indices.
|
||||
func (s *IndicesFlushService) Index(indices ...string) *IndicesFlushService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Force indicates whether a flush should be forced even if it is not
|
||||
// necessarily needed ie. if no changes will be committed to the index.
|
||||
// This is useful if transaction log IDs should be incremented even if
|
||||
// no uncommitted changes are present. (This setting can be considered as internal).
|
||||
func (s *IndicesFlushService) Force(force bool) *IndicesFlushService {
|
||||
s.force = &force
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitIfOngoing, if set to true, indicates that the flush operation will
|
||||
// block until the flush can be executed if another flush operation is
|
||||
// already executing. The default is false and will cause an exception
|
||||
// to be thrown on the shard level if another flush operation is already running..
|
||||
func (s *IndicesFlushService) WaitIfOngoing(waitIfOngoing bool) *IndicesFlushService {
|
||||
s.waitIfOngoing = &waitIfOngoing
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesFlushService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesFlushService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices expression
|
||||
// resolves into no concrete indices. (This includes `_all` string or when
|
||||
// no indices have been specified).
|
||||
func (s *IndicesFlushService) AllowNoIndices(allowNoIndices bool) *IndicesFlushService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards specifies whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both..
|
||||
func (s *IndicesFlushService) ExpandWildcards(expandWildcards string) *IndicesFlushService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesFlushService) Pretty(pretty bool) *IndicesFlushService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesFlushService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_flush", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_flush"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.force != nil {
|
||||
params.Set("force", fmt.Sprintf("%v", *s.force))
|
||||
}
|
||||
if s.waitIfOngoing != nil {
|
||||
params.Set("wait_if_ongoing", fmt.Sprintf("%v", *s.waitIfOngoing))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesFlushService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the service.
|
||||
func (s *IndicesFlushService) Do() (*IndicesFlushResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesFlushResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a flush request.
|
||||
|
||||
type IndicesFlushResponse struct {
|
||||
Shards shardsInfo `json:"_shards"`
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesForcemergeService allows to force merging of one or more indices.
|
||||
// The merge relates to the number of segments a Lucene index holds
|
||||
// within each shard. The force merge operation allows to reduce the number
|
||||
// of segments by merging them.
|
||||
//
|
||||
// See http://www.elastic.co/guide/en/elasticsearch/reference/2.1/indices-forcemerge.html
|
||||
// for more information.
|
||||
type IndicesForcemergeService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
flush *bool
|
||||
ignoreUnavailable *bool
|
||||
maxNumSegments interface{}
|
||||
onlyExpungeDeletes *bool
|
||||
operationThreading interface{}
|
||||
waitForMerge *bool
|
||||
}
|
||||
|
||||
// NewIndicesForcemergeService creates a new IndicesForcemergeService.
|
||||
func NewIndicesForcemergeService(client *Client) *IndicesForcemergeService {
|
||||
return &IndicesForcemergeService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names; use `_all` or empty string to perform
|
||||
// the operation on all indices.
|
||||
func (s *IndicesForcemergeService) Index(index ...string) *IndicesForcemergeService {
|
||||
if s.index == nil {
|
||||
s.index = make([]string, 0)
|
||||
}
|
||||
s.index = append(s.index, index...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *IndicesForcemergeService) AllowNoIndices(allowNoIndices bool) *IndicesForcemergeService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both..
|
||||
func (s *IndicesForcemergeService) ExpandWildcards(expandWildcards string) *IndicesForcemergeService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Flush specifies whether the index should be flushed after performing
|
||||
// the operation (default: true).
|
||||
func (s *IndicesForcemergeService) Flush(flush bool) *IndicesForcemergeService {
|
||||
s.flush = &flush
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should
|
||||
// be ignored when unavailable (missing or closed).
|
||||
func (s *IndicesForcemergeService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesForcemergeService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// MaxNumSegments specifies the number of segments the index should be
|
||||
// merged into (default: dynamic).
|
||||
func (s *IndicesForcemergeService) MaxNumSegments(maxNumSegments interface{}) *IndicesForcemergeService {
|
||||
s.maxNumSegments = maxNumSegments
|
||||
return s
|
||||
}
|
||||
|
||||
// OnlyExpungeDeletes specifies whether the operation should only expunge
|
||||
// deleted documents.
|
||||
func (s *IndicesForcemergeService) OnlyExpungeDeletes(onlyExpungeDeletes bool) *IndicesForcemergeService {
|
||||
s.onlyExpungeDeletes = &onlyExpungeDeletes
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *IndicesForcemergeService) OperationThreading(operationThreading interface{}) *IndicesForcemergeService {
|
||||
s.operationThreading = operationThreading
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForMerge specifies whether the request should block until the
|
||||
// merge process is finished (default: true).
|
||||
func (s *IndicesForcemergeService) WaitForMerge(waitForMerge bool) *IndicesForcemergeService {
|
||||
s.waitForMerge = &waitForMerge
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesForcemergeService) Pretty(pretty bool) *IndicesForcemergeService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesForcemergeService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
// Build URL
|
||||
if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_forcemerge", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_forcemerge"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.flush != nil {
|
||||
params.Set("flush", fmt.Sprintf("%v", *s.flush))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.maxNumSegments != nil {
|
||||
params.Set("max_num_segments", fmt.Sprintf("%v", s.maxNumSegments))
|
||||
}
|
||||
if s.onlyExpungeDeletes != nil {
|
||||
params.Set("only_expunge_deletes", fmt.Sprintf("%v", *s.onlyExpungeDeletes))
|
||||
}
|
||||
if s.operationThreading != nil {
|
||||
params.Set("operation_threading", fmt.Sprintf("%v", s.operationThreading))
|
||||
}
|
||||
if s.waitForMerge != nil {
|
||||
params.Set("wait_for_merge", fmt.Sprintf("%v", *s.waitForMerge))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesForcemergeService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesForcemergeService) Do() (*IndicesForcemergeResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesForcemergeResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesForcemergeResponse is the response of IndicesForcemergeService.Do.
|
||||
type IndicesForcemergeResponse struct {
|
||||
Shards shardsInfo `json:"_shards"`
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesGetService retrieves information about one or more indices.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-index.html
|
||||
// for more details.
|
||||
type IndicesGetService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
feature []string
|
||||
local *bool
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
flatSettings *bool
|
||||
human *bool
|
||||
}
|
||||
|
||||
// NewIndicesGetService creates a new IndicesGetService.
|
||||
func NewIndicesGetService(client *Client) *IndicesGetService {
|
||||
return &IndicesGetService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
feature: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names.
|
||||
func (s *IndicesGetService) Index(indices ...string) *IndicesGetService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Feature is a list of features.
|
||||
func (s *IndicesGetService) Feature(features ...string) *IndicesGetService {
|
||||
s.feature = append(s.feature, features...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information, i.e. do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesGetService) Local(local bool) *IndicesGetService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether to ignore unavailable indexes (default: false).
|
||||
func (s *IndicesGetService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesGetService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard expression
|
||||
// resolves to no concrete indices (default: false).
|
||||
func (s *IndicesGetService) AllowNoIndices(allowNoIndices bool) *IndicesGetService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether wildcard expressions should get
|
||||
// expanded to open or closed indices (default: open).
|
||||
func (s *IndicesGetService) ExpandWildcards(expandWildcards string) *IndicesGetService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
/* Disabled because serialization would fail in that case. */
|
||||
/*
|
||||
// FlatSettings make the service return settings in flat format (default: false).
|
||||
func (s *IndicesGetService) FlatSettings(flatSettings bool) *IndicesGetService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
*/
|
||||
|
||||
// Human indicates whether to return version and creation date values
|
||||
// in human-readable format (default: false).
|
||||
func (s *IndicesGetService) Human(human bool) *IndicesGetService {
|
||||
s.human = &human
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesGetService) Pretty(pretty bool) *IndicesGetService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesGetService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
var index []string
|
||||
|
||||
if len(s.index) > 0 {
|
||||
index = s.index
|
||||
} else {
|
||||
index = []string{"_all"}
|
||||
}
|
||||
|
||||
if len(s.feature) > 0 {
|
||||
// Build URL
|
||||
path, err = uritemplates.Expand("/{index}/{feature}", map[string]string{
|
||||
"index": strings.Join(index, ","),
|
||||
"feature": strings.Join(s.feature, ","),
|
||||
})
|
||||
} else {
|
||||
// Build URL
|
||||
path, err = uritemplates.Expand("/{index}", map[string]string{
|
||||
"index": strings.Join(index, ","),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.human != nil {
|
||||
params.Set("human", fmt.Sprintf("%v", *s.human))
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesGetService) Validate() error {
|
||||
var invalid []string
|
||||
if len(s.index) == 0 {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesGetService) Do() (map[string]*IndicesGetResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
var ret map[string]*IndicesGetResponse
|
||||
if err := s.client.decoder.Decode(res.Body, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesGetResponse is part of the response of IndicesGetService.Do.
|
||||
type IndicesGetResponse struct {
|
||||
Aliases map[string]interface{} `json:"aliases"`
|
||||
Mappings map[string]interface{} `json:"mappings"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
Warmers map[string]interface{} `json:"warmers"`
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
type AliasesService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func NewAliasesService(client *Client) *AliasesService {
|
||||
builder := &AliasesService{
|
||||
client: client,
|
||||
indices: make([]string, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *AliasesService) Pretty(pretty bool) *AliasesService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasesService) Index(indices ...string) *AliasesService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasesService) Do() (*AliasesResult, error) {
|
||||
var err error
|
||||
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err = uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
path += strings.Join(indexPart, ",")
|
||||
|
||||
// TODO Add types here
|
||||
|
||||
// Search
|
||||
path += "/_aliases"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// {
|
||||
// "indexName" : {
|
||||
// "aliases" : {
|
||||
// "alias1" : { },
|
||||
// "alias2" : { }
|
||||
// }
|
||||
// },
|
||||
// "indexName2" : {
|
||||
// ...
|
||||
// },
|
||||
// }
|
||||
indexMap := make(map[string]interface{})
|
||||
if err := s.client.decoder.Decode(res.Body, &indexMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Each (indexName, _)
|
||||
ret := &AliasesResult{
|
||||
Indices: make(map[string]indexResult),
|
||||
}
|
||||
for indexName, indexData := range indexMap {
|
||||
indexOut, found := ret.Indices[indexName]
|
||||
if !found {
|
||||
indexOut = indexResult{Aliases: make([]aliasResult, 0)}
|
||||
}
|
||||
|
||||
// { "aliases" : { ... } }
|
||||
indexDataMap, ok := indexData.(map[string]interface{})
|
||||
if ok {
|
||||
aliasesData, ok := indexDataMap["aliases"].(map[string]interface{})
|
||||
if ok {
|
||||
for aliasName, _ := range aliasesData {
|
||||
aliasRes := aliasResult{AliasName: aliasName}
|
||||
indexOut.Aliases = append(indexOut.Aliases, aliasRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret.Indices[indexName] = indexOut
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of an alias request.
|
||||
|
||||
type AliasesResult struct {
|
||||
Indices map[string]indexResult
|
||||
}
|
||||
|
||||
type indexResult struct {
|
||||
Aliases []aliasResult
|
||||
}
|
||||
|
||||
type aliasResult struct {
|
||||
AliasName string
|
||||
}
|
||||
|
||||
func (ar AliasesResult) IndicesByAlias(aliasName string) []string {
|
||||
indices := make([]string, 0)
|
||||
|
||||
for indexName, indexInfo := range ar.Indices {
|
||||
for _, aliasInfo := range indexInfo.Aliases {
|
||||
if aliasInfo.AliasName == aliasName {
|
||||
indices = append(indices, indexName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return indices
|
||||
}
|
||||
|
||||
func (ir indexResult) HasAlias(aliasName string) bool {
|
||||
for _, alias := range ir.Aliases {
|
||||
if alias.AliasName == aliasName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesGetMappingService retrieves the mapping definitions for an index or
|
||||
// index/type.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-mapping.html
|
||||
// for details.
|
||||
type IndicesGetMappingService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
typ []string
|
||||
local *bool
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
}
|
||||
|
||||
// NewGetMappingService is an alias for NewIndicesGetMappingService.
|
||||
// Use NewIndicesGetMappingService.
|
||||
func NewGetMappingService(client *Client) *IndicesGetMappingService {
|
||||
return NewIndicesGetMappingService(client)
|
||||
}
|
||||
|
||||
// NewIndicesGetMappingService creates a new IndicesGetMappingService.
|
||||
func NewIndicesGetMappingService(client *Client) *IndicesGetMappingService {
|
||||
return &IndicesGetMappingService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
typ: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names.
|
||||
func (s *IndicesGetMappingService) Index(indices ...string) *IndicesGetMappingService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is a list of document types.
|
||||
func (s *IndicesGetMappingService) Type(types ...string) *IndicesGetMappingService {
|
||||
s.typ = append(s.typ, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// This includes `_all` string or when no indices have been specified.
|
||||
func (s *IndicesGetMappingService) AllowNoIndices(allowNoIndices bool) *IndicesGetMappingService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both..
|
||||
func (s *IndicesGetMappingService) ExpandWildcards(expandWildcards string) *IndicesGetMappingService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information, do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesGetMappingService) Local(local bool) *IndicesGetMappingService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesGetMappingService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesGetMappingService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesGetMappingService) Pretty(pretty bool) *IndicesGetMappingService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesGetMappingService) buildURL() (string, url.Values, error) {
|
||||
var index, typ []string
|
||||
|
||||
if len(s.index) > 0 {
|
||||
index = s.index
|
||||
} else {
|
||||
index = []string{"_all"}
|
||||
}
|
||||
|
||||
if len(s.typ) > 0 {
|
||||
typ = s.typ
|
||||
} else {
|
||||
typ = []string{"_all"}
|
||||
}
|
||||
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/_mapping/{type}", map[string]string{
|
||||
"index": strings.Join(index, ","),
|
||||
"type": strings.Join(typ, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesGetMappingService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation. It returns mapping definitions for an index
|
||||
// or index/type.
|
||||
func (s *IndicesGetMappingService) Do() (map[string]interface{}, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
var ret map[string]interface{}
|
||||
if err := s.client.decoder.Decode(res.Body, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesGetSettingsService allows to retrieve settings of one
|
||||
// or more indices.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-settings.html
|
||||
// for more details.
|
||||
type IndicesGetSettingsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
name []string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
flatSettings *bool
|
||||
local *bool
|
||||
}
|
||||
|
||||
// NewIndicesGetSettingsService creates a new IndicesGetSettingsService.
|
||||
func NewIndicesGetSettingsService(client *Client) *IndicesGetSettingsService {
|
||||
return &IndicesGetSettingsService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
name: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names; use `_all` or empty string to perform
|
||||
// the operation on all indices.
|
||||
func (s *IndicesGetSettingsService) Index(indices ...string) *IndicesGetSettingsService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Name are the names of the settings that should be included.
|
||||
func (s *IndicesGetSettingsService) Name(name ...string) *IndicesGetSettingsService {
|
||||
s.name = append(s.name, name...)
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should
|
||||
// be ignored when unavailable (missing or closed).
|
||||
func (s *IndicesGetSettingsService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesGetSettingsService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *IndicesGetSettingsService) AllowNoIndices(allowNoIndices bool) *IndicesGetSettingsService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression
|
||||
// to concrete indices that are open, closed or both.
|
||||
// Options: open, closed, none, all. Default: open,closed.
|
||||
func (s *IndicesGetSettingsService) ExpandWildcards(expandWildcards string) *IndicesGetSettingsService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings indicates whether to return settings in flat format (default: false).
|
||||
func (s *IndicesGetSettingsService) FlatSettings(flatSettings bool) *IndicesGetSettingsService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information, do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesGetSettingsService) Local(local bool) *IndicesGetSettingsService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesGetSettingsService) Pretty(pretty bool) *IndicesGetSettingsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesGetSettingsService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
var index []string
|
||||
|
||||
if len(s.index) > 0 {
|
||||
index = s.index
|
||||
} else {
|
||||
index = []string{"_all"}
|
||||
}
|
||||
|
||||
if len(s.name) > 0 {
|
||||
// Build URL
|
||||
path, err = uritemplates.Expand("/{index}/_settings/{name}", map[string]string{
|
||||
"index": strings.Join(index, ","),
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
} else {
|
||||
// Build URL
|
||||
path, err = uritemplates.Expand("/{index}/_settings", map[string]string{
|
||||
"index": strings.Join(index, ","),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesGetSettingsService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesGetSettingsService) Do() (map[string]*IndicesGetSettingsResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
var ret map[string]*IndicesGetSettingsResponse
|
||||
if err := s.client.decoder.Decode(res.Body, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesGetSettingsResponse is the response of IndicesGetSettingsService.Do.
|
||||
type IndicesGetSettingsResponse struct {
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesGetTemplateService returns an index template.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/indices-templates.html.
|
||||
type IndicesGetTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
name []string
|
||||
flatSettings *bool
|
||||
local *bool
|
||||
}
|
||||
|
||||
// NewIndicesGetTemplateService creates a new IndicesGetTemplateService.
|
||||
func NewIndicesGetTemplateService(client *Client) *IndicesGetTemplateService {
|
||||
return &IndicesGetTemplateService{
|
||||
client: client,
|
||||
name: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Name is the name of the index template.
|
||||
func (s *IndicesGetTemplateService) Name(name ...string) *IndicesGetTemplateService {
|
||||
s.name = append(s.name, name...)
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings is returns settings in flat format (default: false).
|
||||
func (s *IndicesGetTemplateService) FlatSettings(flatSettings bool) *IndicesGetTemplateService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates whether to return local information, i.e. do not retrieve
|
||||
// the state from master node (default: false).
|
||||
func (s *IndicesGetTemplateService) Local(local bool) *IndicesGetTemplateService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesGetTemplateService) Pretty(pretty bool) *IndicesGetTemplateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesGetTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
if len(s.name) > 0 {
|
||||
path, err = uritemplates.Expand("/_template/{name}", map[string]string{
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_template"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesGetTemplateService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesGetTemplateService) Do() (map[string]*IndicesGetTemplateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
var ret map[string]*IndicesGetTemplateResponse
|
||||
if err := s.client.decoder.Decode(res.Body, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesGetTemplateResponse is the response of IndicesGetTemplateService.Do.
|
||||
type IndicesGetTemplateResponse struct {
|
||||
Order int `json:"order,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
||||
Mappings map[string]interface{} `json:"mappings,omitempty"`
|
||||
Aliases map[string]interface{} `json:"aliases,omitempty"`
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesGetWarmerService allows to get the definition of a warmer for a
|
||||
// specific index (or alias, or several indices) based on its name.
|
||||
// The provided name can be a simple wildcard expression or omitted to get
|
||||
// all warmers.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-warmers.html
|
||||
// for more information.
|
||||
type IndicesGetWarmerService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
name []string
|
||||
typ []string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
ignoreUnavailable *bool
|
||||
local *bool
|
||||
}
|
||||
|
||||
// NewIndicesGetWarmerService creates a new IndicesGetWarmerService.
|
||||
func NewIndicesGetWarmerService(client *Client) *IndicesGetWarmerService {
|
||||
return &IndicesGetWarmerService{
|
||||
client: client,
|
||||
typ: make([]string, 0),
|
||||
index: make([]string, 0),
|
||||
name: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names to restrict the operation; use `_all` to perform the operation on all indices.
|
||||
func (s *IndicesGetWarmerService) Index(indices ...string) *IndicesGetWarmerService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Name is the name of the warmer (supports wildcards); leave empty to get all warmers.
|
||||
func (s *IndicesGetWarmerService) Name(name ...string) *IndicesGetWarmerService {
|
||||
s.name = append(s.name, name...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is a list of type names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all types.
|
||||
func (s *IndicesGetWarmerService) Type(typ ...string) *IndicesGetWarmerService {
|
||||
s.typ = append(s.typ, typ...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// This includes `_all` string or when no indices have been specified.
|
||||
func (s *IndicesGetWarmerService) AllowNoIndices(allowNoIndices bool) *IndicesGetWarmerService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesGetWarmerService) ExpandWildcards(expandWildcards string) *IndicesGetWarmerService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesGetWarmerService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesGetWarmerService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// Local indicates wether or not to return local information,
|
||||
// do not retrieve the state from master node (default: false).
|
||||
func (s *IndicesGetWarmerService) Local(local bool) *IndicesGetWarmerService {
|
||||
s.local = &local
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesGetWarmerService) Pretty(pretty bool) *IndicesGetWarmerService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesGetWarmerService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) == 0 && len(s.typ) == 0 && len(s.name) == 0 {
|
||||
path = "/_warmer"
|
||||
} else if len(s.index) == 0 && len(s.typ) == 0 && len(s.name) > 0 {
|
||||
path, err = uritemplates.Expand("/_warmer/{name}", map[string]string{
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
} else if len(s.index) == 0 && len(s.typ) > 0 && len(s.name) == 0 {
|
||||
path, err = uritemplates.Expand("/_all/{type}/_warmer", map[string]string{
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else if len(s.index) == 0 && len(s.typ) > 0 && len(s.name) > 0 {
|
||||
path, err = uritemplates.Expand("/_all/{type}/_warmer/{name}", map[string]string{
|
||||
"type": strings.Join(s.typ, ","),
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 && len(s.typ) == 0 && len(s.name) == 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_warmer", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 && len(s.typ) == 0 && len(s.name) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_warmer/{name}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 && len(s.typ) > 0 && len(s.name) == 0 {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_warmer", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 && len(s.typ) > 0 && len(s.name) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_warmer/{name}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
"name": strings.Join(s.name, ","),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.local != nil {
|
||||
params.Set("local", fmt.Sprintf("%v", *s.local))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesGetWarmerService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesGetWarmerService) Do() (map[string]interface{}, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
var ret map[string]interface{}
|
||||
if err := s.client.decoder.Decode(res.Body, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesOpenService opens an index.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html
|
||||
// for details.
|
||||
type IndicesOpenService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
timeout string
|
||||
masterTimeout string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
}
|
||||
|
||||
// NewIndicesOpenService creates and initializes a new IndicesOpenService.
|
||||
func NewIndicesOpenService(client *Client) *IndicesOpenService {
|
||||
return &IndicesOpenService{client: client}
|
||||
}
|
||||
|
||||
// Index is the name of the index to open.
|
||||
func (s *IndicesOpenService) Index(index string) *IndicesOpenService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesOpenService) Timeout(timeout string) *IndicesOpenService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesOpenService) MasterTimeout(masterTimeout string) *IndicesOpenService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should
|
||||
// be ignored when unavailable (missing or closed).
|
||||
func (s *IndicesOpenService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesOpenService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *IndicesOpenService) AllowNoIndices(allowNoIndices bool) *IndicesOpenService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both..
|
||||
func (s *IndicesOpenService) ExpandWildcards(expandWildcards string) *IndicesOpenService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesOpenService) Pretty(pretty bool) *IndicesOpenService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesOpenService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/{index}/_open", map[string]string{
|
||||
"index": s.index,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesOpenService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesOpenService) Do() (*IndicesOpenResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesOpenResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesOpenResponse is the response of IndicesOpenService.Do.
|
||||
type IndicesOpenResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type AliasService struct {
|
||||
client *Client
|
||||
actions []aliasAction
|
||||
pretty bool
|
||||
}
|
||||
|
||||
type aliasAction struct {
|
||||
// "add" or "remove"
|
||||
Type string
|
||||
// Index name
|
||||
Index string
|
||||
// Alias name
|
||||
Alias string
|
||||
// Filter
|
||||
Filter Query
|
||||
}
|
||||
|
||||
func NewAliasService(client *Client) *AliasService {
|
||||
builder := &AliasService{
|
||||
client: client,
|
||||
actions: make([]aliasAction, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *AliasService) Pretty(pretty bool) *AliasService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasService) Add(indexName string, aliasName string) *AliasService {
|
||||
action := aliasAction{Type: "add", Index: indexName, Alias: aliasName}
|
||||
s.actions = append(s.actions, action)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasService) AddWithFilter(indexName string, aliasName string, filter Query) *AliasService {
|
||||
action := aliasAction{Type: "add", Index: indexName, Alias: aliasName, Filter: filter}
|
||||
s.actions = append(s.actions, action)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasService) Remove(indexName string, aliasName string) *AliasService {
|
||||
action := aliasAction{Type: "remove", Index: indexName, Alias: aliasName}
|
||||
s.actions = append(s.actions, action)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AliasService) Do() (*AliasResult, error) {
|
||||
// Build url
|
||||
path := "/_aliases"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
|
||||
// Actions
|
||||
body := make(map[string]interface{})
|
||||
actionsJson := make([]interface{}, 0)
|
||||
|
||||
for _, action := range s.actions {
|
||||
actionJson := make(map[string]interface{})
|
||||
detailsJson := make(map[string]interface{})
|
||||
detailsJson["index"] = action.Index
|
||||
detailsJson["alias"] = action.Alias
|
||||
if action.Filter != nil {
|
||||
src, err := action.Filter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
detailsJson["filter"] = src
|
||||
}
|
||||
actionJson[action.Type] = detailsJson
|
||||
actionsJson = append(actionsJson, actionJson)
|
||||
}
|
||||
|
||||
body["actions"] = actionsJson
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return results
|
||||
ret := new(AliasResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of an alias request.
|
||||
|
||||
type AliasResult struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesPutMappingService allows to register specific mapping definition
|
||||
// for a specific type.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html
|
||||
// for details.
|
||||
type IndicesPutMappingService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
typ string
|
||||
index []string
|
||||
masterTimeout string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
ignoreConflicts *bool
|
||||
timeout string
|
||||
bodyJson map[string]interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewPutMappingService is an alias for NewIndicesPutMappingService.
|
||||
// Use NewIndicesPutMappingService.
|
||||
func NewPutMappingService(client *Client) *IndicesPutMappingService {
|
||||
return NewIndicesPutMappingService(client)
|
||||
}
|
||||
|
||||
// NewIndicesPutMappingService creates a new IndicesPutMappingService.
|
||||
func NewIndicesPutMappingService(client *Client) *IndicesPutMappingService {
|
||||
return &IndicesPutMappingService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all indices.
|
||||
func (s *IndicesPutMappingService) Index(indices ...string) *IndicesPutMappingService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the name of the document type.
|
||||
func (s *IndicesPutMappingService) Type(typ string) *IndicesPutMappingService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesPutMappingService) Timeout(timeout string) *IndicesPutMappingService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesPutMappingService) MasterTimeout(masterTimeout string) *IndicesPutMappingService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesPutMappingService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesPutMappingService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// This includes `_all` string or when no indices have been specified.
|
||||
func (s *IndicesPutMappingService) AllowNoIndices(allowNoIndices bool) *IndicesPutMappingService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesPutMappingService) ExpandWildcards(expandWildcards string) *IndicesPutMappingService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreConflicts specifies whether to ignore conflicts while updating
|
||||
// the mapping (default: false).
|
||||
func (s *IndicesPutMappingService) IgnoreConflicts(ignoreConflicts bool) *IndicesPutMappingService {
|
||||
s.ignoreConflicts = &ignoreConflicts
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesPutMappingService) Pretty(pretty bool) *IndicesPutMappingService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson contains the mapping definition.
|
||||
func (s *IndicesPutMappingService) BodyJson(mapping map[string]interface{}) *IndicesPutMappingService {
|
||||
s.bodyJson = mapping
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is the mapping definition serialized as a string.
|
||||
func (s *IndicesPutMappingService) BodyString(mapping string) *IndicesPutMappingService {
|
||||
s.bodyString = mapping
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesPutMappingService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
// Build URL: Typ MUST be specified and is verified in Validate.
|
||||
if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_mapping/{type}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": s.typ,
|
||||
})
|
||||
} else {
|
||||
path, err = uritemplates.Expand("/_mapping/{type}", map[string]string{
|
||||
"type": s.typ,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.ignoreConflicts != nil {
|
||||
params.Set("ignore_conflicts", fmt.Sprintf("%v", *s.ignoreConflicts))
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesPutMappingService) Validate() error {
|
||||
var invalid []string
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if s.bodyString == "" && s.bodyJson == nil {
|
||||
invalid = append(invalid, "BodyJson")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesPutMappingService) Do() (*PutMappingResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("PUT", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(PutMappingResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PutMappingResponse is the response of IndicesPutMappingService.Do.
|
||||
type PutMappingResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesPutSettingsService changes specific index level settings in
|
||||
// real time.
|
||||
//
|
||||
// See the documentation at
|
||||
// https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html.
|
||||
type IndicesPutSettingsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index []string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
flatSettings *bool
|
||||
ignoreUnavailable *bool
|
||||
masterTimeout string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewIndicesPutSettingsService creates a new IndicesPutSettingsService.
|
||||
func NewIndicesPutSettingsService(client *Client) *IndicesPutSettingsService {
|
||||
return &IndicesPutSettingsService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all indices.
|
||||
func (s *IndicesPutSettingsService) Index(indices ...string) *IndicesPutSettingsService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices. (This includes `_all`
|
||||
// string or when no indices have been specified).
|
||||
func (s *IndicesPutSettingsService) AllowNoIndices(allowNoIndices bool) *IndicesPutSettingsService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards specifies whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesPutSettingsService) ExpandWildcards(expandWildcards string) *IndicesPutSettingsService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings indicates whether to return settings in flat format (default: false).
|
||||
func (s *IndicesPutSettingsService) FlatSettings(flatSettings bool) *IndicesPutSettingsService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable specifies whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesPutSettingsService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesPutSettingsService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout is the timeout for connection to master.
|
||||
func (s *IndicesPutSettingsService) MasterTimeout(masterTimeout string) *IndicesPutSettingsService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesPutSettingsService) Pretty(pretty bool) *IndicesPutSettingsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson is documented as: The index settings to be updated.
|
||||
func (s *IndicesPutSettingsService) BodyJson(body interface{}) *IndicesPutSettingsService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is documented as: The index settings to be updated.
|
||||
func (s *IndicesPutSettingsService) BodyString(body string) *IndicesPutSettingsService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesPutSettingsService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_settings", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_settings"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesPutSettingsService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesPutSettingsService) Do() (*IndicesPutSettingsResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("PUT", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesPutSettingsResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesPutSettingsResponse is the response of IndicesPutSettingsService.Do.
|
||||
type IndicesPutSettingsResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesPutTemplateService creates or updates index mappings.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/indices-templates.html.
|
||||
type IndicesPutTemplateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
name string
|
||||
order interface{}
|
||||
create *bool
|
||||
timeout string
|
||||
masterTimeout string
|
||||
flatSettings *bool
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewIndicesPutTemplateService creates a new IndicesPutTemplateService.
|
||||
func NewIndicesPutTemplateService(client *Client) *IndicesPutTemplateService {
|
||||
return &IndicesPutTemplateService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Name is the name of the index template.
|
||||
func (s *IndicesPutTemplateService) Name(name string) *IndicesPutTemplateService {
|
||||
s.name = name
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is an explicit operation timeout.
|
||||
func (s *IndicesPutTemplateService) Timeout(timeout string) *IndicesPutTemplateService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesPutTemplateService) MasterTimeout(masterTimeout string) *IndicesPutTemplateService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings indicates whether to return settings in flat format (default: false).
|
||||
func (s *IndicesPutTemplateService) FlatSettings(flatSettings bool) *IndicesPutTemplateService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// Order is the order for this template when merging multiple matching ones
|
||||
// (higher numbers are merged later, overriding the lower numbers).
|
||||
func (s *IndicesPutTemplateService) Order(order interface{}) *IndicesPutTemplateService {
|
||||
s.order = order
|
||||
return s
|
||||
}
|
||||
|
||||
// Create indicates whether the index template should only be added if
|
||||
// new or can also replace an existing one.
|
||||
func (s *IndicesPutTemplateService) Create(create bool) *IndicesPutTemplateService {
|
||||
s.create = &create
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesPutTemplateService) Pretty(pretty bool) *IndicesPutTemplateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson is documented as: The template definition.
|
||||
func (s *IndicesPutTemplateService) BodyJson(body interface{}) *IndicesPutTemplateService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is documented as: The template definition.
|
||||
func (s *IndicesPutTemplateService) BodyString(body string) *IndicesPutTemplateService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesPutTemplateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_template/{name}", map[string]string{
|
||||
"name": s.name,
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.order != nil {
|
||||
params.Set("order", fmt.Sprintf("%v", s.order))
|
||||
}
|
||||
if s.create != nil {
|
||||
params.Set("create", fmt.Sprintf("%v", *s.create))
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesPutTemplateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.name == "" {
|
||||
invalid = append(invalid, "Name")
|
||||
}
|
||||
if s.bodyString == "" && s.bodyJson == nil {
|
||||
invalid = append(invalid, "BodyJson")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesPutTemplateService) Do() (*IndicesPutTemplateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("PUT", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesPutTemplateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesPutTemplateResponse is the response of IndicesPutTemplateService.Do.
|
||||
type IndicesPutTemplateResponse struct {
|
||||
Acknowledged bool `json:"acknowledged,omitempty"`
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesPutWarmerService allows to register a warmer.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-warmers.html.
|
||||
type IndicesPutWarmerService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
typ []string
|
||||
index []string
|
||||
name string
|
||||
masterTimeout string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
requestCache *bool
|
||||
expandWildcards string
|
||||
bodyJson map[string]interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewIndicesPutWarmerService creates a new IndicesPutWarmerService.
|
||||
func NewIndicesPutWarmerService(client *Client) *IndicesPutWarmerService {
|
||||
return &IndicesPutWarmerService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
typ: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is a list of index names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all indices.
|
||||
func (s *IndicesPutWarmerService) Index(indices ...string) *IndicesPutWarmerService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is a list of type names the mapping should be added to
|
||||
// (supports wildcards); use `_all` or omit to add the mapping on all types.
|
||||
func (s *IndicesPutWarmerService) Type(typ ...string) *IndicesPutWarmerService {
|
||||
s.typ = append(s.typ, typ...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Name specifies the name of the warmer (supports wildcards);
|
||||
// leave empty to get all warmers
|
||||
func (s *IndicesPutWarmerService) Name(name string) *IndicesPutWarmerService {
|
||||
s.name = name
|
||||
return s
|
||||
}
|
||||
|
||||
// MasterTimeout specifies the timeout for connection to master.
|
||||
func (s *IndicesPutWarmerService) MasterTimeout(masterTimeout string) *IndicesPutWarmerService {
|
||||
s.masterTimeout = masterTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should be
|
||||
// ignored when unavailable (missing or closed).
|
||||
func (s *IndicesPutWarmerService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesPutWarmerService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// This includes `_all` string or when no indices have been specified.
|
||||
func (s *IndicesPutWarmerService) AllowNoIndices(allowNoIndices bool) *IndicesPutWarmerService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// RequestCache specifies whether the request to be warmed should use the request cache,
|
||||
// defaults to index level setting
|
||||
func (s *IndicesPutWarmerService) RequestCache(requestCache bool) *IndicesPutWarmerService {
|
||||
s.requestCache = &requestCache
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *IndicesPutWarmerService) ExpandWildcards(expandWildcards string) *IndicesPutWarmerService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesPutWarmerService) Pretty(pretty bool) *IndicesPutWarmerService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson contains the mapping definition.
|
||||
func (s *IndicesPutWarmerService) BodyJson(mapping map[string]interface{}) *IndicesPutWarmerService {
|
||||
s.bodyJson = mapping
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is the mapping definition serialized as a string.
|
||||
func (s *IndicesPutWarmerService) BodyString(mapping string) *IndicesPutWarmerService {
|
||||
s.bodyString = mapping
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesPutWarmerService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) == 0 && len(s.typ) == 0 {
|
||||
path, err = uritemplates.Expand("/_warmer/{name}", map[string]string{
|
||||
"name": s.name,
|
||||
})
|
||||
} else if len(s.index) == 0 && len(s.typ) > 0 {
|
||||
path, err = uritemplates.Expand("/_all/{type}/_warmer/{name}", map[string]string{
|
||||
"type": strings.Join(s.typ, ","),
|
||||
"name": s.name,
|
||||
})
|
||||
} else if len(s.index) > 0 && len(s.typ) == 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_warmer/{name}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"name": s.name,
|
||||
})
|
||||
} else {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_warmer/{name}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
"name": s.name,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.requestCache != nil {
|
||||
params.Set("request_cache", fmt.Sprintf("%v", *s.requestCache))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.masterTimeout != "" {
|
||||
params.Set("master_timeout", s.masterTimeout)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesPutWarmerService) Validate() error {
|
||||
var invalid []string
|
||||
if s.name == "" {
|
||||
invalid = append(invalid, "Name")
|
||||
}
|
||||
if s.bodyString == "" && s.bodyJson == nil {
|
||||
invalid = append(invalid, "BodyJson")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesPutWarmerService) Do() (*PutWarmerResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("PUT", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(PutWarmerResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PutWarmerResponse is the response of IndicesPutWarmerService.Do.
|
||||
type PutWarmerResponse struct {
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
type RefreshService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
force *bool
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func NewRefreshService(client *Client) *RefreshService {
|
||||
builder := &RefreshService{
|
||||
client: client,
|
||||
indices: make([]string, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *RefreshService) Index(indices ...string) *RefreshService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *RefreshService) Force(force bool) *RefreshService {
|
||||
s.force = &force
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *RefreshService) Pretty(pretty bool) *RefreshService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *RefreshService) Do() (*RefreshResult, error) {
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err := uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
if len(indexPart) > 0 {
|
||||
path += strings.Join(indexPart, ",")
|
||||
}
|
||||
|
||||
path += "/_refresh"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.force != nil {
|
||||
params.Set("force", fmt.Sprintf("%v", *s.force))
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(RefreshResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of a refresh request.
|
||||
|
||||
type RefreshResult struct {
|
||||
Shards shardsInfo `json:"_shards,omitempty"`
|
||||
}
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// IndicesStatsService provides stats on various metrics of one or more
|
||||
// indices. See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-stats.html.
|
||||
type IndicesStatsService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
metric []string
|
||||
index []string
|
||||
level string
|
||||
types []string
|
||||
completionFields []string
|
||||
fielddataFields []string
|
||||
fields []string
|
||||
groups []string
|
||||
human *bool
|
||||
}
|
||||
|
||||
// NewIndicesStatsService creates a new IndicesStatsService.
|
||||
func NewIndicesStatsService(client *Client) *IndicesStatsService {
|
||||
return &IndicesStatsService{
|
||||
client: client,
|
||||
index: make([]string, 0),
|
||||
metric: make([]string, 0),
|
||||
completionFields: make([]string, 0),
|
||||
fielddataFields: make([]string, 0),
|
||||
fields: make([]string, 0),
|
||||
groups: make([]string, 0),
|
||||
types: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Metric limits the information returned the specific metrics. Options are:
|
||||
// docs, store, indexing, get, search, completion, fielddata, flush, merge,
|
||||
// query_cache, refresh, suggest, and warmer.
|
||||
func (s *IndicesStatsService) Metric(metric ...string) *IndicesStatsService {
|
||||
s.metric = append(s.metric, metric...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Index is the list of index names; use `_all` or empty string to perform
|
||||
// the operation on all indices.
|
||||
func (s *IndicesStatsService) Index(indices ...string) *IndicesStatsService {
|
||||
s.index = append(s.index, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is a list of document types for the `indexing` index metric.
|
||||
func (s *IndicesStatsService) Type(types ...string) *IndicesStatsService {
|
||||
s.types = append(s.types, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Level returns stats aggregated at cluster, index or shard level.
|
||||
func (s *IndicesStatsService) Level(level string) *IndicesStatsService {
|
||||
s.level = level
|
||||
return s
|
||||
}
|
||||
|
||||
// CompletionFields is a list of fields for `fielddata` and `suggest`
|
||||
// index metric (supports wildcards).
|
||||
func (s *IndicesStatsService) CompletionFields(completionFields ...string) *IndicesStatsService {
|
||||
s.completionFields = append(s.completionFields, completionFields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// FielddataFields is a list of fields for `fielddata` index metric (supports wildcards).
|
||||
func (s *IndicesStatsService) FielddataFields(fielddataFields ...string) *IndicesStatsService {
|
||||
s.fielddataFields = append(s.fielddataFields, fielddataFields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields is a list of fields for `fielddata` and `completion` index metric
|
||||
// (supports wildcards).
|
||||
func (s *IndicesStatsService) Fields(fields ...string) *IndicesStatsService {
|
||||
s.fields = append(s.fields, fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Groups is a list of search groups for `search` index metric.
|
||||
func (s *IndicesStatsService) Groups(groups ...string) *IndicesStatsService {
|
||||
s.groups = append(s.groups, groups...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Human indicates whether to return time and byte values in human-readable format..
|
||||
func (s *IndicesStatsService) Human(human bool) *IndicesStatsService {
|
||||
s.human = &human
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *IndicesStatsService) Pretty(pretty bool) *IndicesStatsService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *IndicesStatsService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
if len(s.index) > 0 && len(s.metric) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_stats/{metric}", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"metric": strings.Join(s.metric, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_stats", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else if len(s.metric) > 0 {
|
||||
path, err = uritemplates.Expand("/_stats/{metric}", map[string]string{
|
||||
"metric": strings.Join(s.metric, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_stats"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if len(s.groups) > 0 {
|
||||
params.Set("groups", strings.Join(s.groups, ","))
|
||||
}
|
||||
if s.human != nil {
|
||||
params.Set("human", fmt.Sprintf("%v", *s.human))
|
||||
}
|
||||
if s.level != "" {
|
||||
params.Set("level", s.level)
|
||||
}
|
||||
if len(s.types) > 0 {
|
||||
params.Set("types", strings.Join(s.types, ","))
|
||||
}
|
||||
if len(s.completionFields) > 0 {
|
||||
params.Set("completion_fields", strings.Join(s.completionFields, ","))
|
||||
}
|
||||
if len(s.fielddataFields) > 0 {
|
||||
params.Set("fielddata_fields", strings.Join(s.fielddataFields, ","))
|
||||
}
|
||||
if len(s.fields) > 0 {
|
||||
params.Set("fields", strings.Join(s.fields, ","))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *IndicesStatsService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *IndicesStatsService) Do() (*IndicesStatsResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(IndicesStatsResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IndicesStatsResponse is the response of IndicesStatsService.Do.
|
||||
type IndicesStatsResponse struct {
|
||||
// Shards provides information returned from shards.
|
||||
Shards shardsInfo `json:"_shards"`
|
||||
|
||||
// All provides summary stats about all indices.
|
||||
All *IndexStats `json:"_all,omitempty"`
|
||||
|
||||
// Indices provides a map into the stats of an index. The key of the
|
||||
// map is the index name.
|
||||
Indices map[string]*IndexStats `json:"indices,omitempty"`
|
||||
}
|
||||
|
||||
// IndexStats is index stats for a specific index.
|
||||
type IndexStats struct {
|
||||
Primaries *IndexStatsDetails `json:"primaries,omitempty"`
|
||||
Total *IndexStatsDetails `json:"total,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsDetails struct {
|
||||
Docs *IndexStatsDocs `json:"docs,omitempty"`
|
||||
Store *IndexStatsStore `json:"store,omitempty"`
|
||||
Indexing *IndexStatsIndexing `json:"indexing,omitempty"`
|
||||
Get *IndexStatsGet `json:"get,omitempty"`
|
||||
Search *IndexStatsSearch `json:"search,omitempty"`
|
||||
Merges *IndexStatsMerges `json:"merges,omitempty"`
|
||||
Refresh *IndexStatsRefresh `json:"refresh,omitempty"`
|
||||
Flush *IndexStatsFlush `json:"flush,omitempty"`
|
||||
Warmer *IndexStatsWarmer `json:"warmer,omitempty"`
|
||||
FilterCache *IndexStatsFilterCache `json:"filter_cache,omitempty"`
|
||||
IdCache *IndexStatsIdCache `json:"id_cache,omitempty"`
|
||||
Fielddata *IndexStatsFielddata `json:"fielddata,omitempty"`
|
||||
Percolate *IndexStatsPercolate `json:"percolate,omitempty"`
|
||||
Completion *IndexStatsCompletion `json:"completion,omitempty"`
|
||||
Segments *IndexStatsSegments `json:"segments,omitempty"`
|
||||
Translog *IndexStatsTranslog `json:"translog,omitempty"`
|
||||
Suggest *IndexStatsSuggest `json:"suggest,omitempty"`
|
||||
QueryCache *IndexStatsQueryCache `json:"query_cache,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsDocs struct {
|
||||
Count int64 `json:"count,omitempty"`
|
||||
Deleted int64 `json:"deleted,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsStore struct {
|
||||
Size string `json:"size,omitempty"` // human size, e.g. 119.3mb
|
||||
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
|
||||
ThrottleTime string `json:"throttle_time,omitempty"` // human time, e.g. 0s
|
||||
ThrottleTimeInMillis int64 `json:"throttle_time_in_millis,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsIndexing struct {
|
||||
IndexTotal int64 `json:"index_total,omitempty"`
|
||||
IndexTime string `json:"index_time,omitempty"`
|
||||
IndexTimeInMillis int64 `json:"index_time_in_millis,omitempty"`
|
||||
IndexCurrent int64 `json:"index_current,omitempty"`
|
||||
DeleteTotal int64 `json:"delete_total,omitempty"`
|
||||
DeleteTime string `json:"delete_time,omitempty"`
|
||||
DeleteTimeInMillis int64 `json:"delete_time_in_millis,omitempty"`
|
||||
DeleteCurrent int64 `json:"delete_current,omitempty"`
|
||||
NoopUpdateTotal int64 `json:"noop_update_total,omitempty"`
|
||||
IsThrottled bool `json:"is_throttled,omitempty"`
|
||||
ThrottleTime string `json:"throttle_time,omitempty"`
|
||||
ThrottleTimeInMillis int64 `json:"throttle_time_in_millis,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsGet struct {
|
||||
Total int64 `json:"total,omitempty"`
|
||||
GetTime string `json:"get_time,omitempty"`
|
||||
TimeInMillis int64 `json:"time_in_millis,omitempty"`
|
||||
ExistsTotal int64 `json:"exists_total,omitempty"`
|
||||
ExistsTime string `json:"exists_time,omitempty"`
|
||||
ExistsTimeInMillis int64 `json:"exists_time_in_millis,omitempty"`
|
||||
MissingTotal int64 `json:"missing_total,omitempty"`
|
||||
MissingTime string `json:"missing_time,omitempty"`
|
||||
MissingTimeInMillis int64 `json:"missing_time_in_millis,omitempty"`
|
||||
Current int64 `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsSearch struct {
|
||||
OpenContexts int64 `json:"open_contexts,omitempty"`
|
||||
QueryTotal int64 `json:"query_total,omitempty"`
|
||||
QueryTime string `json:"query_time,omitempty"`
|
||||
QueryTimeInMillis int64 `json:"query_time_in_millis,omitempty"`
|
||||
QueryCurrent int64 `json:"query_current,omitempty"`
|
||||
FetchTotal int64 `json:"fetch_total,omitempty"`
|
||||
FetchTime string `json:"fetch_time,omitempty"`
|
||||
FetchTimeInMillis int64 `json:"fetch_time_in_millis,omitempty"`
|
||||
FetchCurrent int64 `json:"fetch_current,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsMerges struct {
|
||||
Current int64 `json:"current,omitempty"`
|
||||
CurrentDocs int64 `json:"current_docs,omitempty"`
|
||||
CurrentSize string `json:"current_size,omitempty"`
|
||||
CurrentSizeInBytes int64 `json:"current_size_in_bytes,omitempty"`
|
||||
Total int64 `json:"total,omitempty"`
|
||||
TotalTime string `json:"total_time,omitempty"`
|
||||
TotalTimeInMillis int64 `json:"total_time_in_millis,omitempty"`
|
||||
TotalDocs int64 `json:"total_docs,omitempty"`
|
||||
TotalSize string `json:"total_size,omitempty"`
|
||||
TotalSizeInBytes int64 `json:"total_size_in_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsRefresh struct {
|
||||
Total int64 `json:"total,omitempty"`
|
||||
TotalTime string `json:"total_time,omitempty"`
|
||||
TotalTimeInMillis int64 `json:"total_time_in_millis,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsFlush struct {
|
||||
Total int64 `json:"total,omitempty"`
|
||||
TotalTime string `json:"total_time,omitempty"`
|
||||
TotalTimeInMillis int64 `json:"total_time_in_millis,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsWarmer struct {
|
||||
Current int64 `json:"current,omitempty"`
|
||||
Total int64 `json:"total,omitempty"`
|
||||
TotalTime string `json:"total_time,omitempty"`
|
||||
TotalTimeInMillis int64 `json:"total_time_in_millis,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsFilterCache struct {
|
||||
MemorySize string `json:"memory_size,omitempty"`
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes,omitempty"`
|
||||
Evictions int64 `json:"evictions,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsIdCache struct {
|
||||
MemorySize string `json:"memory_size,omitempty"`
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsFielddata struct {
|
||||
MemorySize string `json:"memory_size,omitempty"`
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes,omitempty"`
|
||||
Evictions int64 `json:"evictions,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsPercolate struct {
|
||||
Total int64 `json:"total,omitempty"`
|
||||
GetTime string `json:"get_time,omitempty"`
|
||||
TimeInMillis int64 `json:"time_in_millis,omitempty"`
|
||||
Current int64 `json:"current,omitempty"`
|
||||
MemorySize string `json:"memory_size,omitempty"`
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes,omitempty"`
|
||||
Queries int64 `json:"queries,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsCompletion struct {
|
||||
Size string `json:"size,omitempty"`
|
||||
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsSegments struct {
|
||||
Count int64 `json:"count,omitempty"`
|
||||
Memory string `json:"memory,omitempty"`
|
||||
MemoryInBytes int64 `json:"memory_in_bytes,omitempty"`
|
||||
IndexWriterMemory string `json:"index_writer_memory,omitempty"`
|
||||
IndexWriterMemoryInBytes int64 `json:"index_writer_memory_in_bytes,omitempty"`
|
||||
IndexWriterMaxMemory string `json:"index_writer_max_memory,omitempty"`
|
||||
IndexWriterMaxMemoryInBytes int64 `json:"index_writer_max_memory_in_bytes,omitempty"`
|
||||
VersionMapMemory string `json:"version_map_memory,omitempty"`
|
||||
VersionMapMemoryInBytes int64 `json:"version_map_memory_in_bytes,omitempty"`
|
||||
FixedBitSetMemory string `json:"fixed_bit_set,omitempty"`
|
||||
FixedBitSetMemoryInBytes int64 `json:"fixed_bit_set_memory_in_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsTranslog struct {
|
||||
Operations int64 `json:"operations,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsSuggest struct {
|
||||
Total int64 `json:"total,omitempty"`
|
||||
Time string `json:"time,omitempty"`
|
||||
TimeInMillis int64 `json:"time_in_millis,omitempty"`
|
||||
Current int64 `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
type IndexStatsQueryCache struct {
|
||||
MemorySize string `json:"memory_size,omitempty"`
|
||||
MemorySizeInBytes int64 `json:"memory_size_in_bytes,omitempty"`
|
||||
Evictions int64 `json:"evictions,omitempty"`
|
||||
HitCount int64 `json:"hit_count,omitempty"`
|
||||
MissCount int64 `json:"miss_count,omitempty"`
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// InnerHit implements a simple join for parent/child, nested, and even
|
||||
// top-level documents in Elasticsearch.
|
||||
// It is an experimental feature for Elasticsearch versions 1.5 (or greater).
|
||||
// See http://www.elastic.co/guide/en/elasticsearch/reference/1.5/search-request-inner-hits.html
|
||||
// for documentation.
|
||||
//
|
||||
// See the tests for SearchSource, HasChildFilter, HasChildQuery,
|
||||
// HasParentFilter, HasParentQuery, NestedFilter, and NestedQuery
|
||||
// for usage examples.
|
||||
type InnerHit struct {
|
||||
source *SearchSource
|
||||
path string
|
||||
typ string
|
||||
|
||||
name string
|
||||
}
|
||||
|
||||
// NewInnerHit creates a new InnerHit.
|
||||
func NewInnerHit() *InnerHit {
|
||||
return &InnerHit{source: NewSearchSource()}
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Path(path string) *InnerHit {
|
||||
hit.path = path
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Type(typ string) *InnerHit {
|
||||
hit.typ = typ
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Query(query Query) *InnerHit {
|
||||
hit.source.Query(query)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) From(from int) *InnerHit {
|
||||
hit.source.From(from)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Size(size int) *InnerHit {
|
||||
hit.source.Size(size)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) TrackScores(trackScores bool) *InnerHit {
|
||||
hit.source.TrackScores(trackScores)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Explain(explain bool) *InnerHit {
|
||||
hit.source.Explain(explain)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Version(version bool) *InnerHit {
|
||||
hit.source.Version(version)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Field(fieldName string) *InnerHit {
|
||||
hit.source.Field(fieldName)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Fields(fieldNames ...string) *InnerHit {
|
||||
hit.source.Fields(fieldNames...)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) NoFields() *InnerHit {
|
||||
hit.source.NoFields()
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) FetchSource(fetchSource bool) *InnerHit {
|
||||
hit.source.FetchSource(fetchSource)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) FetchSourceContext(fetchSourceContext *FetchSourceContext) *InnerHit {
|
||||
hit.source.FetchSourceContext(fetchSourceContext)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) FieldDataFields(fieldDataFields ...string) *InnerHit {
|
||||
hit.source.FieldDataFields(fieldDataFields...)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) FieldDataField(fieldDataField string) *InnerHit {
|
||||
hit.source.FieldDataField(fieldDataField)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) ScriptFields(scriptFields ...*ScriptField) *InnerHit {
|
||||
hit.source.ScriptFields(scriptFields...)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) ScriptField(scriptField *ScriptField) *InnerHit {
|
||||
hit.source.ScriptField(scriptField)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Sort(field string, ascending bool) *InnerHit {
|
||||
hit.source.Sort(field, ascending)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) SortWithInfo(info SortInfo) *InnerHit {
|
||||
hit.source.SortWithInfo(info)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) SortBy(sorter ...Sorter) *InnerHit {
|
||||
hit.source.SortBy(sorter...)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Highlight(highlight *Highlight) *InnerHit {
|
||||
hit.source.Highlight(highlight)
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Highlighter() *Highlight {
|
||||
return hit.source.Highlighter()
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Name(name string) *InnerHit {
|
||||
hit.name = name
|
||||
return hit
|
||||
}
|
||||
|
||||
func (hit *InnerHit) Source() (interface{}, error) {
|
||||
src, err := hit.source.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source, ok := src.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Notice that hit.typ and hit.path are not exported here.
|
||||
// They are only used with SearchSource and serialized there.
|
||||
|
||||
if hit.name != "" {
|
||||
source["name"] = hit.name
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// Logger specifies the interface for all log operations.
|
||||
type Logger interface {
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// MgetService allows to get multiple documents based on an index,
|
||||
// type (optional) and id (possibly routing). The response includes
|
||||
// a docs array with all the fetched documents, each element similar
|
||||
// in structure to a document provided by the Get API.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html
|
||||
// for details.
|
||||
type MgetService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
preference string
|
||||
realtime *bool
|
||||
refresh *bool
|
||||
items []*MultiGetItem
|
||||
}
|
||||
|
||||
func NewMgetService(client *Client) *MgetService {
|
||||
builder := &MgetService{
|
||||
client: client,
|
||||
items: make([]*MultiGetItem, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (b *MgetService) Preference(preference string) *MgetService {
|
||||
b.preference = preference
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *MgetService) Refresh(refresh bool) *MgetService {
|
||||
b.refresh = &refresh
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *MgetService) Realtime(realtime bool) *MgetService {
|
||||
b.realtime = &realtime
|
||||
return b
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *MgetService) Pretty(pretty bool) *MgetService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (b *MgetService) Add(items ...*MultiGetItem) *MgetService {
|
||||
b.items = append(b.items, items...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *MgetService) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
items := make([]interface{}, len(b.items))
|
||||
for i, item := range b.items {
|
||||
src, err := item.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items[i] = src
|
||||
}
|
||||
source["docs"] = items
|
||||
return source, nil
|
||||
}
|
||||
|
||||
func (b *MgetService) Do() (*MgetResponse, error) {
|
||||
// Build url
|
||||
path := "/_mget"
|
||||
|
||||
params := make(url.Values)
|
||||
if b.realtime != nil {
|
||||
params.Add("realtime", fmt.Sprintf("%v", *b.realtime))
|
||||
}
|
||||
if b.preference != "" {
|
||||
params.Add("preference", b.preference)
|
||||
}
|
||||
if b.refresh != nil {
|
||||
params.Add("refresh", fmt.Sprintf("%v", *b.refresh))
|
||||
}
|
||||
|
||||
// Set body
|
||||
body, err := b.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := b.client.PerformRequest("GET", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(MgetResponse)
|
||||
if err := b.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Multi Get Item --
|
||||
|
||||
// MultiGetItem is a single document to retrieve via the MgetService.
|
||||
type MultiGetItem struct {
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
routing string
|
||||
fields []string
|
||||
version *int64 // see org.elasticsearch.common.lucene.uid.Versions
|
||||
versionType string // see org.elasticsearch.index.VersionType
|
||||
fsc *FetchSourceContext
|
||||
}
|
||||
|
||||
func NewMultiGetItem() *MultiGetItem {
|
||||
return &MultiGetItem{}
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) Index(index string) *MultiGetItem {
|
||||
item.index = index
|
||||
return item
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) Type(typ string) *MultiGetItem {
|
||||
item.typ = typ
|
||||
return item
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) Id(id string) *MultiGetItem {
|
||||
item.id = id
|
||||
return item
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) Routing(routing string) *MultiGetItem {
|
||||
item.routing = routing
|
||||
return item
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) Fields(fields ...string) *MultiGetItem {
|
||||
if item.fields == nil {
|
||||
item.fields = make([]string, 0)
|
||||
}
|
||||
item.fields = append(item.fields, fields...)
|
||||
return item
|
||||
}
|
||||
|
||||
// Version can be MatchAny (-3), MatchAnyPre120 (0), NotFound (-1),
|
||||
// or NotSet (-2). These are specified in org.elasticsearch.common.lucene.uid.Versions.
|
||||
// The default in Elasticsearch is MatchAny (-3).
|
||||
func (item *MultiGetItem) Version(version int64) *MultiGetItem {
|
||||
item.version = &version
|
||||
return item
|
||||
}
|
||||
|
||||
// VersionType can be "internal", "external", "external_gt", "external_gte",
|
||||
// or "force". See org.elasticsearch.index.VersionType in Elasticsearch source.
|
||||
// It is "internal" by default.
|
||||
func (item *MultiGetItem) VersionType(versionType string) *MultiGetItem {
|
||||
item.versionType = versionType
|
||||
return item
|
||||
}
|
||||
|
||||
func (item *MultiGetItem) FetchSource(fetchSourceContext *FetchSourceContext) *MultiGetItem {
|
||||
item.fsc = fetchSourceContext
|
||||
return item
|
||||
}
|
||||
|
||||
// Source returns the serialized JSON to be sent to Elasticsearch as
|
||||
// part of a MultiGet search.
|
||||
func (item *MultiGetItem) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
|
||||
source["_id"] = item.id
|
||||
|
||||
if item.index != "" {
|
||||
source["_index"] = item.index
|
||||
}
|
||||
if item.typ != "" {
|
||||
source["_type"] = item.typ
|
||||
}
|
||||
if item.fsc != nil {
|
||||
src, err := item.fsc.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["_source"] = src
|
||||
}
|
||||
if item.fields != nil {
|
||||
source["fields"] = item.fields
|
||||
}
|
||||
if item.routing != "" {
|
||||
source["_routing"] = item.routing
|
||||
}
|
||||
if item.version != nil {
|
||||
source["version"] = fmt.Sprintf("%d", *item.version)
|
||||
}
|
||||
if item.versionType != "" {
|
||||
source["version_type"] = item.versionType
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Result of a Multi Get request.
|
||||
|
||||
type MgetResponse struct {
|
||||
Docs []*GetResult `json:"docs,omitempty"`
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MultiSearch executes one or more searches in one roundtrip.
|
||||
// See http://www.elasticsearch.org/guide/reference/api/multi-search/
|
||||
type MultiSearchService struct {
|
||||
client *Client
|
||||
requests []*SearchRequest
|
||||
indices []string
|
||||
pretty bool
|
||||
routing string
|
||||
preference string
|
||||
}
|
||||
|
||||
func NewMultiSearchService(client *Client) *MultiSearchService {
|
||||
builder := &MultiSearchService{
|
||||
client: client,
|
||||
requests: make([]*SearchRequest, 0),
|
||||
indices: make([]string, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) Add(requests ...*SearchRequest) *MultiSearchService {
|
||||
s.requests = append(s.requests, requests...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) Index(indices ...string) *MultiSearchService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) Pretty(pretty bool) *MultiSearchService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) Do() (*MultiSearchResult, error) {
|
||||
// Build url
|
||||
path := "/_msearch"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
|
||||
// Set body
|
||||
lines := make([]string, 0)
|
||||
for _, sr := range s.requests {
|
||||
// Set default indices if not specified in the request
|
||||
if !sr.HasIndices() && len(s.indices) > 0 {
|
||||
sr = sr.Index(s.indices...)
|
||||
}
|
||||
|
||||
header, err := json.Marshal(sr.header())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(sr.body())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines = append(lines, string(header))
|
||||
lines = append(lines, string(body))
|
||||
}
|
||||
body := strings.Join(lines, "\n") + "\n" // Don't forget trailing \n
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("GET", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(MultiSearchResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type MultiSearchResult struct {
|
||||
Responses []*SearchResult `json:"responses,omitempty"`
|
||||
}
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// MultiTermvectorService returns information and statistics on terms in the
|
||||
// fields of a particular document. The document could be stored in the
|
||||
// index or artificially provided by the user.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-termvectors.html
|
||||
// for documentation.
|
||||
type MultiTermvectorService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
typ string
|
||||
fieldStatistics *bool
|
||||
fields []string
|
||||
ids []string
|
||||
offsets *bool
|
||||
parent string
|
||||
payloads *bool
|
||||
positions *bool
|
||||
preference string
|
||||
realtime *bool
|
||||
routing string
|
||||
termStatistics *bool
|
||||
version interface{}
|
||||
versionType string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
docs []*MultiTermvectorItem
|
||||
}
|
||||
|
||||
// NewMultiTermvectorService creates a new MultiTermvectorService.
|
||||
func NewMultiTermvectorService(client *Client) *MultiTermvectorService {
|
||||
return &MultiTermvectorService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *MultiTermvectorService) Pretty(pretty bool) *MultiTermvectorService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds documents to MultiTermvectors service.
|
||||
func (s *MultiTermvectorService) Add(docs ...*MultiTermvectorItem) *MultiTermvectorService {
|
||||
s.docs = append(s.docs, docs...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Index in which the document resides.
|
||||
func (s *MultiTermvectorService) Index(index string) *MultiTermvectorService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type of the document.
|
||||
func (s *MultiTermvectorService) Type(typ string) *MultiTermvectorService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// FieldStatistics specifies if document count, sum of document frequencies and sum of total term frequencies should be returned. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) FieldStatistics(fieldStatistics bool) *MultiTermvectorService {
|
||||
s.fieldStatistics = &fieldStatistics
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields is a comma-separated list of fields to return. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Fields(fields []string) *MultiTermvectorService {
|
||||
s.fields = fields
|
||||
return s
|
||||
}
|
||||
|
||||
// Ids is a comma-separated list of documents ids. You must define ids as parameter or set "ids" or "docs" in the request body.
|
||||
func (s *MultiTermvectorService) Ids(ids []string) *MultiTermvectorService {
|
||||
s.ids = ids
|
||||
return s
|
||||
}
|
||||
|
||||
// Offsets specifies if term offsets should be returned. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Offsets(offsets bool) *MultiTermvectorService {
|
||||
s.offsets = &offsets
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent id of documents. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Parent(parent string) *MultiTermvectorService {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Payloads specifies if term payloads should be returned. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Payloads(payloads bool) *MultiTermvectorService {
|
||||
s.payloads = &payloads
|
||||
return s
|
||||
}
|
||||
|
||||
// Positions specifies if term positions should be returned. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Positions(positions bool) *MultiTermvectorService {
|
||||
s.positions = &positions
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be performed on (default: random). Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Preference(preference string) *MultiTermvectorService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Realtime specifies if requests are real-time as opposed to near-real-time (default: true).
|
||||
func (s *MultiTermvectorService) Realtime(realtime bool) *MultiTermvectorService {
|
||||
s.realtime = &realtime
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing specific routing value. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) Routing(routing string) *MultiTermvectorService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// TermStatistics specifies if total term frequency and document frequency should be returned. Applies to all returned documents unless otherwise specified in body "params" or "docs".
|
||||
func (s *MultiTermvectorService) TermStatistics(termStatistics bool) *MultiTermvectorService {
|
||||
s.termStatistics = &termStatistics
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is explicit version number for concurrency control.
|
||||
func (s *MultiTermvectorService) Version(version interface{}) *MultiTermvectorService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is specific version type.
|
||||
func (s *MultiTermvectorService) VersionType(versionType string) *MultiTermvectorService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson is documented as: Define ids, documents, parameters or a list of parameters per document here. You must at least provide a list of document ids. See documentation..
|
||||
func (s *MultiTermvectorService) BodyJson(body interface{}) *MultiTermvectorService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is documented as: Define ids, documents, parameters or a list of parameters per document here. You must at least provide a list of document ids. See documentation..
|
||||
func (s *MultiTermvectorService) BodyString(body string) *MultiTermvectorService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiTermvectorService) Source() interface{} {
|
||||
source := make(map[string]interface{})
|
||||
docs := make([]interface{}, len(s.docs))
|
||||
for i, doc := range s.docs {
|
||||
docs[i] = doc.Source()
|
||||
}
|
||||
source["docs"] = docs
|
||||
return source
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *MultiTermvectorService) buildURL() (string, url.Values, error) {
|
||||
var path string
|
||||
var err error
|
||||
|
||||
if s.index != "" && s.typ != "" {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_mtermvectors", map[string]string{
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
} else if s.index != "" && s.typ == "" {
|
||||
path, err = uritemplates.Expand("/{index}/_mtermvectors", map[string]string{
|
||||
"index": s.index,
|
||||
})
|
||||
} else {
|
||||
path = "/_mtermvectors"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.fieldStatistics != nil {
|
||||
params.Set("field_statistics", fmt.Sprintf("%v", *s.fieldStatistics))
|
||||
}
|
||||
if len(s.fields) > 0 {
|
||||
params.Set("fields", strings.Join(s.fields, ","))
|
||||
}
|
||||
if len(s.ids) > 0 {
|
||||
params.Set("ids", strings.Join(s.ids, ","))
|
||||
}
|
||||
if s.offsets != nil {
|
||||
params.Set("offsets", fmt.Sprintf("%v", *s.offsets))
|
||||
}
|
||||
if s.parent != "" {
|
||||
params.Set("parent", s.parent)
|
||||
}
|
||||
if s.payloads != nil {
|
||||
params.Set("payloads", fmt.Sprintf("%v", *s.payloads))
|
||||
}
|
||||
if s.positions != nil {
|
||||
params.Set("positions", fmt.Sprintf("%v", *s.positions))
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if s.realtime != nil {
|
||||
params.Set("realtime", fmt.Sprintf("%v", *s.realtime))
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.termStatistics != nil {
|
||||
params.Set("term_statistics", fmt.Sprintf("%v", *s.termStatistics))
|
||||
}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *MultiTermvectorService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" && s.typ != "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *MultiTermvectorService) Do() (*MultiTermvectorResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else if len(s.bodyString) > 0 {
|
||||
body = s.bodyString
|
||||
} else {
|
||||
body = s.Source()
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(MultiTermvectorResponse)
|
||||
if err := json.Unmarshal(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MultiTermvectorResponse is the response of MultiTermvectorService.Do.
|
||||
type MultiTermvectorResponse struct {
|
||||
Docs []*TermvectorsResponse `json:"docs"`
|
||||
}
|
||||
|
||||
// -- MultiTermvectorItem --
|
||||
|
||||
// MultiTermvectorItem is a single document to retrieve via MultiTermvectorService.
|
||||
type MultiTermvectorItem struct {
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
doc interface{}
|
||||
fieldStatistics *bool
|
||||
fields []string
|
||||
perFieldAnalyzer map[string]string
|
||||
offsets *bool
|
||||
parent string
|
||||
payloads *bool
|
||||
positions *bool
|
||||
preference string
|
||||
realtime *bool
|
||||
routing string
|
||||
termStatistics *bool
|
||||
}
|
||||
|
||||
func NewMultiTermvectorItem() *MultiTermvectorItem {
|
||||
return &MultiTermvectorItem{}
|
||||
}
|
||||
|
||||
func (s *MultiTermvectorItem) Index(index string) *MultiTermvectorItem {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiTermvectorItem) Type(typ string) *MultiTermvectorItem {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiTermvectorItem) Id(id string) *MultiTermvectorItem {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// Doc is the document to analyze.
|
||||
func (s *MultiTermvectorItem) Doc(doc interface{}) *MultiTermvectorItem {
|
||||
s.doc = doc
|
||||
return s
|
||||
}
|
||||
|
||||
// FieldStatistics specifies if document count, sum of document frequencies
|
||||
// and sum of total term frequencies should be returned.
|
||||
func (s *MultiTermvectorItem) FieldStatistics(fieldStatistics bool) *MultiTermvectorItem {
|
||||
s.fieldStatistics = &fieldStatistics
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields a list of fields to return.
|
||||
func (s *MultiTermvectorItem) Fields(fields ...string) *MultiTermvectorItem {
|
||||
if s.fields == nil {
|
||||
s.fields = make([]string, 0)
|
||||
}
|
||||
s.fields = append(s.fields, fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// PerFieldAnalyzer allows to specify a different analyzer than the one
|
||||
// at the field.
|
||||
func (s *MultiTermvectorItem) PerFieldAnalyzer(perFieldAnalyzer map[string]string) *MultiTermvectorItem {
|
||||
s.perFieldAnalyzer = perFieldAnalyzer
|
||||
return s
|
||||
}
|
||||
|
||||
// Offsets specifies if term offsets should be returned.
|
||||
func (s *MultiTermvectorItem) Offsets(offsets bool) *MultiTermvectorItem {
|
||||
s.offsets = &offsets
|
||||
return s
|
||||
}
|
||||
|
||||
// Parent id of documents.
|
||||
func (s *MultiTermvectorItem) Parent(parent string) *MultiTermvectorItem {
|
||||
s.parent = parent
|
||||
return s
|
||||
}
|
||||
|
||||
// Payloads specifies if term payloads should be returned.
|
||||
func (s *MultiTermvectorItem) Payloads(payloads bool) *MultiTermvectorItem {
|
||||
s.payloads = &payloads
|
||||
return s
|
||||
}
|
||||
|
||||
// Positions specifies if term positions should be returned.
|
||||
func (s *MultiTermvectorItem) Positions(positions bool) *MultiTermvectorItem {
|
||||
s.positions = &positions
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specify the node or shard the operation
|
||||
// should be performed on (default: random).
|
||||
func (s *MultiTermvectorItem) Preference(preference string) *MultiTermvectorItem {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Realtime specifies if request is real-time as opposed to
|
||||
// near-real-time (default: true).
|
||||
func (s *MultiTermvectorItem) Realtime(realtime bool) *MultiTermvectorItem {
|
||||
s.realtime = &realtime
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a specific routing value.
|
||||
func (s *MultiTermvectorItem) Routing(routing string) *MultiTermvectorItem {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// TermStatistics specifies if total term frequency and document frequency
|
||||
// should be returned.
|
||||
func (s *MultiTermvectorItem) TermStatistics(termStatistics bool) *MultiTermvectorItem {
|
||||
s.termStatistics = &termStatistics
|
||||
return s
|
||||
}
|
||||
|
||||
// Source returns the serialized JSON to be sent to Elasticsearch as
|
||||
// part of a MultiTermvector.
|
||||
func (s *MultiTermvectorItem) Source() interface{} {
|
||||
source := make(map[string]interface{})
|
||||
|
||||
source["_id"] = s.id
|
||||
|
||||
if s.index != "" {
|
||||
source["_index"] = s.index
|
||||
}
|
||||
if s.typ != "" {
|
||||
source["_type"] = s.typ
|
||||
}
|
||||
if s.fields != nil {
|
||||
source["fields"] = s.fields
|
||||
}
|
||||
if s.fieldStatistics != nil {
|
||||
source["field_statistics"] = fmt.Sprintf("%v", *s.fieldStatistics)
|
||||
}
|
||||
if s.offsets != nil {
|
||||
source["offsets"] = s.offsets
|
||||
}
|
||||
if s.parent != "" {
|
||||
source["parent"] = s.parent
|
||||
}
|
||||
if s.payloads != nil {
|
||||
source["payloads"] = fmt.Sprintf("%v", *s.payloads)
|
||||
}
|
||||
if s.positions != nil {
|
||||
source["positions"] = fmt.Sprintf("%v", *s.positions)
|
||||
}
|
||||
if s.preference != "" {
|
||||
source["preference"] = s.preference
|
||||
}
|
||||
if s.realtime != nil {
|
||||
source["realtime"] = fmt.Sprintf("%v", *s.realtime)
|
||||
}
|
||||
if s.routing != "" {
|
||||
source["routing"] = s.routing
|
||||
}
|
||||
if s.termStatistics != nil {
|
||||
source["term_statistics"] = fmt.Sprintf("%v", *s.termStatistics)
|
||||
}
|
||||
if s.doc != nil {
|
||||
source["doc"] = s.doc
|
||||
}
|
||||
if s.perFieldAnalyzer != nil && len(s.perFieldAnalyzer) > 0 {
|
||||
source["per_field_analyzer"] = s.perFieldAnalyzer
|
||||
}
|
||||
|
||||
return source
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = fmt.Print
|
||||
_ = log.Print
|
||||
_ = strings.Index
|
||||
_ = uritemplates.Expand
|
||||
_ = url.Parse
|
||||
)
|
||||
|
||||
// NodesInfoService allows to retrieve one or more or all of the
|
||||
// cluster nodes information.
|
||||
// It is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-nodes-info.html.
|
||||
type NodesInfoService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
nodeId []string
|
||||
metric []string
|
||||
flatSettings *bool
|
||||
human *bool
|
||||
}
|
||||
|
||||
// NewNodesInfoService creates a new NodesInfoService.
|
||||
func NewNodesInfoService(client *Client) *NodesInfoService {
|
||||
return &NodesInfoService{
|
||||
client: client,
|
||||
nodeId: []string{"_all"},
|
||||
metric: []string{"_all"},
|
||||
}
|
||||
}
|
||||
|
||||
// NodeId is a list of node IDs or names to limit the returned information.
|
||||
// Use "_local" to return information from the node you're connecting to,
|
||||
// leave empty to get information from all nodes.
|
||||
func (s *NodesInfoService) NodeId(nodeId ...string) *NodesInfoService {
|
||||
s.nodeId = append(s.nodeId, nodeId...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Metric is a list of metrics you wish returned. Leave empty to return all.
|
||||
// Valid metrics are: settings, os, process, jvm, thread_pool, network,
|
||||
// transport, http, and plugins.
|
||||
func (s *NodesInfoService) Metric(metric ...string) *NodesInfoService {
|
||||
s.metric = append(s.metric, metric...)
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatSettings returns settings in flat format (default: false).
|
||||
func (s *NodesInfoService) FlatSettings(flatSettings bool) *NodesInfoService {
|
||||
s.flatSettings = &flatSettings
|
||||
return s
|
||||
}
|
||||
|
||||
// Human indicates whether to return time and byte values in human-readable format.
|
||||
func (s *NodesInfoService) Human(human bool) *NodesInfoService {
|
||||
s.human = &human
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates whether to indent the returned JSON.
|
||||
func (s *NodesInfoService) Pretty(pretty bool) *NodesInfoService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *NodesInfoService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
path, err := uritemplates.Expand("/_nodes/{node_id}/{metric}", map[string]string{
|
||||
"node_id": strings.Join(s.nodeId, ","),
|
||||
"metric": strings.Join(s.metric, ","),
|
||||
})
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.flatSettings != nil {
|
||||
params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings))
|
||||
}
|
||||
if s.human != nil {
|
||||
params.Set("human", fmt.Sprintf("%v", *s.human))
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *NodesInfoService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *NodesInfoService) Do() (*NodesInfoResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(NodesInfoResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NodesInfoResponse is the response of NodesInfoService.Do.
|
||||
type NodesInfoResponse struct {
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Nodes map[string]*NodesInfoNode `json:"nodes"`
|
||||
}
|
||||
|
||||
type NodesInfoNode struct {
|
||||
// Name of the node, e.g. "Mister Fear"
|
||||
Name string `json:"name"`
|
||||
// TransportAddress, e.g. "127.0.0.1:9300"
|
||||
TransportAddress string `json:"transport_address"`
|
||||
// Host is the host name, e.g. "macbookair"
|
||||
Host string `json:"host"`
|
||||
// IP is the IP address, e.g. "192.168.1.2"
|
||||
IP string `json:"ip"`
|
||||
// Version is the Elasticsearch version running on the node, e.g. "1.4.3"
|
||||
Version string `json:"version"`
|
||||
// Build is the Elasticsearch build, e.g. "36a29a7"
|
||||
Build string `json:"build"`
|
||||
// HTTPAddress, e.g. "127.0.0.1:9200"
|
||||
HTTPAddress string `json:"http_address"`
|
||||
// HTTPSAddress, e.g. "127.0.0.1:9200"
|
||||
HTTPSAddress string `json:"https_address"`
|
||||
|
||||
// Attributes of the node.
|
||||
Attributes map[string]interface{} `json:"attributes"`
|
||||
|
||||
// Settings of the node, e.g. paths and pidfile.
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
|
||||
// OS information, e.g. CPU and memory.
|
||||
OS *NodesInfoNodeOS `json:"os"`
|
||||
|
||||
// Process information, e.g. max file descriptors.
|
||||
Process *NodesInfoNodeProcess `json:"process"`
|
||||
|
||||
// JVM information, e.g. VM version.
|
||||
JVM *NodesInfoNodeProcess `json:"jvm"`
|
||||
|
||||
// ThreadPool information.
|
||||
ThreadPool *NodesInfoNodeThreadPool `json:"thread_pool"`
|
||||
|
||||
// Network information.
|
||||
Network *NodesInfoNodeNetwork `json:"network"`
|
||||
|
||||
// Network information.
|
||||
Transport *NodesInfoNodeTransport `json:"transport"`
|
||||
|
||||
// HTTP information.
|
||||
HTTP *NodesInfoNodeHTTP `json:"http"`
|
||||
|
||||
// Plugins information.
|
||||
Plugins []*NodesInfoNodePlugin `json:"plugins"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeOS struct {
|
||||
RefreshInterval string `json:"refresh_interval"` // e.g. 1s
|
||||
RefreshIntervalInMillis int `json:"refresh_interval_in_millis"` // e.g. 1000
|
||||
AvailableProcessors int `json:"available_processors"` // e.g. 4
|
||||
|
||||
// CPU information
|
||||
CPU struct {
|
||||
Vendor string `json:"vendor"` // e.g. Intel
|
||||
Model string `json:"model"` // e.g. iMac15,1
|
||||
MHz int `json:"mhz"` // e.g. 3500
|
||||
TotalCores int `json:"total_cores"` // e.g. 4
|
||||
TotalSockets int `json:"total_sockets"` // e.g. 4
|
||||
CoresPerSocket int `json:"cores_per_socket"` // e.g. 16
|
||||
CacheSizeInBytes int `json:"cache_size_in_bytes"` // e.g. 256
|
||||
} `json:"cpu"`
|
||||
|
||||
// Mem information
|
||||
Mem struct {
|
||||
Total string `json:"total"` // e.g. 16gb
|
||||
TotalInBytes int `json:"total_in_bytes"` // e.g. 17179869184
|
||||
} `json:"mem"`
|
||||
|
||||
// Swap information
|
||||
Swap struct {
|
||||
Total string `json:"total"` // e.g. 1gb
|
||||
TotalInBytes int `json:"total_in_bytes"` // e.g. 1073741824
|
||||
} `json:"swap"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeProcess struct {
|
||||
RefreshInterval string `json:"refresh_interval"` // e.g. 1s
|
||||
RefreshIntervalInMillis int `json:"refresh_interval_in_millis"` // e.g. 1000
|
||||
ID int `json:"id"` // process id, e.g. 87079
|
||||
MaxFileDescriptors int `json:"max_file_descriptors"` // e.g. 32768
|
||||
Mlockall bool `json:"mlockall"` // e.g. false
|
||||
}
|
||||
|
||||
type NodesInfoNodeJVM struct {
|
||||
PID int `json:"pid"` // process id, e.g. 87079
|
||||
Version string `json:"version"` // e.g. "1.8.0_25"
|
||||
VMName string `json:"vm_name"` // e.g. "Java HotSpot(TM) 64-Bit Server VM"
|
||||
VMVersion string `json:"vm_version"` // e.g. "25.25-b02"
|
||||
VMVendor string `json:"vm_vendor"` // e.g. "Oracle Corporation"
|
||||
StartTime time.Time `json:"start_time"` // e.g. "2015-01-03T15:18:30.982Z"
|
||||
StartTimeInMillis int64 `json:"start_time_in_millis"`
|
||||
|
||||
// Mem information
|
||||
Mem struct {
|
||||
HeapInit string `json:"heap_init"` // e.g. 1gb
|
||||
HeapInitInBytes int `json:"heap_init_in_bytes"`
|
||||
HeapMax string `json:"heap_max"` // e.g. 4gb
|
||||
HeapMaxInBytes int `json:"heap_max_in_bytes"`
|
||||
NonHeapInit string `json:"non_heap_init"` // e.g. 2.4mb
|
||||
NonHeapInitInBytes int `json:"non_heap_init_in_bytes"`
|
||||
NonHeapMax string `json:"non_heap_max"` // e.g. 0b
|
||||
NonHeapMaxInBytes int `json:"non_heap_max_in_bytes"`
|
||||
DirectMax string `json:"direct_max"` // e.g. 4gb
|
||||
DirectMaxInBytes int `json:"direct_max_in_bytes"`
|
||||
} `json:"mem"`
|
||||
|
||||
GCCollectors []string `json:"gc_collectors"` // e.g. ["ParNew"]
|
||||
MemoryPools []string `json:"memory_pools"` // e.g. ["Code Cache", "Metaspace"]
|
||||
}
|
||||
|
||||
type NodesInfoNodeThreadPool struct {
|
||||
Percolate *NodesInfoNodeThreadPoolSection `json:"percolate"`
|
||||
Bench *NodesInfoNodeThreadPoolSection `json:"bench"`
|
||||
Listener *NodesInfoNodeThreadPoolSection `json:"listener"`
|
||||
Index *NodesInfoNodeThreadPoolSection `json:"index"`
|
||||
Refresh *NodesInfoNodeThreadPoolSection `json:"refresh"`
|
||||
Suggest *NodesInfoNodeThreadPoolSection `json:"suggest"`
|
||||
Generic *NodesInfoNodeThreadPoolSection `json:"generic"`
|
||||
Warmer *NodesInfoNodeThreadPoolSection `json:"warmer"`
|
||||
Search *NodesInfoNodeThreadPoolSection `json:"search"`
|
||||
Flush *NodesInfoNodeThreadPoolSection `json:"flush"`
|
||||
Optimize *NodesInfoNodeThreadPoolSection `json:"optimize"`
|
||||
Management *NodesInfoNodeThreadPoolSection `json:"management"`
|
||||
Get *NodesInfoNodeThreadPoolSection `json:"get"`
|
||||
Merge *NodesInfoNodeThreadPoolSection `json:"merge"`
|
||||
Bulk *NodesInfoNodeThreadPoolSection `json:"bulk"`
|
||||
Snapshot *NodesInfoNodeThreadPoolSection `json:"snapshot"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeThreadPoolSection struct {
|
||||
Type string `json:"type"` // e.g. fixed
|
||||
Min int `json:"min"` // e.g. 4
|
||||
Max int `json:"max"` // e.g. 4
|
||||
KeepAlive string `json:"keep_alive"` // e.g. "5m"
|
||||
QueueSize interface{} `json:"queue_size"` // e.g. "1k" or -1
|
||||
}
|
||||
|
||||
type NodesInfoNodeNetwork struct {
|
||||
RefreshInterval string `json:"refresh_interval"` // e.g. 1s
|
||||
RefreshIntervalInMillis int `json:"refresh_interval_in_millis"` // e.g. 1000
|
||||
PrimaryInterface struct {
|
||||
Address string `json:"address"` // e.g. 192.168.1.2
|
||||
Name string `json:"name"` // e.g. en0
|
||||
MACAddress string `json:"mac_address"` // e.g. 11:22:33:44:55:66
|
||||
} `json:"primary_interface"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeTransport struct {
|
||||
BoundAddress []string `json:"bound_address"`
|
||||
PublishAddress string `json:"publish_address"`
|
||||
Profiles map[string]*NodesInfoNodeTransportProfile `json:"profiles"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeTransportProfile struct {
|
||||
BoundAddress []string `json:"bound_address"`
|
||||
PublishAddress string `json:"publish_address"`
|
||||
}
|
||||
|
||||
type NodesInfoNodeHTTP struct {
|
||||
BoundAddress []string `json:"bound_address"` // e.g. ["127.0.0.1:9200", "[fe80::1]:9200", "[::1]:9200"]
|
||||
PublishAddress string `json:"publish_address"` // e.g. "127.0.0.1:9300"
|
||||
MaxContentLength string `json:"max_content_length"` // e.g. "100mb"
|
||||
MaxContentLengthInBytes int64 `json:"max_content_length_in_bytes"`
|
||||
}
|
||||
|
||||
type NodesInfoNodePlugin struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Site bool `json:"site"`
|
||||
JVM bool `json:"jvm"`
|
||||
URL string `json:"url"` // e.g. /_plugin/dummy/
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
type OptimizeService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
maxNumSegments *int
|
||||
onlyExpungeDeletes *bool
|
||||
flush *bool
|
||||
waitForMerge *bool
|
||||
force *bool
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func NewOptimizeService(client *Client) *OptimizeService {
|
||||
builder := &OptimizeService{
|
||||
client: client,
|
||||
indices: make([]string, 0),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *OptimizeService) Index(indices ...string) *OptimizeService {
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) MaxNumSegments(maxNumSegments int) *OptimizeService {
|
||||
s.maxNumSegments = &maxNumSegments
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) OnlyExpungeDeletes(onlyExpungeDeletes bool) *OptimizeService {
|
||||
s.onlyExpungeDeletes = &onlyExpungeDeletes
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) Flush(flush bool) *OptimizeService {
|
||||
s.flush = &flush
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) WaitForMerge(waitForMerge bool) *OptimizeService {
|
||||
s.waitForMerge = &waitForMerge
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) Force(force bool) *OptimizeService {
|
||||
s.force = &force
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) Pretty(pretty bool) *OptimizeService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *OptimizeService) Do() (*OptimizeResult, error) {
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err := uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
if len(indexPart) > 0 {
|
||||
path += strings.Join(indexPart, ",")
|
||||
}
|
||||
|
||||
path += "/_optimize"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.maxNumSegments != nil {
|
||||
params.Set("max_num_segments", fmt.Sprintf("%d", *s.maxNumSegments))
|
||||
}
|
||||
if s.onlyExpungeDeletes != nil {
|
||||
params.Set("only_expunge_deletes", fmt.Sprintf("%v", *s.onlyExpungeDeletes))
|
||||
}
|
||||
if s.flush != nil {
|
||||
params.Set("flush", fmt.Sprintf("%v", *s.flush))
|
||||
}
|
||||
if s.waitForMerge != nil {
|
||||
params.Set("wait_for_merge", fmt.Sprintf("%v", *s.waitForMerge))
|
||||
}
|
||||
if s.force != nil {
|
||||
params.Set("force", fmt.Sprintf("%v", *s.force))
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
ret := new(OptimizeResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// -- Result of an optimize request.
|
||||
|
||||
type OptimizeResult struct {
|
||||
Shards shardsInfo `json:"_shards,omitempty"`
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// PercolateService is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/search-percolate.html.
|
||||
type PercolateService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
index string
|
||||
typ string
|
||||
id string
|
||||
version interface{}
|
||||
versionType string
|
||||
routing []string
|
||||
preference string
|
||||
ignoreUnavailable *bool
|
||||
percolateIndex string
|
||||
percolatePreference string
|
||||
percolateRouting string
|
||||
source string
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
percolateFormat string
|
||||
percolateType string
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
}
|
||||
|
||||
// NewPercolateService creates a new PercolateService.
|
||||
func NewPercolateService(client *Client) *PercolateService {
|
||||
return &PercolateService{
|
||||
client: client,
|
||||
routing: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Index is the name of the index of the document being percolated.
|
||||
func (s *PercolateService) Index(index string) *PercolateService {
|
||||
s.index = index
|
||||
return s
|
||||
}
|
||||
|
||||
// Type is the type of the document being percolated.
|
||||
func (s *PercolateService) Type(typ string) *PercolateService {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Id is to substitute the document in the request body with a
|
||||
// document that is known by the specified id. On top of the id,
|
||||
// the index and type parameter will be used to retrieve
|
||||
// the document from within the cluster.
|
||||
func (s *PercolateService) Id(id string) *PercolateService {
|
||||
s.id = id
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expressions
|
||||
// to concrete indices that are open, closed or both.
|
||||
func (s *PercolateService) ExpandWildcards(expandWildcards string) *PercolateService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// PercolateFormat indicates whether to return an array of matching
|
||||
// query IDs instead of objects.
|
||||
func (s *PercolateService) PercolateFormat(percolateFormat string) *PercolateService {
|
||||
s.percolateFormat = percolateFormat
|
||||
return s
|
||||
}
|
||||
|
||||
// PercolateType is the type to percolate document into. Defaults to type.
|
||||
func (s *PercolateService) PercolateType(percolateType string) *PercolateService {
|
||||
s.percolateType = percolateType
|
||||
return s
|
||||
}
|
||||
|
||||
// PercolateRouting is the routing value to use when percolating
|
||||
// the existing document.
|
||||
func (s *PercolateService) PercolateRouting(percolateRouting string) *PercolateService {
|
||||
s.percolateRouting = percolateRouting
|
||||
return s
|
||||
}
|
||||
|
||||
// Source is the URL-encoded request definition.
|
||||
func (s *PercolateService) Source(source string) *PercolateService {
|
||||
s.source = source
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices.
|
||||
// (This includes `_all` string or when no indices have been specified).
|
||||
func (s *PercolateService) AllowNoIndices(allowNoIndices bool) *PercolateService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether specified concrete indices should
|
||||
// be ignored when unavailable (missing or closed).
|
||||
func (s *PercolateService) IgnoreUnavailable(ignoreUnavailable bool) *PercolateService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// PercolateIndex is the index to percolate the document into. Defaults to index.
|
||||
func (s *PercolateService) PercolateIndex(percolateIndex string) *PercolateService {
|
||||
s.percolateIndex = percolateIndex
|
||||
return s
|
||||
}
|
||||
|
||||
// PercolatePreference defines which shard to prefer when executing
|
||||
// the percolate request.
|
||||
func (s *PercolateService) PercolatePreference(percolatePreference string) *PercolateService {
|
||||
s.percolatePreference = percolatePreference
|
||||
return s
|
||||
}
|
||||
|
||||
// Version is an explicit version number for concurrency control.
|
||||
func (s *PercolateService) Version(version interface{}) *PercolateService {
|
||||
s.version = version
|
||||
return s
|
||||
}
|
||||
|
||||
// VersionType is the specific version type.
|
||||
func (s *PercolateService) VersionType(versionType string) *PercolateService {
|
||||
s.versionType = versionType
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a list of specific routing values.
|
||||
func (s *PercolateService) Routing(routing []string) *PercolateService {
|
||||
s.routing = routing
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be
|
||||
// performed on (default: random).
|
||||
func (s *PercolateService) Preference(preference string) *PercolateService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *PercolateService) Pretty(pretty bool) *PercolateService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Doc wraps the given document into the "doc" key of the body.
|
||||
func (s *PercolateService) Doc(doc interface{}) *PercolateService {
|
||||
return s.BodyJson(map[string]interface{}{"doc": doc})
|
||||
}
|
||||
|
||||
// BodyJson is the percolator request definition using the percolate DSL.
|
||||
func (s *PercolateService) BodyJson(body interface{}) *PercolateService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyString is the percolator request definition using the percolate DSL.
|
||||
func (s *PercolateService) BodyString(body string) *PercolateService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *PercolateService) buildURL() (string, url.Values, error) {
|
||||
// Build URL
|
||||
var path string
|
||||
var err error
|
||||
if s.id == "" {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_percolate", map[string]string{
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
})
|
||||
} else {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/{id}/_percolate", map[string]string{
|
||||
"index": s.index,
|
||||
"type": s.typ,
|
||||
"id": s.id,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.version != nil {
|
||||
params.Set("version", fmt.Sprintf("%v", s.version))
|
||||
}
|
||||
if s.versionType != "" {
|
||||
params.Set("version_type", s.versionType)
|
||||
}
|
||||
if len(s.routing) > 0 {
|
||||
params.Set("routing", strings.Join(s.routing, ","))
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
if s.percolateIndex != "" {
|
||||
params.Set("percolate_index", s.percolateIndex)
|
||||
}
|
||||
if s.percolatePreference != "" {
|
||||
params.Set("percolate_preference", s.percolatePreference)
|
||||
}
|
||||
if s.percolateRouting != "" {
|
||||
params.Set("percolate_routing", s.percolateRouting)
|
||||
}
|
||||
if s.source != "" {
|
||||
params.Set("source", s.source)
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.percolateFormat != "" {
|
||||
params.Set("percolate_format", s.percolateFormat)
|
||||
}
|
||||
if s.percolateType != "" {
|
||||
params.Set("percolate_type", s.percolateType)
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *PercolateService) Validate() error {
|
||||
var invalid []string
|
||||
if s.index == "" {
|
||||
invalid = append(invalid, "Index")
|
||||
}
|
||||
if s.typ == "" {
|
||||
invalid = append(invalid, "Type")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *PercolateService) Do() (*PercolateResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
var body interface{}
|
||||
if s.bodyJson != nil {
|
||||
body = s.bodyJson
|
||||
} else {
|
||||
body = s.bodyString
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("GET", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(PercolateResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// PercolateResponse is the response of PercolateService.Do.
|
||||
type PercolateResponse struct {
|
||||
TookInMillis int64 `json:"took"` // search time in milliseconds
|
||||
Total int64 `json:"total"` // total matches
|
||||
Matches []*PercolateMatch `json:"matches,omitempty"`
|
||||
Aggregations Aggregations `json:"aggregations,omitempty"` // results from aggregations
|
||||
}
|
||||
|
||||
// PercolateMatch returns a single match in a PercolateResponse.
|
||||
type PercolateMatch struct {
|
||||
Index string `json:"_index,omitempty"`
|
||||
Id string `json:"_id"`
|
||||
Score float64 `json:"_score,omitempty"`
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// PingService checks if an Elasticsearch server on a given URL is alive.
|
||||
// When asked for, it can also return various information about the
|
||||
// Elasticsearch server, e.g. the Elasticsearch version number.
|
||||
//
|
||||
// Ping simply starts a HTTP GET request to the URL of the server.
|
||||
// If the server responds with HTTP Status code 200 OK, the server is alive.
|
||||
type PingService struct {
|
||||
client *Client
|
||||
url string
|
||||
timeout string
|
||||
httpHeadOnly bool
|
||||
pretty bool
|
||||
}
|
||||
|
||||
// PingResult is the result returned from querying the Elasticsearch server.
|
||||
type PingResult struct {
|
||||
Name string `json:"name"`
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Version struct {
|
||||
Number string `json:"number"`
|
||||
BuildHash string `json:"build_hash"`
|
||||
BuildTimestamp string `json:"build_timestamp"`
|
||||
BuildSnapshot bool `json:"build_snapshot"`
|
||||
LuceneVersion string `json:"lucene_version"`
|
||||
} `json:"version"`
|
||||
TagLine string `json:"tagline"`
|
||||
}
|
||||
|
||||
func NewPingService(client *Client) *PingService {
|
||||
return &PingService{
|
||||
client: client,
|
||||
url: DefaultURL,
|
||||
httpHeadOnly: false,
|
||||
pretty: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PingService) URL(url string) *PingService {
|
||||
s.url = url
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *PingService) Timeout(timeout string) *PingService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// HeadOnly makes the service to only return the status code in Do;
|
||||
// the PingResult will be nil.
|
||||
func (s *PingService) HttpHeadOnly(httpHeadOnly bool) *PingService {
|
||||
s.httpHeadOnly = httpHeadOnly
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *PingService) Pretty(pretty bool) *PingService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Do returns the PingResult, the HTTP status code of the Elasticsearch
|
||||
// server, and an error.
|
||||
func (s *PingService) Do() (*PingResult, int, error) {
|
||||
s.client.mu.RLock()
|
||||
basicAuth := s.client.basicAuth
|
||||
basicAuthUsername := s.client.basicAuthUsername
|
||||
basicAuthPassword := s.client.basicAuthPassword
|
||||
s.client.mu.RUnlock()
|
||||
|
||||
url_ := s.url + "/"
|
||||
|
||||
params := make(url.Values)
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if len(params) > 0 {
|
||||
url_ += "?" + params.Encode()
|
||||
}
|
||||
|
||||
var method string
|
||||
if s.httpHeadOnly {
|
||||
method = "HEAD"
|
||||
} else {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
// Notice: This service must NOT use PerformRequest!
|
||||
req, err := NewRequest(method, url_)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if basicAuth {
|
||||
req.SetBasicAuth(basicAuthUsername, basicAuthPassword)
|
||||
}
|
||||
|
||||
res, err := s.client.c.Do((*http.Request)(req))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var ret *PingResult
|
||||
if !s.httpHeadOnly {
|
||||
ret = new(PingResult)
|
||||
if err := json.NewDecoder(res.Body).Decode(ret); err != nil {
|
||||
return nil, res.StatusCode, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, res.StatusCode, nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// HasPlugin indicates whether the cluster has the named plugin.
|
||||
func (c *Client) HasPlugin(name string) (bool, error) {
|
||||
plugins, err := c.Plugins()
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
for _, plugin := range plugins {
|
||||
if plugin == name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Plugins returns the list of all registered plugins.
|
||||
func (c *Client) Plugins() ([]string, error) {
|
||||
stats, err := c.ClusterStats().Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stats == nil {
|
||||
return nil, err
|
||||
}
|
||||
if stats.Nodes == nil {
|
||||
return nil, err
|
||||
}
|
||||
var plugins []string
|
||||
for _, plugin := range stats.Nodes.Plugins {
|
||||
plugins = append(plugins, plugin.Name)
|
||||
}
|
||||
return plugins, nil
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// Query represents the generic query interface. A query's sole purpose
|
||||
// is to return the source of the query as a JSON-serializable object.
|
||||
// Returning map[string]interface{} is the norm for queries.
|
||||
type Query interface {
|
||||
// Source returns the JSON-serializable query request.
|
||||
Source() (interface{}, error)
|
||||
}
|
||||
+572
@@ -0,0 +1,572 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// ReindexService is a method to copy documents from one index to another.
|
||||
// It was introduced in Elasticsearch 2.3.0.
|
||||
//
|
||||
// Notice that Elastic already had a Reindexer service that pre-dated
|
||||
// the Reindex API. Use that if you're on an earlier version of Elasticsearch.
|
||||
//
|
||||
// It is documented at https://www.elastic.co/guide/en/elasticsearch/plugins/master/plugins-reindex.html.
|
||||
type ReindexService struct {
|
||||
client *Client
|
||||
pretty bool
|
||||
consistency string
|
||||
refresh *bool
|
||||
timeout string
|
||||
waitForCompletion *bool
|
||||
bodyJson interface{}
|
||||
bodyString string
|
||||
source *ReindexSource
|
||||
destination *ReindexDestination
|
||||
conflicts string
|
||||
size *int
|
||||
script *Script
|
||||
}
|
||||
|
||||
// NewReindexService creates a new ReindexService.
|
||||
func NewReindexService(client *Client) *ReindexService {
|
||||
return &ReindexService{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Consistency specifies an explicit write consistency setting for the operation.
|
||||
func (s *ReindexService) Consistency(consistency string) *ReindexService {
|
||||
s.consistency = consistency
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh indicates whether Elasticsearch should refresh the effected indexes
|
||||
// immediately.
|
||||
func (s *ReindexService) Refresh(refresh bool) *ReindexService {
|
||||
s.refresh = &refresh
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout is the time each individual bulk request should wait for shards
|
||||
// that are unavailable.
|
||||
func (s *ReindexService) Timeout(timeout string) *ReindexService {
|
||||
s.timeout = timeout
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitForCompletion indicates whether Elasticsearch should block until the
|
||||
// reindex is complete.
|
||||
func (s *ReindexService) WaitForCompletion(waitForCompletion bool) *ReindexService {
|
||||
s.waitForCompletion = &waitForCompletion
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty indicates that the JSON response be indented and human readable.
|
||||
func (s *ReindexService) Pretty(pretty bool) *ReindexService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Source specifies the source of the reindexing process.
|
||||
func (s *ReindexService) Source(source *ReindexSource) *ReindexService {
|
||||
s.source = source
|
||||
return s
|
||||
}
|
||||
|
||||
// SourceIndex specifies the source index of the reindexing process.
|
||||
func (s *ReindexService) SourceIndex(index string) *ReindexService {
|
||||
if s.source == nil {
|
||||
s.source = NewReindexSource()
|
||||
}
|
||||
s.source = s.source.Index(index)
|
||||
return s
|
||||
}
|
||||
|
||||
// Destination specifies the destination of the reindexing process.
|
||||
func (s *ReindexService) Destination(destination *ReindexDestination) *ReindexService {
|
||||
s.destination = destination
|
||||
return s
|
||||
}
|
||||
|
||||
// DestinationIndex specifies the destination index of the reindexing process.
|
||||
func (s *ReindexService) DestinationIndex(index string) *ReindexService {
|
||||
if s.destination == nil {
|
||||
s.destination = NewReindexDestination()
|
||||
}
|
||||
s.destination = s.destination.Index(index)
|
||||
return s
|
||||
}
|
||||
|
||||
// DestinationIndexAndType specifies both the destination index and type
|
||||
// of the reindexing process.
|
||||
func (s *ReindexService) DestinationIndexAndType(index, typ string) *ReindexService {
|
||||
if s.destination == nil {
|
||||
s.destination = NewReindexDestination()
|
||||
}
|
||||
s.destination = s.destination.Index(index)
|
||||
s.destination = s.destination.Type(typ)
|
||||
return s
|
||||
}
|
||||
|
||||
// Conflicts indicates what to do when the process detects version conflicts.
|
||||
// Possible values are "proceed" and "abort".
|
||||
func (s *ReindexService) Conflicts(conflicts string) *ReindexService {
|
||||
s.conflicts = conflicts
|
||||
return s
|
||||
}
|
||||
|
||||
// AbortOnVersionConflict aborts the request on version conflicts.
|
||||
// It is an alias to setting Conflicts("abort").
|
||||
func (s *ReindexService) AbortOnVersionConflict() *ReindexService {
|
||||
s.conflicts = "abort"
|
||||
return s
|
||||
}
|
||||
|
||||
// ProceedOnVersionConflict aborts the request on version conflicts.
|
||||
// It is an alias to setting Conflicts("proceed").
|
||||
func (s *ReindexService) ProceedOnVersionConflict() *ReindexService {
|
||||
s.conflicts = "proceed"
|
||||
return s
|
||||
}
|
||||
|
||||
// Size sets an upper limit for the number of processed documents.
|
||||
func (s *ReindexService) Size(size int) *ReindexService {
|
||||
s.size = &size
|
||||
return s
|
||||
}
|
||||
|
||||
// Script allows for modification of the documents as they are reindexed
|
||||
// from source to destination.
|
||||
func (s *ReindexService) Script(script *Script) *ReindexService {
|
||||
s.script = script
|
||||
return s
|
||||
}
|
||||
|
||||
// BodyJson specifies e.g. the query to restrict the results specified with the
|
||||
// Query DSL (optional). The interface{} will be serialized to a JSON document,
|
||||
// so use a map[string]interface{}.
|
||||
func (s *ReindexService) BodyJson(body interface{}) *ReindexService {
|
||||
s.bodyJson = body
|
||||
return s
|
||||
}
|
||||
|
||||
// Body specifies e.g. a query to restrict the results specified with
|
||||
// the Query DSL (optional).
|
||||
func (s *ReindexService) BodyString(body string) *ReindexService {
|
||||
s.bodyString = body
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *ReindexService) buildURL() (string, url.Values, error) {
|
||||
// Build URL path
|
||||
path := "/_reindex"
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", "1")
|
||||
}
|
||||
if s.consistency != "" {
|
||||
params.Set("consistency", s.consistency)
|
||||
}
|
||||
if s.refresh != nil {
|
||||
params.Set("refresh", fmt.Sprintf("%v", *s.refresh))
|
||||
}
|
||||
if s.timeout != "" {
|
||||
params.Set("timeout", s.timeout)
|
||||
}
|
||||
if s.waitForCompletion != nil {
|
||||
params.Set("wait_for_completion", fmt.Sprintf("%v", *s.waitForCompletion))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *ReindexService) Validate() error {
|
||||
var invalid []string
|
||||
if s.source == nil {
|
||||
invalid = append(invalid, "Source")
|
||||
} else {
|
||||
if len(s.source.indices) == 0 {
|
||||
invalid = append(invalid, "Source.Index")
|
||||
}
|
||||
}
|
||||
if s.destination == nil {
|
||||
invalid = append(invalid, "Destination")
|
||||
}
|
||||
if len(invalid) > 0 {
|
||||
return fmt.Errorf("missing required fields: %v", invalid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// body returns the body part of the document request.
|
||||
func (s *ReindexService) body() (interface{}, error) {
|
||||
if s.bodyJson != nil {
|
||||
return s.bodyJson, nil
|
||||
}
|
||||
if s.bodyString != "" {
|
||||
return s.bodyString, nil
|
||||
}
|
||||
|
||||
body := make(map[string]interface{})
|
||||
|
||||
if s.conflicts != "" {
|
||||
body["conflicts"] = s.conflicts
|
||||
}
|
||||
if s.size != nil {
|
||||
body["size"] = *s.size
|
||||
}
|
||||
if s.script != nil {
|
||||
out, err := s.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body["script"] = out
|
||||
}
|
||||
|
||||
src, err := s.source.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body["source"] = src
|
||||
|
||||
dst, err := s.destination.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body["dest"] = dst
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// Do executes the operation.
|
||||
func (s *ReindexService) Do() (*ReindexResponse, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup HTTP request body
|
||||
body, err := s.body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HTTP response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return operation response
|
||||
ret := new(ReindexResponse)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ReindexResponse is the response of ReindexService.Do.
|
||||
type ReindexResponse struct {
|
||||
Took interface{} `json:"took"` // 2.3.0 returns "37.7ms" while 2.2 returns 38 for took
|
||||
TimedOut bool `json:"timed_out"`
|
||||
Total int64 `json:"total"`
|
||||
Updated int64 `json:"updated"`
|
||||
Created int64 `json:"created"`
|
||||
Deleted int64 `json:"deleted"`
|
||||
Batches int64 `json:"batches"`
|
||||
VersionConflicts int64 `json:"version_conflicts"`
|
||||
Noops int64 `json:"noops"`
|
||||
Retries int64 `json:"retries"`
|
||||
Canceled string `json:"canceled"`
|
||||
Failures []shardOperationFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// -- Source of Reindex --
|
||||
|
||||
// ReindexSource specifies the source of a Reindex process.
|
||||
type ReindexSource struct {
|
||||
searchType string // default in ES is "query_then_fetch"
|
||||
indices []string
|
||||
types []string
|
||||
routing *string
|
||||
preference *string
|
||||
requestCache *bool
|
||||
scroll string
|
||||
query Query
|
||||
sorts []SortInfo
|
||||
sorters []Sorter
|
||||
searchSource *SearchSource
|
||||
}
|
||||
|
||||
// NewReindexSource creates a new ReindexSource.
|
||||
func NewReindexSource() *ReindexSource {
|
||||
return &ReindexSource{
|
||||
indices: make([]string, 0),
|
||||
types: make([]string, 0),
|
||||
sorts: make([]SortInfo, 0),
|
||||
sorters: make([]Sorter, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// SearchType is the search operation type. Possible values are
|
||||
// "query_then_fetch" and "dfs_query_then_fetch".
|
||||
func (r *ReindexSource) SearchType(searchType string) *ReindexSource {
|
||||
r.searchType = searchType
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) SearchTypeDfsQueryThenFetch() *ReindexSource {
|
||||
return r.SearchType("dfs_query_then_fetch")
|
||||
}
|
||||
|
||||
func (r *ReindexSource) SearchTypeQueryThenFetch() *ReindexSource {
|
||||
return r.SearchType("query_then_fetch")
|
||||
}
|
||||
|
||||
func (r *ReindexSource) Index(indices ...string) *ReindexSource {
|
||||
r.indices = append(r.indices, indices...)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) Type(types ...string) *ReindexSource {
|
||||
r.types = append(r.types, types...)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) Preference(preference string) *ReindexSource {
|
||||
r.preference = &preference
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) RequestCache(requestCache bool) *ReindexSource {
|
||||
r.requestCache = &requestCache
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) Scroll(scroll string) *ReindexSource {
|
||||
r.scroll = scroll
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ReindexSource) Query(query Query) *ReindexSource {
|
||||
r.query = query
|
||||
return r
|
||||
}
|
||||
|
||||
// Sort adds a sort order.
|
||||
func (s *ReindexSource) Sort(field string, ascending bool) *ReindexSource {
|
||||
s.sorts = append(s.sorts, SortInfo{Field: field, Ascending: ascending})
|
||||
return s
|
||||
}
|
||||
|
||||
// SortWithInfo adds a sort order.
|
||||
func (s *ReindexSource) SortWithInfo(info SortInfo) *ReindexSource {
|
||||
s.sorts = append(s.sorts, info)
|
||||
return s
|
||||
}
|
||||
|
||||
// SortBy adds a sort order.
|
||||
func (s *ReindexSource) SortBy(sorter ...Sorter) *ReindexSource {
|
||||
s.sorters = append(s.sorters, sorter...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Source returns a serializable JSON request for the request.
|
||||
func (r *ReindexSource) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
|
||||
if r.query != nil {
|
||||
src, err := r.query.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["query"] = src
|
||||
} else if r.searchSource != nil {
|
||||
src, err := r.searchSource.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["source"] = src
|
||||
}
|
||||
|
||||
if r.searchType != "" {
|
||||
source["search_type"] = r.searchType
|
||||
}
|
||||
|
||||
switch len(r.indices) {
|
||||
case 0:
|
||||
case 1:
|
||||
source["index"] = r.indices[0]
|
||||
default:
|
||||
source["index"] = r.indices
|
||||
}
|
||||
|
||||
switch len(r.types) {
|
||||
case 0:
|
||||
case 1:
|
||||
source["type"] = r.types[0]
|
||||
default:
|
||||
source["type"] = r.types
|
||||
}
|
||||
|
||||
if r.preference != nil && *r.preference != "" {
|
||||
source["preference"] = *r.preference
|
||||
}
|
||||
|
||||
if r.requestCache != nil {
|
||||
source["request_cache"] = fmt.Sprintf("%v", *r.requestCache)
|
||||
}
|
||||
|
||||
if r.scroll != "" {
|
||||
source["scroll"] = r.scroll
|
||||
}
|
||||
|
||||
if len(r.sorters) > 0 {
|
||||
sortarr := make([]interface{}, 0)
|
||||
for _, sorter := range r.sorters {
|
||||
src, err := sorter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortarr = append(sortarr, src)
|
||||
}
|
||||
source["sort"] = sortarr
|
||||
} else if len(r.sorts) > 0 {
|
||||
sortarr := make([]interface{}, 0)
|
||||
for _, sort := range r.sorts {
|
||||
src, err := sort.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortarr = append(sortarr, src)
|
||||
}
|
||||
source["sort"] = sortarr
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -source Destination of Reindex --
|
||||
|
||||
// ReindexDestination is the destination of a Reindex API call.
|
||||
// It is basically the meta data of a BulkIndexRequest.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/2.3/docs-reindex.html
|
||||
// fsourcer details.
|
||||
type ReindexDestination struct {
|
||||
index string
|
||||
typ string
|
||||
routing string
|
||||
parent string
|
||||
opType string
|
||||
version int64 // default is MATCH_ANY
|
||||
versionType string // default is "internal"
|
||||
}
|
||||
|
||||
// NewReindexDestination returns a new ReindexDestination.
|
||||
func NewReindexDestination() *ReindexDestination {
|
||||
return &ReindexDestination{}
|
||||
}
|
||||
|
||||
// Index specifies name of the Elasticsearch index to use as the destination
|
||||
// of a reindexing process.
|
||||
func (r *ReindexDestination) Index(index string) *ReindexDestination {
|
||||
r.index = index
|
||||
return r
|
||||
}
|
||||
|
||||
// Type specifies the Elasticsearch type to use for reindexing.
|
||||
func (r *ReindexDestination) Type(typ string) *ReindexDestination {
|
||||
r.typ = typ
|
||||
return r
|
||||
}
|
||||
|
||||
// Routing specifies a routing value for the reindexing request.
|
||||
// It can be "keep", "discard", or start with "=". The latter specifies
|
||||
// the routing on the bulk request.
|
||||
func (r *ReindexDestination) Routing(routing string) *ReindexDestination {
|
||||
r.routing = routing
|
||||
return r
|
||||
}
|
||||
|
||||
// Keep sets the routing on the bulk request sent for each match to the routing
|
||||
// of the match (the default).
|
||||
func (r *ReindexDestination) Keep() *ReindexDestination {
|
||||
r.routing = "keep"
|
||||
return r
|
||||
}
|
||||
|
||||
// Discard sets the routing on the bulk request sent for each match to null.
|
||||
func (r *ReindexDestination) Discard() *ReindexDestination {
|
||||
r.routing = "discard"
|
||||
return r
|
||||
}
|
||||
|
||||
// Parent specifies the identifier of the parent document (if available).
|
||||
func (r *ReindexDestination) Parent(parent string) *ReindexDestination {
|
||||
r.parent = parent
|
||||
return r
|
||||
}
|
||||
|
||||
// OpType specifies if this request should follow create-only or upsert
|
||||
// behavior. This follows the OpType of the standard document index API.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#operation-type
|
||||
// for details.
|
||||
func (r *ReindexDestination) OpType(opType string) *ReindexDestination {
|
||||
r.opType = opType
|
||||
return r
|
||||
}
|
||||
|
||||
// Version indicates the version of the document as part of an optimistic
|
||||
// concurrency model.
|
||||
func (r *ReindexDestination) Version(version int64) *ReindexDestination {
|
||||
r.version = version
|
||||
return r
|
||||
}
|
||||
|
||||
// VersionType specifies how versions are created.
|
||||
func (r *ReindexDestination) VersionType(versionType string) *ReindexDestination {
|
||||
r.versionType = versionType
|
||||
return r
|
||||
}
|
||||
|
||||
// Source returns a serializable JSON request for the request.
|
||||
func (r *ReindexDestination) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if r.index != "" {
|
||||
source["index"] = r.index
|
||||
}
|
||||
if r.typ != "" {
|
||||
source["type"] = r.typ
|
||||
}
|
||||
if r.routing != "" {
|
||||
source["routing"] = r.routing
|
||||
}
|
||||
if r.opType != "" {
|
||||
source["op_type"] = r.opType
|
||||
}
|
||||
if r.parent != "" {
|
||||
source["parent"] = r.parent
|
||||
}
|
||||
if r.version > 0 {
|
||||
source["version"] = r.version
|
||||
}
|
||||
if r.versionType != "" {
|
||||
source["version_type"] = r.versionType
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Reindexer simplifies the process of reindexing an index. You typically
|
||||
// reindex a source index to a target index. However, you can also specify
|
||||
// a query that filters out documents from the source index before bulk
|
||||
// indexing them into the target index. The caller may also specify a
|
||||
// different client for the target, e.g. when copying indices from one
|
||||
// Elasticsearch cluster to another.
|
||||
//
|
||||
// Internally, the Reindex users a scan and scroll operation on the source
|
||||
// index and bulk indexing to push data into the target index.
|
||||
//
|
||||
// By default the reindexer fetches the _source, _parent, and _routing
|
||||
// attributes from the source index, using the provided CopyToTargetIndex
|
||||
// will copy those attributes into the destinationIndex.
|
||||
// This behaviour can be overridden by setting the ScanFields and providing a
|
||||
// custom ReindexerFunc.
|
||||
//
|
||||
// The caller is responsible for setting up and/or clearing the target index
|
||||
// before starting the reindex process.
|
||||
//
|
||||
// See http://www.elastic.co/guide/en/elasticsearch/guide/current/reindex.html
|
||||
// for more information about reindexing.
|
||||
type Reindexer struct {
|
||||
sourceClient, targetClient *Client
|
||||
sourceIndex string
|
||||
query Query
|
||||
scanFields []string
|
||||
bulkSize int
|
||||
size int
|
||||
scroll string
|
||||
reindexerFunc ReindexerFunc
|
||||
progress ReindexerProgressFunc
|
||||
statsOnly bool
|
||||
}
|
||||
|
||||
// A ReindexerFunc receives each hit from the sourceIndex.
|
||||
// It can choose to add any number of BulkableRequests to the bulkService.
|
||||
type ReindexerFunc func(hit *SearchHit, bulkService *BulkService) error
|
||||
|
||||
// CopyToTargetIndex returns a ReindexerFunc that copies the SearchHit's
|
||||
// _source, _parent, and _routing attributes into the targetIndex
|
||||
func CopyToTargetIndex(targetIndex string) ReindexerFunc {
|
||||
return func(hit *SearchHit, bulkService *BulkService) error {
|
||||
// TODO(oe) Do we need to deserialize here?
|
||||
source := make(map[string]interface{})
|
||||
if err := json.Unmarshal(*hit.Source, &source); err != nil {
|
||||
return err
|
||||
}
|
||||
req := NewBulkIndexRequest().Index(targetIndex).Type(hit.Type).Id(hit.Id).Doc(source)
|
||||
if hit.Parent != "" {
|
||||
req = req.Parent(hit.Parent)
|
||||
}
|
||||
if hit.Routing != "" {
|
||||
req = req.Routing(hit.Routing)
|
||||
}
|
||||
bulkService.Add(req)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ReindexerProgressFunc is a callback that can be used with Reindexer
|
||||
// to report progress while reindexing data.
|
||||
type ReindexerProgressFunc func(current, total int64)
|
||||
|
||||
// ReindexerResponse is returned from the Do func in a Reindexer.
|
||||
// By default, it returns the number of succeeded and failed bulk operations.
|
||||
// To return details about all failed items, set StatsOnly to false in
|
||||
// Reindexer.
|
||||
type ReindexerResponse struct {
|
||||
Success int64
|
||||
Failed int64
|
||||
Errors []*BulkResponseItem
|
||||
}
|
||||
|
||||
// NewReindexer returns a new Reindexer.
|
||||
func NewReindexer(client *Client, source string, reindexerFunc ReindexerFunc) *Reindexer {
|
||||
return &Reindexer{
|
||||
sourceClient: client,
|
||||
sourceIndex: source,
|
||||
reindexerFunc: reindexerFunc,
|
||||
statsOnly: true,
|
||||
}
|
||||
}
|
||||
|
||||
// TargetClient specifies a different client for the target. This is
|
||||
// necessary when the target index is in a different Elasticsearch cluster.
|
||||
// By default, the source and target clients are the same.
|
||||
func (ix *Reindexer) TargetClient(c *Client) *Reindexer {
|
||||
ix.targetClient = c
|
||||
return ix
|
||||
}
|
||||
|
||||
// Query specifies the query to apply to the source. It filters out those
|
||||
// documents to be indexed into target. A nil query does not filter out any
|
||||
// documents.
|
||||
func (ix *Reindexer) Query(q Query) *Reindexer {
|
||||
ix.query = q
|
||||
return ix
|
||||
}
|
||||
|
||||
// ScanFields specifies the fields the scan query should load.
|
||||
// The default fields are _source, _parent, _routing.
|
||||
func (ix *Reindexer) ScanFields(scanFields ...string) *Reindexer {
|
||||
ix.scanFields = scanFields
|
||||
return ix
|
||||
}
|
||||
|
||||
// BulkSize returns the number of documents to send to Elasticsearch per chunk.
|
||||
// The default is 500.
|
||||
func (ix *Reindexer) BulkSize(bulkSize int) *Reindexer {
|
||||
ix.bulkSize = bulkSize
|
||||
return ix
|
||||
}
|
||||
|
||||
// Size is the number of results to return per shard, not per request.
|
||||
// So a size of 10 which hits 5 shards will return a maximum of 50 results
|
||||
// per scan request.
|
||||
func (ix *Reindexer) Size(size int) *Reindexer {
|
||||
ix.size = size
|
||||
return ix
|
||||
}
|
||||
|
||||
// Scroll specifies for how long the scroll operation on the source index
|
||||
// should be maintained. The default is 5m.
|
||||
func (ix *Reindexer) Scroll(timeout string) *Reindexer {
|
||||
ix.scroll = timeout
|
||||
return ix
|
||||
}
|
||||
|
||||
// Progress indicates a callback that will be called while indexing.
|
||||
func (ix *Reindexer) Progress(f ReindexerProgressFunc) *Reindexer {
|
||||
ix.progress = f
|
||||
return ix
|
||||
}
|
||||
|
||||
// StatsOnly indicates whether the Do method should return details e.g. about
|
||||
// the documents that failed while indexing. It is true by default, i.e. only
|
||||
// the number of documents that succeeded/failed are returned. Set to false
|
||||
// if you want all the details.
|
||||
func (ix *Reindexer) StatsOnly(statsOnly bool) *Reindexer {
|
||||
ix.statsOnly = statsOnly
|
||||
return ix
|
||||
}
|
||||
|
||||
// Do starts the reindexing process.
|
||||
func (ix *Reindexer) Do() (*ReindexerResponse, error) {
|
||||
if ix.sourceClient == nil {
|
||||
return nil, errors.New("no source client")
|
||||
}
|
||||
if ix.sourceIndex == "" {
|
||||
return nil, errors.New("no source index")
|
||||
}
|
||||
if ix.targetClient == nil {
|
||||
ix.targetClient = ix.sourceClient
|
||||
}
|
||||
if ix.scanFields == nil {
|
||||
ix.scanFields = []string{"_source", "_parent", "_routing"}
|
||||
}
|
||||
if ix.bulkSize <= 0 {
|
||||
ix.bulkSize = 500
|
||||
}
|
||||
if ix.scroll == "" {
|
||||
ix.scroll = "5m"
|
||||
}
|
||||
|
||||
// Count total to report progress (if necessary)
|
||||
var err error
|
||||
var current, total int64
|
||||
if ix.progress != nil {
|
||||
total, err = ix.count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare scan and scroll to iterate through the source index
|
||||
scanner := ix.sourceClient.Scan(ix.sourceIndex).Scroll(ix.scroll).Fields(ix.scanFields...)
|
||||
if ix.query != nil {
|
||||
scanner = scanner.Query(ix.query)
|
||||
}
|
||||
if ix.size > 0 {
|
||||
scanner = scanner.Size(ix.size)
|
||||
}
|
||||
cursor, err := scanner.Do()
|
||||
|
||||
bulk := ix.targetClient.Bulk()
|
||||
|
||||
ret := &ReindexerResponse{
|
||||
Errors: make([]*BulkResponseItem, 0),
|
||||
}
|
||||
|
||||
// Main loop iterates through the source index and bulk indexes into target.
|
||||
for {
|
||||
docs, err := cursor.Next()
|
||||
if err == EOS {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if docs.TotalHits() > 0 {
|
||||
for _, hit := range docs.Hits.Hits {
|
||||
if ix.progress != nil {
|
||||
current++
|
||||
ix.progress(current, total)
|
||||
}
|
||||
|
||||
err := ix.reindexerFunc(hit, bulk)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if bulk.NumberOfActions() >= ix.bulkSize {
|
||||
bulk, err = ix.commit(bulk, ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final flush
|
||||
if bulk.NumberOfActions() > 0 {
|
||||
bulk, err = ix.commit(bulk, ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
bulk = nil
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// count returns the number of documents in the source index.
|
||||
// The query is taken into account, if specified.
|
||||
func (ix *Reindexer) count() (int64, error) {
|
||||
service := ix.sourceClient.Count(ix.sourceIndex)
|
||||
if ix.query != nil {
|
||||
service = service.Query(ix.query)
|
||||
}
|
||||
return service.Do()
|
||||
}
|
||||
|
||||
// commit commits a bulk, updates the stats, and returns a fresh bulk service.
|
||||
func (ix *Reindexer) commit(bulk *BulkService, ret *ReindexerResponse) (*BulkService, error) {
|
||||
bres, err := bulk.Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Success += int64(len(bres.Succeeded()))
|
||||
failed := bres.Failed()
|
||||
ret.Failed += int64(len(failed))
|
||||
if !ix.statsOnly {
|
||||
ret.Errors = append(ret.Errors, failed...)
|
||||
}
|
||||
bulk = ix.targetClient.Bulk()
|
||||
return bulk, nil
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Elasticsearch-specific HTTP request
|
||||
type Request http.Request
|
||||
|
||||
// NewRequest is a http.Request and adds features such as encoding the body.
|
||||
func NewRequest(method, url string) (*Request, error) {
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("User-Agent", "elastic/"+Version+" ("+runtime.GOOS+"-"+runtime.GOARCH+")")
|
||||
req.Header.Add("Accept", "application/json")
|
||||
return (*Request)(req), nil
|
||||
}
|
||||
|
||||
// SetBasicAuth wraps http.Request's SetBasicAuth.
|
||||
func (r *Request) SetBasicAuth(username, password string) {
|
||||
((*http.Request)(r)).SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
// SetBody encodes the body in the request. Optionally, it performs GZIP compression.
|
||||
func (r *Request) SetBody(body interface{}, gzipCompress bool) error {
|
||||
switch b := body.(type) {
|
||||
case string:
|
||||
if gzipCompress {
|
||||
return r.setBodyGzip(b)
|
||||
} else {
|
||||
return r.setBodyString(b)
|
||||
}
|
||||
default:
|
||||
if gzipCompress {
|
||||
return r.setBodyGzip(body)
|
||||
} else {
|
||||
return r.setBodyJson(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setBodyJson encodes the body as a struct to be marshaled via json.Marshal.
|
||||
func (r *Request) setBodyJson(data interface{}) error {
|
||||
body, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r.setBodyReader(bytes.NewReader(body))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setBodyString encodes the body as a string.
|
||||
func (r *Request) setBodyString(body string) error {
|
||||
return r.setBodyReader(strings.NewReader(body))
|
||||
}
|
||||
|
||||
// setBodyGzip gzip's the body. It accepts both strings and structs as body.
|
||||
// The latter will be encoded via json.Marshal.
|
||||
func (r *Request) setBodyGzip(body interface{}) error {
|
||||
switch b := body.(type) {
|
||||
case string:
|
||||
buf := new(bytes.Buffer)
|
||||
w := gzip.NewWriter(buf)
|
||||
if _, err := w.Write([]byte(b)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Add("Content-Encoding", "gzip")
|
||||
r.Header.Add("Vary", "Accept-Encoding")
|
||||
return r.setBodyReader(bytes.NewReader(buf.Bytes()))
|
||||
default:
|
||||
data, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
w := gzip.NewWriter(buf)
|
||||
if _, err := w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Add("Content-Encoding", "gzip")
|
||||
r.Header.Add("Vary", "Accept-Encoding")
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
return r.setBodyReader(bytes.NewReader(buf.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
// setBodyReader writes the body from an io.Reader.
|
||||
func (r *Request) setBodyReader(body io.Reader) error {
|
||||
rc, ok := body.(io.ReadCloser)
|
||||
if !ok && body != nil {
|
||||
rc = ioutil.NopCloser(body)
|
||||
}
|
||||
r.Body = rc
|
||||
if body != nil {
|
||||
switch v := body.(type) {
|
||||
case *strings.Reader:
|
||||
r.ContentLength = int64(v.Len())
|
||||
case *bytes.Buffer:
|
||||
r.ContentLength = int64(v.Len())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
type Rescore struct {
|
||||
rescorer Rescorer
|
||||
windowSize *int
|
||||
defaultRescoreWindowSize *int
|
||||
}
|
||||
|
||||
func NewRescore() *Rescore {
|
||||
return &Rescore{}
|
||||
}
|
||||
|
||||
func (r *Rescore) WindowSize(windowSize int) *Rescore {
|
||||
r.windowSize = &windowSize
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Rescore) IsEmpty() bool {
|
||||
return r.rescorer == nil
|
||||
}
|
||||
|
||||
func (r *Rescore) Rescorer(rescorer Rescorer) *Rescore {
|
||||
r.rescorer = rescorer
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Rescore) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if r.windowSize != nil {
|
||||
source["window_size"] = *r.windowSize
|
||||
} else if r.defaultRescoreWindowSize != nil {
|
||||
source["window_size"] = *r.defaultRescoreWindowSize
|
||||
}
|
||||
rescorerSrc, err := r.rescorer.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source[r.rescorer.Name()] = rescorerSrc
|
||||
return source, nil
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
type Rescorer interface {
|
||||
Name() string
|
||||
Source() (interface{}, error)
|
||||
}
|
||||
|
||||
// -- Query Rescorer --
|
||||
|
||||
type QueryRescorer struct {
|
||||
query Query
|
||||
rescoreQueryWeight *float64
|
||||
queryWeight *float64
|
||||
scoreMode string
|
||||
}
|
||||
|
||||
func NewQueryRescorer(query Query) *QueryRescorer {
|
||||
return &QueryRescorer{
|
||||
query: query,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *QueryRescorer) Name() string {
|
||||
return "query"
|
||||
}
|
||||
|
||||
func (r *QueryRescorer) RescoreQueryWeight(rescoreQueryWeight float64) *QueryRescorer {
|
||||
r.rescoreQueryWeight = &rescoreQueryWeight
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *QueryRescorer) QueryWeight(queryWeight float64) *QueryRescorer {
|
||||
r.queryWeight = &queryWeight
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *QueryRescorer) ScoreMode(scoreMode string) *QueryRescorer {
|
||||
r.scoreMode = scoreMode
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *QueryRescorer) Source() (interface{}, error) {
|
||||
rescoreQuery, err := r.query.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
source := make(map[string]interface{})
|
||||
source["rescore_query"] = rescoreQuery
|
||||
if r.queryWeight != nil {
|
||||
source["query_weight"] = *r.queryWeight
|
||||
}
|
||||
if r.rescoreQueryWeight != nil {
|
||||
source["rescore_query_weight"] = *r.rescoreQueryWeight
|
||||
}
|
||||
if r.scoreMode != "" {
|
||||
source["score_mode"] = r.scoreMode
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Response represents a response from Elasticsearch.
|
||||
type Response struct {
|
||||
// StatusCode is the HTTP status code, e.g. 200.
|
||||
StatusCode int
|
||||
// Header is the HTTP header from the HTTP response.
|
||||
// Keys in the map are canonicalized (see http.CanonicalHeaderKey).
|
||||
Header http.Header
|
||||
// Body is the deserialized response body.
|
||||
Body json.RawMessage
|
||||
}
|
||||
|
||||
// newResponse creates a new response from the HTTP response.
|
||||
func (c *Client) newResponse(res *http.Response) (*Response, error) {
|
||||
r := &Response{
|
||||
StatusCode: res.StatusCode,
|
||||
Header: res.Header,
|
||||
}
|
||||
if res.Body != nil {
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// HEAD requests return a body but no content
|
||||
if len(slurp) > 0 {
|
||||
if err := c.decoder.Decode(slurp, &r.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultKeepAlive = "5m"
|
||||
)
|
||||
|
||||
var (
|
||||
// End of stream (or scan)
|
||||
EOS = errors.New("EOS")
|
||||
|
||||
// No ScrollId
|
||||
ErrNoScrollId = errors.New("no scrollId")
|
||||
)
|
||||
|
||||
// ScanService manages a cursor through documents in Elasticsearch.
|
||||
type ScanService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
types []string
|
||||
keepAlive string
|
||||
searchSource *SearchSource
|
||||
pretty bool
|
||||
routing string
|
||||
preference string
|
||||
size *int
|
||||
}
|
||||
|
||||
// NewScanService creates a new service to iterate through the results
|
||||
// of a query.
|
||||
func NewScanService(client *Client) *ScanService {
|
||||
builder := &ScanService{
|
||||
client: client,
|
||||
searchSource: NewSearchSource().Query(NewMatchAllQuery()),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
// Index sets the name(s) of the index to use for scan.
|
||||
func (s *ScanService) Index(indices ...string) *ScanService {
|
||||
if s.indices == nil {
|
||||
s.indices = make([]string, 0)
|
||||
}
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Types allows to restrict the scan to a list of types.
|
||||
func (s *ScanService) Type(types ...string) *ScanService {
|
||||
if s.types == nil {
|
||||
s.types = make([]string, 0)
|
||||
}
|
||||
s.types = append(s.types, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Scroll is an alias for KeepAlive, the time to keep
|
||||
// the cursor alive (e.g. "5m" for 5 minutes).
|
||||
func (s *ScanService) Scroll(keepAlive string) *ScanService {
|
||||
s.keepAlive = keepAlive
|
||||
return s
|
||||
}
|
||||
|
||||
// KeepAlive sets the maximum time the cursor will be
|
||||
// available before expiration (e.g. "5m" for 5 minutes).
|
||||
func (s *ScanService) KeepAlive(keepAlive string) *ScanService {
|
||||
s.keepAlive = keepAlive
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields tells Elasticsearch to only load specific fields from a search hit.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-fields.html.
|
||||
func (s *ScanService) Fields(fields ...string) *ScanService {
|
||||
s.searchSource = s.searchSource.Fields(fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// SearchSource sets the search source builder to use with this service.
|
||||
func (s *ScanService) SearchSource(searchSource *SearchSource) *ScanService {
|
||||
s.searchSource = searchSource
|
||||
if s.searchSource == nil {
|
||||
s.searchSource = NewSearchSource().Query(NewMatchAllQuery())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing allows for (a comma-separated) list of specific routing values.
|
||||
func (s *ScanService) Routing(routings ...string) *ScanService {
|
||||
s.routing = strings.Join(routings, ",")
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference specifies the node or shard the operation should be
|
||||
// performed on (default: "random").
|
||||
func (s *ScanService) Preference(preference string) *ScanService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Query sets the query to perform, e.g. MatchAllQuery.
|
||||
func (s *ScanService) Query(query Query) *ScanService {
|
||||
s.searchSource = s.searchSource.Query(query)
|
||||
return s
|
||||
}
|
||||
|
||||
// PostFilter is executed as the last filter. It only affects the
|
||||
// search hits but not facets. See
|
||||
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-post-filter.html
|
||||
// for details.
|
||||
func (s *ScanService) PostFilter(postFilter Query) *ScanService {
|
||||
s.searchSource = s.searchSource.PostFilter(postFilter)
|
||||
return s
|
||||
}
|
||||
|
||||
// FetchSource indicates whether the response should contain the stored
|
||||
// _source for every hit.
|
||||
func (s *ScanService) FetchSource(fetchSource bool) *ScanService {
|
||||
s.searchSource = s.searchSource.FetchSource(fetchSource)
|
||||
return s
|
||||
}
|
||||
|
||||
// FetchSourceContext indicates how the _source should be fetched.
|
||||
func (s *ScanService) FetchSourceContext(fetchSourceContext *FetchSourceContext) *ScanService {
|
||||
s.searchSource = s.searchSource.FetchSourceContext(fetchSourceContext)
|
||||
return s
|
||||
}
|
||||
|
||||
// Version can be set to true to return a version for each search hit.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-version.html.
|
||||
func (s *ScanService) Version(version bool) *ScanService {
|
||||
s.searchSource = s.searchSource.Version(version)
|
||||
return s
|
||||
}
|
||||
|
||||
// Sort the results by the given field, in the given order.
|
||||
// Use the alternative SortWithInfo to use a struct to define the sorting.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html
|
||||
// for detailed documentation of sorting.
|
||||
func (s *ScanService) Sort(field string, ascending bool) *ScanService {
|
||||
s.searchSource = s.searchSource.Sort(field, ascending)
|
||||
return s
|
||||
}
|
||||
|
||||
// SortWithInfo defines how to sort results.
|
||||
// Use the Sort func for a shortcut.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html
|
||||
// for detailed documentation of sorting.
|
||||
func (s *ScanService) SortWithInfo(info SortInfo) *ScanService {
|
||||
s.searchSource = s.searchSource.SortWithInfo(info)
|
||||
return s
|
||||
}
|
||||
|
||||
// SortBy defines how to sort results.
|
||||
// Use the Sort func for a shortcut.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-sort.html
|
||||
// for detailed documentation of sorting.
|
||||
func (s *ScanService) SortBy(sorter ...Sorter) *ScanService {
|
||||
s.searchSource = s.searchSource.SortBy(sorter...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty enables the caller to indent the JSON output.
|
||||
func (s *ScanService) Pretty(pretty bool) *ScanService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Size is the number of results to return per shard, not per request.
|
||||
// So a size of 10 which hits 5 shards will return a maximum of 50 results
|
||||
// per scan request.
|
||||
func (s *ScanService) Size(size int) *ScanService {
|
||||
s.size = &size
|
||||
return s
|
||||
}
|
||||
|
||||
// Do executes the query and returns a "server-side cursor".
|
||||
func (s *ScanService) Do() (*ScanCursor, error) {
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err := uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
if len(indexPart) > 0 {
|
||||
path += strings.Join(indexPart, ",")
|
||||
}
|
||||
|
||||
// Types
|
||||
typesPart := make([]string, 0)
|
||||
for _, typ := range s.types {
|
||||
typ, err := uritemplates.Expand("{type}", map[string]string{
|
||||
"type": typ,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesPart = append(typesPart, typ)
|
||||
}
|
||||
if len(typesPart) > 0 {
|
||||
path += "/" + strings.Join(typesPart, ",")
|
||||
}
|
||||
|
||||
// Search
|
||||
path += "/_search"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if !s.searchSource.hasSort() {
|
||||
// TODO: ES 2.1 deprecates search_type=scan. See https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking_21_search_changes.html#_literal_search_type_scan_literal_deprecated.
|
||||
params.Set("search_type", "scan")
|
||||
}
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.keepAlive != "" {
|
||||
params.Set("scroll", s.keepAlive)
|
||||
} else {
|
||||
params.Set("scroll", defaultKeepAlive)
|
||||
}
|
||||
if s.size != nil && *s.size > 0 {
|
||||
params.Set("size", fmt.Sprintf("%d", *s.size))
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
|
||||
// Get response
|
||||
body, err := s.searchSource.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
searchResult := new(SearchResult)
|
||||
if err := s.client.decoder.Decode(res.Body, searchResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cursor := NewScanCursor(s.client, s.keepAlive, s.pretty, searchResult)
|
||||
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
// scanCursor represents a single page of results from
|
||||
// an Elasticsearch Scan operation.
|
||||
type ScanCursor struct {
|
||||
Results *SearchResult
|
||||
|
||||
client *Client
|
||||
keepAlive string
|
||||
pretty bool
|
||||
currentPage int
|
||||
}
|
||||
|
||||
// newScanCursor returns a new initialized instance
|
||||
// of scanCursor.
|
||||
func NewScanCursor(client *Client, keepAlive string, pretty bool, searchResult *SearchResult) *ScanCursor {
|
||||
return &ScanCursor{
|
||||
client: client,
|
||||
keepAlive: keepAlive,
|
||||
pretty: pretty,
|
||||
Results: searchResult,
|
||||
}
|
||||
}
|
||||
|
||||
// TotalHits is a convenience method that returns the number
|
||||
// of hits the cursor will iterate through.
|
||||
func (c *ScanCursor) TotalHits() int64 {
|
||||
if c.Results.Hits == nil {
|
||||
return 0
|
||||
}
|
||||
return c.Results.Hits.TotalHits
|
||||
}
|
||||
|
||||
// Next returns the next search result or nil when all
|
||||
// documents have been scanned.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// for {
|
||||
// res, err := cursor.Next()
|
||||
// if err == elastic.EOS {
|
||||
// // End of stream (or scan)
|
||||
// break
|
||||
// }
|
||||
// if err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
// // Work with res
|
||||
// }
|
||||
//
|
||||
func (c *ScanCursor) Next() (*SearchResult, error) {
|
||||
if c.currentPage > 0 {
|
||||
if c.Results.Hits == nil || len(c.Results.Hits.Hits) == 0 || c.Results.Hits.TotalHits == 0 {
|
||||
return nil, EOS
|
||||
}
|
||||
}
|
||||
if c.Results.ScrollId == "" {
|
||||
return nil, EOS
|
||||
}
|
||||
|
||||
// Build url
|
||||
path := "/_search/scroll"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if c.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", c.pretty))
|
||||
}
|
||||
if c.keepAlive != "" {
|
||||
params.Set("scroll", c.keepAlive)
|
||||
} else {
|
||||
params.Set("scroll", defaultKeepAlive)
|
||||
}
|
||||
|
||||
// Set body
|
||||
body := c.Results.ScrollId
|
||||
|
||||
// Get response
|
||||
res, err := c.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
c.Results = &SearchResult{ScrollId: body}
|
||||
if err := c.client.decoder.Decode(res.Body, c.Results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.currentPage += 1
|
||||
|
||||
return c.Results, nil
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import "errors"
|
||||
|
||||
// Script holds all the paramaters necessary to compile or find in cache
|
||||
// and then execute a script.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html
|
||||
// for details of scripting.
|
||||
type Script struct {
|
||||
script string
|
||||
typ string
|
||||
lang string
|
||||
params map[string]interface{}
|
||||
}
|
||||
|
||||
// NewScript creates and initializes a new Script.
|
||||
func NewScript(script string) *Script {
|
||||
return &Script{
|
||||
script: script,
|
||||
typ: "", // default type is "inline"
|
||||
params: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewScriptInline creates and initializes a new Script of type "inline".
|
||||
func NewScriptInline(script string) *Script {
|
||||
return NewScript(script).Type("inline")
|
||||
}
|
||||
|
||||
// NewScriptId creates and initializes a new Script of type "id".
|
||||
func NewScriptId(script string) *Script {
|
||||
return NewScript(script).Type("id")
|
||||
}
|
||||
|
||||
// NewScriptFile creates and initializes a new Script of type "file".
|
||||
func NewScriptFile(script string) *Script {
|
||||
return NewScript(script).Type("file")
|
||||
}
|
||||
|
||||
// Script is either the cache key of the script to be compiled/executed
|
||||
// or the actual script source code for inline scripts. For indexed
|
||||
// scripts this is the id used in the request. For file scripts this is
|
||||
// the file name.
|
||||
func (s *Script) Script(script string) *Script {
|
||||
s.script = script
|
||||
return s
|
||||
}
|
||||
|
||||
// Type sets the type of script: "inline", "id", or "file".
|
||||
func (s *Script) Type(typ string) *Script {
|
||||
s.typ = typ
|
||||
return s
|
||||
}
|
||||
|
||||
// Lang sets the language of the script. Permitted values are "groovy",
|
||||
// "expression", "mustache", "mvel" (default), "javascript", "python".
|
||||
// To use certain languages, you need to configure your server and/or
|
||||
// add plugins. See https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html
|
||||
// for details.
|
||||
func (s *Script) Lang(lang string) *Script {
|
||||
s.lang = lang
|
||||
return s
|
||||
}
|
||||
|
||||
// Param adds a key/value pair to the parameters that this script will be executed with.
|
||||
func (s *Script) Param(name string, value interface{}) *Script {
|
||||
if s.params == nil {
|
||||
s.params = make(map[string]interface{})
|
||||
}
|
||||
s.params[name] = value
|
||||
return s
|
||||
}
|
||||
|
||||
// Params sets the map of parameters this script will be executed with.
|
||||
func (s *Script) Params(params map[string]interface{}) *Script {
|
||||
s.params = params
|
||||
return s
|
||||
}
|
||||
|
||||
// Source returns the JSON serializable data for this Script.
|
||||
func (s *Script) Source() (interface{}, error) {
|
||||
if s.typ == "" && s.lang == "" && len(s.params) == 0 {
|
||||
return s.script, nil
|
||||
}
|
||||
source := make(map[string]interface{})
|
||||
if s.typ == "" {
|
||||
source["inline"] = s.script
|
||||
} else {
|
||||
source[s.typ] = s.script
|
||||
}
|
||||
if s.lang != "" {
|
||||
source["lang"] = s.lang
|
||||
}
|
||||
if len(s.params) > 0 {
|
||||
source["params"] = s.params
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Script Field --
|
||||
|
||||
// ScriptField is a single script field.
|
||||
type ScriptField struct {
|
||||
FieldName string // name of the field
|
||||
|
||||
script *Script
|
||||
}
|
||||
|
||||
// NewScriptField creates and initializes a new ScriptField.
|
||||
func NewScriptField(fieldName string, script *Script) *ScriptField {
|
||||
return &ScriptField{FieldName: fieldName, script: script}
|
||||
}
|
||||
|
||||
// Source returns the serializable JSON for the ScriptField.
|
||||
func (f *ScriptField) Source() (interface{}, error) {
|
||||
if f.script == nil {
|
||||
return nil, errors.New("ScriptField expects script")
|
||||
}
|
||||
source := make(map[string]interface{})
|
||||
src, err := f.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["script"] = src
|
||||
return source, nil
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// ScrollService manages a cursor through documents in Elasticsearch.
|
||||
type ScrollService struct {
|
||||
client *Client
|
||||
indices []string
|
||||
types []string
|
||||
keepAlive string
|
||||
query Query
|
||||
size *int
|
||||
pretty bool
|
||||
scrollId string
|
||||
}
|
||||
|
||||
func NewScrollService(client *Client) *ScrollService {
|
||||
builder := &ScrollService{
|
||||
client: client,
|
||||
query: NewMatchAllQuery(),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
func (s *ScrollService) Index(indices ...string) *ScrollService {
|
||||
if s.indices == nil {
|
||||
s.indices = make([]string, 0)
|
||||
}
|
||||
s.indices = append(s.indices, indices...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) Type(types ...string) *ScrollService {
|
||||
if s.types == nil {
|
||||
s.types = make([]string, 0)
|
||||
}
|
||||
s.types = append(s.types, types...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Scroll is an alias for KeepAlive, the time to keep
|
||||
// the cursor alive (e.g. "5m" for 5 minutes).
|
||||
func (s *ScrollService) Scroll(keepAlive string) *ScrollService {
|
||||
s.keepAlive = keepAlive
|
||||
return s
|
||||
}
|
||||
|
||||
// KeepAlive sets the maximum time the cursor will be
|
||||
// available before expiration (e.g. "5m" for 5 minutes).
|
||||
func (s *ScrollService) KeepAlive(keepAlive string) *ScrollService {
|
||||
s.keepAlive = keepAlive
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) Query(query Query) *ScrollService {
|
||||
s.query = query
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) Pretty(pretty bool) *ScrollService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) Size(size int) *ScrollService {
|
||||
s.size = &size
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) ScrollId(scrollId string) *ScrollService {
|
||||
s.scrollId = scrollId
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ScrollService) Do() (*SearchResult, error) {
|
||||
if s.scrollId == "" {
|
||||
return s.GetFirstPage()
|
||||
}
|
||||
return s.GetNextPage()
|
||||
}
|
||||
|
||||
func (s *ScrollService) GetFirstPage() (*SearchResult, error) {
|
||||
// Build url
|
||||
path := "/"
|
||||
|
||||
// Indices part
|
||||
indexPart := make([]string, 0)
|
||||
for _, index := range s.indices {
|
||||
index, err := uritemplates.Expand("{index}", map[string]string{
|
||||
"index": index,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexPart = append(indexPart, index)
|
||||
}
|
||||
if len(indexPart) > 0 {
|
||||
path += strings.Join(indexPart, ",")
|
||||
}
|
||||
|
||||
// Types
|
||||
typesPart := make([]string, 0)
|
||||
for _, typ := range s.types {
|
||||
typ, err := uritemplates.Expand("{type}", map[string]string{
|
||||
"type": typ,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesPart = append(typesPart, typ)
|
||||
}
|
||||
if len(typesPart) > 0 {
|
||||
path += "/" + strings.Join(typesPart, ",")
|
||||
}
|
||||
|
||||
// Search
|
||||
path += "/_search"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.keepAlive != "" {
|
||||
params.Set("scroll", s.keepAlive)
|
||||
} else {
|
||||
params.Set("scroll", defaultKeepAlive)
|
||||
}
|
||||
if s.size != nil && *s.size > 0 {
|
||||
params.Set("size", fmt.Sprintf("%d", *s.size))
|
||||
}
|
||||
|
||||
// Set body
|
||||
body := make(map[string]interface{})
|
||||
if s.query != nil {
|
||||
src, err := s.query.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body["query"] = src
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
searchResult := new(SearchResult)
|
||||
if err := s.client.decoder.Decode(res.Body, searchResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
func (s *ScrollService) GetNextPage() (*SearchResult, error) {
|
||||
if s.scrollId == "" {
|
||||
return nil, EOS
|
||||
}
|
||||
|
||||
// Build url
|
||||
path := "/_search/scroll"
|
||||
|
||||
// Parameters
|
||||
params := make(url.Values)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.keepAlive != "" {
|
||||
params.Set("scroll", s.keepAlive)
|
||||
} else {
|
||||
params.Set("scroll", defaultKeepAlive)
|
||||
}
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest("POST", path, params, s.scrollId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return result
|
||||
searchResult := new(SearchResult)
|
||||
if err := s.client.decoder.Decode(res.Body, searchResult); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine last page
|
||||
if searchResult == nil || searchResult.Hits == nil || len(searchResult.Hits.Hits) == 0 || searchResult.Hits.TotalHits == 0 {
|
||||
return nil, EOS
|
||||
}
|
||||
|
||||
return searchResult, nil
|
||||
}
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/olivere/elastic.v3/uritemplates"
|
||||
)
|
||||
|
||||
// Search for documents in Elasticsearch.
|
||||
type SearchService struct {
|
||||
client *Client
|
||||
searchSource *SearchSource
|
||||
source interface{}
|
||||
pretty bool
|
||||
searchType string
|
||||
index []string
|
||||
typ []string
|
||||
routing string
|
||||
preference string
|
||||
ignoreUnavailable *bool
|
||||
allowNoIndices *bool
|
||||
expandWildcards string
|
||||
}
|
||||
|
||||
// NewSearchService creates a new service for searching in Elasticsearch.
|
||||
func NewSearchService(client *Client) *SearchService {
|
||||
builder := &SearchService{
|
||||
client: client,
|
||||
searchSource: NewSearchSource(),
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
// SearchSource sets the search source builder to use with this service.
|
||||
func (s *SearchService) SearchSource(searchSource *SearchSource) *SearchService {
|
||||
s.searchSource = searchSource
|
||||
if s.searchSource == nil {
|
||||
s.searchSource = NewSearchSource()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Source allows the user to set the request body manually without using
|
||||
// any of the structs and interfaces in Elastic.
|
||||
func (s *SearchService) Source(source interface{}) *SearchService {
|
||||
s.source = source
|
||||
return s
|
||||
}
|
||||
|
||||
// Index sets the names of the indices to use for search.
|
||||
func (s *SearchService) Index(index ...string) *SearchService {
|
||||
if s.index == nil {
|
||||
s.index = make([]string, 0)
|
||||
}
|
||||
s.index = append(s.index, index...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Types adds search restrictions for a list of types.
|
||||
func (s *SearchService) Type(typ ...string) *SearchService {
|
||||
if s.typ == nil {
|
||||
s.typ = make([]string, 0)
|
||||
}
|
||||
s.typ = append(s.typ, typ...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Pretty enables the caller to indent the JSON output.
|
||||
func (s *SearchService) Pretty(pretty bool) *SearchService {
|
||||
s.pretty = pretty
|
||||
return s
|
||||
}
|
||||
|
||||
// Timeout sets the timeout to use, e.g. "1s" or "1000ms".
|
||||
func (s *SearchService) Timeout(timeout string) *SearchService {
|
||||
s.searchSource = s.searchSource.Timeout(timeout)
|
||||
return s
|
||||
}
|
||||
|
||||
// TimeoutInMillis sets the timeout in milliseconds.
|
||||
func (s *SearchService) TimeoutInMillis(timeoutInMillis int) *SearchService {
|
||||
s.searchSource = s.searchSource.TimeoutInMillis(timeoutInMillis)
|
||||
return s
|
||||
}
|
||||
|
||||
// SearchType sets the search operation type. Valid values are:
|
||||
// "query_then_fetch", "query_and_fetch", "dfs_query_then_fetch",
|
||||
// "dfs_query_and_fetch", "count", "scan".
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-search-type.html
|
||||
// for details.
|
||||
func (s *SearchService) SearchType(searchType string) *SearchService {
|
||||
s.searchType = searchType
|
||||
return s
|
||||
}
|
||||
|
||||
// Routing is a list of specific routing values to control the shards
|
||||
// the search will be executed on.
|
||||
func (s *SearchService) Routing(routings ...string) *SearchService {
|
||||
s.routing = strings.Join(routings, ",")
|
||||
return s
|
||||
}
|
||||
|
||||
// Preference sets the preference to execute the search. Defaults to
|
||||
// randomize across shards ("random"). Can be set to "_local" to prefer
|
||||
// local shards, "_primary" to execute on primary shards only,
|
||||
// or a custom value which guarantees that the same order will be used
|
||||
// across different requests.
|
||||
func (s *SearchService) Preference(preference string) *SearchService {
|
||||
s.preference = preference
|
||||
return s
|
||||
}
|
||||
|
||||
// Query sets the query to perform, e.g. MatchAllQuery.
|
||||
func (s *SearchService) Query(query Query) *SearchService {
|
||||
s.searchSource = s.searchSource.Query(query)
|
||||
return s
|
||||
}
|
||||
|
||||
// PostFilter will be executed after the query has been executed and
|
||||
// only affects the search hits, not the aggregations.
|
||||
// This filter is always executed as the last filtering mechanism.
|
||||
func (s *SearchService) PostFilter(postFilter Query) *SearchService {
|
||||
s.searchSource = s.searchSource.PostFilter(postFilter)
|
||||
return s
|
||||
}
|
||||
|
||||
// FetchSource indicates whether the response should contain the stored
|
||||
// _source for every hit.
|
||||
func (s *SearchService) FetchSource(fetchSource bool) *SearchService {
|
||||
s.searchSource = s.searchSource.FetchSource(fetchSource)
|
||||
return s
|
||||
}
|
||||
|
||||
// FetchSourceContext indicates how the _source should be fetched.
|
||||
func (s *SearchService) FetchSourceContext(fetchSourceContext *FetchSourceContext) *SearchService {
|
||||
s.searchSource = s.searchSource.FetchSourceContext(fetchSourceContext)
|
||||
return s
|
||||
}
|
||||
|
||||
// Highlight adds highlighting to the search.
|
||||
func (s *SearchService) Highlight(highlight *Highlight) *SearchService {
|
||||
s.searchSource = s.searchSource.Highlight(highlight)
|
||||
return s
|
||||
}
|
||||
|
||||
// GlobalSuggestText defines the global text to use with all suggesters.
|
||||
// This avoids repetition.
|
||||
func (s *SearchService) GlobalSuggestText(globalText string) *SearchService {
|
||||
s.searchSource = s.searchSource.GlobalSuggestText(globalText)
|
||||
return s
|
||||
}
|
||||
|
||||
// Suggester adds a suggester to the search.
|
||||
func (s *SearchService) Suggester(suggester Suggester) *SearchService {
|
||||
s.searchSource = s.searchSource.Suggester(suggester)
|
||||
return s
|
||||
}
|
||||
|
||||
// Aggregation adds an aggreation to perform as part of the search.
|
||||
func (s *SearchService) Aggregation(name string, aggregation Aggregation) *SearchService {
|
||||
s.searchSource = s.searchSource.Aggregation(name, aggregation)
|
||||
return s
|
||||
}
|
||||
|
||||
// MinScore sets the minimum score below which docs will be filtered out.
|
||||
func (s *SearchService) MinScore(minScore float64) *SearchService {
|
||||
s.searchSource = s.searchSource.MinScore(minScore)
|
||||
return s
|
||||
}
|
||||
|
||||
// From index to start the search from. Defaults to 0.
|
||||
func (s *SearchService) From(from int) *SearchService {
|
||||
s.searchSource = s.searchSource.From(from)
|
||||
return s
|
||||
}
|
||||
|
||||
// Size is the number of search hits to return. Defaults to 10.
|
||||
func (s *SearchService) Size(size int) *SearchService {
|
||||
s.searchSource = s.searchSource.Size(size)
|
||||
return s
|
||||
}
|
||||
|
||||
// Explain indicates whether each search hit should be returned with
|
||||
// an explanation of the hit (ranking).
|
||||
func (s *SearchService) Explain(explain bool) *SearchService {
|
||||
s.searchSource = s.searchSource.Explain(explain)
|
||||
return s
|
||||
}
|
||||
|
||||
// Version indicates whether each search hit should be returned with
|
||||
// a version associated to it.
|
||||
func (s *SearchService) Version(version bool) *SearchService {
|
||||
s.searchSource = s.searchSource.Version(version)
|
||||
return s
|
||||
}
|
||||
|
||||
// Sort adds a sort order.
|
||||
func (s *SearchService) Sort(field string, ascending bool) *SearchService {
|
||||
s.searchSource = s.searchSource.Sort(field, ascending)
|
||||
return s
|
||||
}
|
||||
|
||||
// SortWithInfo adds a sort order.
|
||||
func (s *SearchService) SortWithInfo(info SortInfo) *SearchService {
|
||||
s.searchSource = s.searchSource.SortWithInfo(info)
|
||||
return s
|
||||
}
|
||||
|
||||
// SortBy adds a sort order.
|
||||
func (s *SearchService) SortBy(sorter ...Sorter) *SearchService {
|
||||
s.searchSource = s.searchSource.SortBy(sorter...)
|
||||
return s
|
||||
}
|
||||
|
||||
// NoFields indicates that no fields should be loaded, resulting in only
|
||||
// id and type to be returned per field.
|
||||
func (s *SearchService) NoFields() *SearchService {
|
||||
s.searchSource = s.searchSource.NoFields()
|
||||
return s
|
||||
}
|
||||
|
||||
// Field adds a single field to load and return (note, must be stored) as
|
||||
// part of the search request. If none are specified, the source of the
|
||||
// document will be returned.
|
||||
func (s *SearchService) Field(fieldName string) *SearchService {
|
||||
s.searchSource = s.searchSource.Field(fieldName)
|
||||
return s
|
||||
}
|
||||
|
||||
// Fields sets the fields to load and return as part of the search request.
|
||||
// If none are specified, the source of the document will be returned.
|
||||
func (s *SearchService) Fields(fields ...string) *SearchService {
|
||||
s.searchSource = s.searchSource.Fields(fields...)
|
||||
return s
|
||||
}
|
||||
|
||||
// IgnoreUnavailable indicates whether the specified concrete indices
|
||||
// should be ignored when unavailable (missing or closed).
|
||||
func (s *SearchService) IgnoreUnavailable(ignoreUnavailable bool) *SearchService {
|
||||
s.ignoreUnavailable = &ignoreUnavailable
|
||||
return s
|
||||
}
|
||||
|
||||
// AllowNoIndices indicates whether to ignore if a wildcard indices
|
||||
// expression resolves into no concrete indices. (This includes `_all` string
|
||||
// or when no indices have been specified).
|
||||
func (s *SearchService) AllowNoIndices(allowNoIndices bool) *SearchService {
|
||||
s.allowNoIndices = &allowNoIndices
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandWildcards indicates whether to expand wildcard expression to
|
||||
// concrete indices that are open, closed or both.
|
||||
func (s *SearchService) ExpandWildcards(expandWildcards string) *SearchService {
|
||||
s.expandWildcards = expandWildcards
|
||||
return s
|
||||
}
|
||||
|
||||
// buildURL builds the URL for the operation.
|
||||
func (s *SearchService) buildURL() (string, url.Values, error) {
|
||||
var err error
|
||||
var path string
|
||||
|
||||
if len(s.index) > 0 && len(s.typ) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/{type}/_search", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else if len(s.index) > 0 {
|
||||
path, err = uritemplates.Expand("/{index}/_search", map[string]string{
|
||||
"index": strings.Join(s.index, ","),
|
||||
})
|
||||
} else if len(s.typ) > 0 {
|
||||
path, err = uritemplates.Expand("/_all/{type}/_search", map[string]string{
|
||||
"type": strings.Join(s.typ, ","),
|
||||
})
|
||||
} else {
|
||||
path = "/_search"
|
||||
}
|
||||
if err != nil {
|
||||
return "", url.Values{}, err
|
||||
}
|
||||
|
||||
// Add query string parameters
|
||||
params := url.Values{}
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if s.searchType != "" {
|
||||
params.Set("search_type", s.searchType)
|
||||
}
|
||||
if s.routing != "" {
|
||||
params.Set("routing", s.routing)
|
||||
}
|
||||
if s.preference != "" {
|
||||
params.Set("preference", s.preference)
|
||||
}
|
||||
if s.allowNoIndices != nil {
|
||||
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
|
||||
}
|
||||
if s.expandWildcards != "" {
|
||||
params.Set("expand_wildcards", s.expandWildcards)
|
||||
}
|
||||
if s.ignoreUnavailable != nil {
|
||||
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
|
||||
}
|
||||
return path, params, nil
|
||||
}
|
||||
|
||||
// Validate checks if the operation is valid.
|
||||
func (s *SearchService) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do executes the search and returns a SearchResult.
|
||||
func (s *SearchService) Do() (*SearchResult, error) {
|
||||
// Check pre-conditions
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get URL for request
|
||||
path, params, err := s.buildURL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Perform request
|
||||
var body interface{}
|
||||
if s.source != nil {
|
||||
body = s.source
|
||||
} else {
|
||||
src, err := s.searchSource.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = src
|
||||
}
|
||||
res, err := s.client.PerformRequest("POST", path, params, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return search results
|
||||
ret := new(SearchResult)
|
||||
if err := s.client.decoder.Decode(res.Body, ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// SearchResult is the result of a search in Elasticsearch.
|
||||
type SearchResult struct {
|
||||
TookInMillis int64 `json:"took"` // search time in milliseconds
|
||||
ScrollId string `json:"_scroll_id"` // only used with Scroll and Scan operations
|
||||
Hits *SearchHits `json:"hits"` // the actual search hits
|
||||
Suggest SearchSuggest `json:"suggest"` // results from suggesters
|
||||
Aggregations Aggregations `json:"aggregations"` // results from aggregations
|
||||
TimedOut bool `json:"timed_out"` // true if the search timed out
|
||||
//Error string `json:"error,omitempty"` // used in MultiSearch only
|
||||
// TODO double-check that MultiGet now returns details error information
|
||||
Error *ErrorDetails `json:"error,omitempty"` // only used in MultiGet
|
||||
}
|
||||
|
||||
// TotalHits is a convenience function to return the number of hits for
|
||||
// a search result.
|
||||
func (r *SearchResult) TotalHits() int64 {
|
||||
if r.Hits != nil {
|
||||
return r.Hits.TotalHits
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Each is a utility function to iterate over all hits. It saves you from
|
||||
// checking for nil values. Notice that Each will ignore errors in
|
||||
// serializing JSON.
|
||||
func (r *SearchResult) Each(typ reflect.Type) []interface{} {
|
||||
if r.Hits == nil || r.Hits.Hits == nil || len(r.Hits.Hits) == 0 {
|
||||
return nil
|
||||
}
|
||||
slice := make([]interface{}, 0)
|
||||
for _, hit := range r.Hits.Hits {
|
||||
v := reflect.New(typ).Elem()
|
||||
if err := json.Unmarshal(*hit.Source, v.Addr().Interface()); err == nil {
|
||||
slice = append(slice, v.Interface())
|
||||
}
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// SearchHits specifies the list of search hits.
|
||||
type SearchHits struct {
|
||||
TotalHits int64 `json:"total"` // total number of hits found
|
||||
MaxScore *float64 `json:"max_score"` // maximum score of all hits
|
||||
Hits []*SearchHit `json:"hits"` // the actual hits returned
|
||||
}
|
||||
|
||||
// SearchHit is a single hit.
|
||||
type SearchHit struct {
|
||||
Score *float64 `json:"_score"` // computed score
|
||||
Index string `json:"_index"` // index name
|
||||
Type string `json:"_type"` // type meta field
|
||||
Id string `json:"_id"` // external or internal
|
||||
Uid string `json:"_uid"` // uid meta field (see MapperService.java for all meta fields)
|
||||
Timestamp int64 `json:"_timestamp"` // timestamp meta field
|
||||
TTL int64 `json:"_ttl"` // ttl meta field
|
||||
Routing string `json:"_routing"` // routing meta field
|
||||
Parent string `json:"_parent"` // parent meta field
|
||||
Version *int64 `json:"_version"` // version number, when Version is set to true in SearchService
|
||||
Sort []interface{} `json:"sort"` // sort information
|
||||
Highlight SearchHitHighlight `json:"highlight"` // highlighter information
|
||||
Source *json.RawMessage `json:"_source"` // stored document source
|
||||
Fields map[string]interface{} `json:"fields"` // returned fields
|
||||
Explanation *SearchExplanation `json:"_explanation"` // explains how the score was computed
|
||||
MatchedQueries []string `json:"matched_queries"` // matched queries
|
||||
InnerHits map[string]*SearchHitInnerHits `json:"inner_hits"` // inner hits with ES >= 1.5.0
|
||||
|
||||
// Shard
|
||||
// HighlightFields
|
||||
// SortValues
|
||||
// MatchedFilters
|
||||
}
|
||||
|
||||
type SearchHitInnerHits struct {
|
||||
Hits *SearchHits `json:"hits"`
|
||||
}
|
||||
|
||||
// SearchExplanation explains how the score for a hit was computed.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-explain.html.
|
||||
type SearchExplanation struct {
|
||||
Value float64 `json:"value"` // e.g. 1.0
|
||||
Description string `json:"description"` // e.g. "boost" or "ConstantScore(*:*), product of:"
|
||||
Details []SearchExplanation `json:"details,omitempty"` // recursive details
|
||||
}
|
||||
|
||||
// Suggest
|
||||
|
||||
// SearchSuggest is a map of suggestions.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters.html.
|
||||
type SearchSuggest map[string][]SearchSuggestion
|
||||
|
||||
// SearchSuggestion is a single search suggestion.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters.html.
|
||||
type SearchSuggestion struct {
|
||||
Text string `json:"text"`
|
||||
Offset int `json:"offset"`
|
||||
Length int `json:"length"`
|
||||
Options []SearchSuggestionOption `json:"options"`
|
||||
}
|
||||
|
||||
// SearchSuggestionOption is an option of a SearchSuggestion.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters.html.
|
||||
type SearchSuggestionOption struct {
|
||||
Text string `json:"text"`
|
||||
Highlighted string `json:"highlighted"`
|
||||
Score float64 `json:"score"`
|
||||
CollateMatch bool `json:"collate_match"`
|
||||
Freq int `json:"freq"` // deprecated in 2.x
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// Aggregations (see search_aggs.go)
|
||||
|
||||
// Highlighting
|
||||
|
||||
// SearchHitHighlight is the highlight information of a search hit.
|
||||
// See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-highlighting.html
|
||||
// for a general discussion of highlighting.
|
||||
type SearchHitHighlight map[string][]string
|
||||
+1274
File diff suppressed because it is too large
Load Diff
+76
@@ -0,0 +1,76 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// ChildrenAggregation is a special single bucket aggregation that enables
|
||||
// aggregating from buckets on parent document types to buckets on child documents.
|
||||
// It is available from 1.4.0.Beta1 upwards.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-children-aggregation.html
|
||||
type ChildrenAggregation struct {
|
||||
typ string
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewChildrenAggregation() *ChildrenAggregation {
|
||||
return &ChildrenAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ChildrenAggregation) Type(typ string) *ChildrenAggregation {
|
||||
a.typ = typ
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ChildrenAggregation) SubAggregation(name string, subAggregation Aggregation) *ChildrenAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *ChildrenAggregation) Meta(metaData map[string]interface{}) *ChildrenAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ChildrenAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "to-answers" : {
|
||||
// "children": {
|
||||
// "type" : "answer"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "type" : ... } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["children"] = opts
|
||||
opts["type"] = a.typ
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// DateHistogramAggregation is a multi-bucket aggregation similar to the
|
||||
// histogram except it can only be applied on date values.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-datehistogram-aggregation.html
|
||||
type DateHistogramAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
missing interface{}
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
|
||||
interval string
|
||||
order string
|
||||
orderAsc bool
|
||||
minDocCount *int64
|
||||
extendedBoundsMin interface{}
|
||||
extendedBoundsMax interface{}
|
||||
timeZone string
|
||||
format string
|
||||
offset string
|
||||
}
|
||||
|
||||
// NewDateHistogramAggregation creates a new DateHistogramAggregation.
|
||||
func NewDateHistogramAggregation() *DateHistogramAggregation {
|
||||
return &DateHistogramAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
// Field on which the aggregation is processed.
|
||||
func (a *DateHistogramAggregation) Field(field string) *DateHistogramAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) Script(script *Script) *DateHistogramAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing configures the value to use when documents miss a value.
|
||||
func (a *DateHistogramAggregation) Missing(missing interface{}) *DateHistogramAggregation {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) SubAggregation(name string, subAggregation Aggregation) *DateHistogramAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *DateHistogramAggregation) Meta(metaData map[string]interface{}) *DateHistogramAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
// Interval by which the aggregation gets processed.
|
||||
// Allowed values are: "year", "quarter", "month", "week", "day",
|
||||
// "hour", "minute". It also supports time settings like "1.5h"
|
||||
// (up to "w" for weeks).
|
||||
func (a *DateHistogramAggregation) Interval(interval string) *DateHistogramAggregation {
|
||||
a.interval = interval
|
||||
return a
|
||||
}
|
||||
|
||||
// Order specifies the sort order. Valid values for order are:
|
||||
// "_key", "_count", a sub-aggregation name, or a sub-aggregation name
|
||||
// with a metric.
|
||||
func (a *DateHistogramAggregation) Order(order string, asc bool) *DateHistogramAggregation {
|
||||
a.order = order
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByCount(asc bool) *DateHistogramAggregation {
|
||||
// "order" : { "_count" : "asc" }
|
||||
a.order = "_count"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByCountAsc() *DateHistogramAggregation {
|
||||
return a.OrderByCount(true)
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByCountDesc() *DateHistogramAggregation {
|
||||
return a.OrderByCount(false)
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByKey(asc bool) *DateHistogramAggregation {
|
||||
// "order" : { "_key" : "asc" }
|
||||
a.order = "_key"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByKeyAsc() *DateHistogramAggregation {
|
||||
return a.OrderByKey(true)
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) OrderByKeyDesc() *DateHistogramAggregation {
|
||||
return a.OrderByKey(false)
|
||||
}
|
||||
|
||||
// OrderByAggregation creates a bucket ordering strategy which sorts buckets
|
||||
// based on a single-valued calc get.
|
||||
func (a *DateHistogramAggregation) OrderByAggregation(aggName string, asc bool) *DateHistogramAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "avg_height" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "avg_height" : { "avg" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
// OrderByAggregationAndMetric creates a bucket ordering strategy which
|
||||
// sorts buckets based on a multi-valued calc get.
|
||||
func (a *DateHistogramAggregation) OrderByAggregationAndMetric(aggName, metric string, asc bool) *DateHistogramAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "height_stats.avg" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "height_stats" : { "stats" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName + "." + metric
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
// MinDocCount sets the minimum document count per bucket.
|
||||
// Buckets with less documents than this min value will not be returned.
|
||||
func (a *DateHistogramAggregation) MinDocCount(minDocCount int64) *DateHistogramAggregation {
|
||||
a.minDocCount = &minDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
// TimeZone sets the timezone in which to translate dates before computing buckets.
|
||||
func (a *DateHistogramAggregation) TimeZone(timeZone string) *DateHistogramAggregation {
|
||||
a.timeZone = timeZone
|
||||
return a
|
||||
}
|
||||
|
||||
// Format sets the format to use for dates.
|
||||
func (a *DateHistogramAggregation) Format(format string) *DateHistogramAggregation {
|
||||
a.format = format
|
||||
return a
|
||||
}
|
||||
|
||||
// Offset sets the offset of time intervals in the histogram, e.g. "+6h".
|
||||
func (a *DateHistogramAggregation) Offset(offset string) *DateHistogramAggregation {
|
||||
a.offset = offset
|
||||
return a
|
||||
}
|
||||
|
||||
// ExtendedBounds accepts int, int64, string, or time.Time values.
|
||||
// In case the lower value in the histogram would be greater than min or the
|
||||
// upper value would be less than max, empty buckets will be generated.
|
||||
func (a *DateHistogramAggregation) ExtendedBounds(min, max interface{}) *DateHistogramAggregation {
|
||||
a.extendedBoundsMin = min
|
||||
a.extendedBoundsMax = max
|
||||
return a
|
||||
}
|
||||
|
||||
// ExtendedBoundsMin accepts int, int64, string, or time.Time values.
|
||||
func (a *DateHistogramAggregation) ExtendedBoundsMin(min interface{}) *DateHistogramAggregation {
|
||||
a.extendedBoundsMin = min
|
||||
return a
|
||||
}
|
||||
|
||||
// ExtendedBoundsMax accepts int, int64, string, or time.Time values.
|
||||
func (a *DateHistogramAggregation) ExtendedBoundsMax(max interface{}) *DateHistogramAggregation {
|
||||
a.extendedBoundsMax = max
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateHistogramAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "articles_over_time" : {
|
||||
// "date_histogram" : {
|
||||
// "field" : "date",
|
||||
// "interval" : "month"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "date_histogram" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["date_histogram"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
if a.missing != nil {
|
||||
opts["missing"] = a.missing
|
||||
}
|
||||
|
||||
opts["interval"] = a.interval
|
||||
if a.minDocCount != nil {
|
||||
opts["min_doc_count"] = *a.minDocCount
|
||||
}
|
||||
if a.order != "" {
|
||||
o := make(map[string]interface{})
|
||||
if a.orderAsc {
|
||||
o[a.order] = "asc"
|
||||
} else {
|
||||
o[a.order] = "desc"
|
||||
}
|
||||
opts["order"] = o
|
||||
}
|
||||
if a.timeZone != "" {
|
||||
opts["time_zone"] = a.timeZone
|
||||
}
|
||||
if a.offset != "" {
|
||||
opts["offset"] = a.offset
|
||||
}
|
||||
if a.format != "" {
|
||||
opts["format"] = a.format
|
||||
}
|
||||
if a.extendedBoundsMin != nil || a.extendedBoundsMax != nil {
|
||||
bounds := make(map[string]interface{})
|
||||
if a.extendedBoundsMin != nil {
|
||||
bounds["min"] = a.extendedBoundsMin
|
||||
}
|
||||
if a.extendedBoundsMax != nil {
|
||||
bounds["max"] = a.extendedBoundsMax
|
||||
}
|
||||
opts["extended_bounds"] = bounds
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DateRangeAggregation is a range aggregation that is dedicated for
|
||||
// date values. The main difference between this aggregation and the
|
||||
// normal range aggregation is that the from and to values can be expressed
|
||||
// in Date Math expressions, and it is also possible to specify a
|
||||
// date format by which the from and to response fields will be returned.
|
||||
// Note that this aggregration includes the from value and excludes the to
|
||||
// value for each range.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html
|
||||
type DateRangeAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
keyed *bool
|
||||
unmapped *bool
|
||||
format string
|
||||
entries []DateRangeAggregationEntry
|
||||
}
|
||||
|
||||
type DateRangeAggregationEntry struct {
|
||||
Key string
|
||||
From interface{}
|
||||
To interface{}
|
||||
}
|
||||
|
||||
func NewDateRangeAggregation() *DateRangeAggregation {
|
||||
return &DateRangeAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
entries: make([]DateRangeAggregationEntry, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Field(field string) *DateRangeAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Script(script *Script) *DateRangeAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) SubAggregation(name string, subAggregation Aggregation) *DateRangeAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *DateRangeAggregation) Meta(metaData map[string]interface{}) *DateRangeAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Keyed(keyed bool) *DateRangeAggregation {
|
||||
a.keyed = &keyed
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Unmapped(unmapped bool) *DateRangeAggregation {
|
||||
a.unmapped = &unmapped
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Format(format string) *DateRangeAggregation {
|
||||
a.format = format
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddRange(from, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddRangeWithKey(key string, from, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddUnboundedTo(from interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddUnboundedToWithKey(key string, from interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddUnboundedFrom(to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) AddUnboundedFromWithKey(key string, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Lt(to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) LtWithKey(key string, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Between(from, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) BetweenWithKey(key string, from, to interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Gt(from interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) GtWithKey(key string, from interface{}) *DateRangeAggregation {
|
||||
a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "range" : {
|
||||
// "date_range": {
|
||||
// "field": "date",
|
||||
// "format": "MM-yyy",
|
||||
// "ranges": [
|
||||
// { "to": "now-10M/M" },
|
||||
// { "from": "now-10M/M" }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "date_range" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["date_range"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
|
||||
if a.keyed != nil {
|
||||
opts["keyed"] = *a.keyed
|
||||
}
|
||||
if a.unmapped != nil {
|
||||
opts["unmapped"] = *a.unmapped
|
||||
}
|
||||
if a.format != "" {
|
||||
opts["format"] = a.format
|
||||
}
|
||||
|
||||
ranges := make([]interface{}, 0)
|
||||
for _, ent := range a.entries {
|
||||
r := make(map[string]interface{})
|
||||
if ent.Key != "" {
|
||||
r["key"] = ent.Key
|
||||
}
|
||||
if ent.From != nil {
|
||||
switch from := ent.From.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["from"] = from
|
||||
case time.Time:
|
||||
r["from"] = from.Format(time.RFC3339)
|
||||
case string:
|
||||
r["from"] = from
|
||||
}
|
||||
}
|
||||
if ent.To != nil {
|
||||
switch to := ent.To.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["to"] = to
|
||||
case time.Time:
|
||||
r["to"] = to.Format(time.RFC3339)
|
||||
case string:
|
||||
r["to"] = to
|
||||
}
|
||||
}
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
opts["ranges"] = ranges
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// FilterAggregation defines a single bucket of all the documents
|
||||
// in the current document set context that match a specified filter.
|
||||
// Often this will be used to narrow down the current aggregation context
|
||||
// to a specific set of documents.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filter-aggregation.html
|
||||
type FilterAggregation struct {
|
||||
filter Query
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewFilterAggregation() *FilterAggregation {
|
||||
return &FilterAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *FilterAggregation) SubAggregation(name string, subAggregation Aggregation) *FilterAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *FilterAggregation) Meta(metaData map[string]interface{}) *FilterAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *FilterAggregation) Filter(filter Query) *FilterAggregation {
|
||||
a.filter = filter
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *FilterAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "in_stock_products" : {
|
||||
// "filter" : { "range" : { "stock" : { "gt" : 0 } } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "filter" : {} } part.
|
||||
|
||||
src, err := a.filter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source := make(map[string]interface{})
|
||||
source["filter"] = src
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import "errors"
|
||||
|
||||
// FiltersAggregation defines a multi bucket aggregations where each bucket
|
||||
// is associated with a filter. Each bucket will collect all documents that
|
||||
// match its associated filter.
|
||||
//
|
||||
// Notice that the caller has to decide whether to add filters by name
|
||||
// (using FilterWithName) or unnamed filters (using Filter or Filters). One cannot
|
||||
// use both named and unnamed filters.
|
||||
//
|
||||
// For details, see
|
||||
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filters-aggregation.html
|
||||
type FiltersAggregation struct {
|
||||
unnamedFilters []Query
|
||||
namedFilters map[string]Query
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
// NewFiltersAggregation initializes a new FiltersAggregation.
|
||||
func NewFiltersAggregation() *FiltersAggregation {
|
||||
return &FiltersAggregation{
|
||||
unnamedFilters: make([]Query, 0),
|
||||
namedFilters: make(map[string]Query),
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
// Filter adds an unnamed filter. Notice that you can
|
||||
// either use named or unnamed filters, but not both.
|
||||
func (a *FiltersAggregation) Filter(filter Query) *FiltersAggregation {
|
||||
a.unnamedFilters = append(a.unnamedFilters, filter)
|
||||
return a
|
||||
}
|
||||
|
||||
// Filters adds one or more unnamed filters. Notice that you can
|
||||
// either use named or unnamed filters, but not both.
|
||||
func (a *FiltersAggregation) Filters(filters ...Query) *FiltersAggregation {
|
||||
if len(filters) > 0 {
|
||||
a.unnamedFilters = append(a.unnamedFilters, filters...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterWithName adds a filter with a specific name. Notice that you can
|
||||
// either use named or unnamed filters, but not both.
|
||||
func (a *FiltersAggregation) FilterWithName(name string, filter Query) *FiltersAggregation {
|
||||
a.namedFilters[name] = filter
|
||||
return a
|
||||
}
|
||||
|
||||
// SubAggregation adds a sub-aggregation to this aggregation.
|
||||
func (a *FiltersAggregation) SubAggregation(name string, subAggregation Aggregation) *FiltersAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *FiltersAggregation) Meta(metaData map[string]interface{}) *FiltersAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
// Source returns the a JSON-serializable interface.
|
||||
// If the aggregation is invalid, an error is returned. This may e.g. happen
|
||||
// if you mixed named and unnamed filters.
|
||||
func (a *FiltersAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "messages" : {
|
||||
// "filters" : {
|
||||
// "filters" : {
|
||||
// "errors" : { "term" : { "body" : "error" }},
|
||||
// "warnings" : { "term" : { "body" : "warning" }}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the (outer) { "filters" : {} } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
filters := make(map[string]interface{})
|
||||
source["filters"] = filters
|
||||
|
||||
if len(a.unnamedFilters) > 0 && len(a.namedFilters) > 0 {
|
||||
return nil, errors.New("elastic: use either named or unnamed filters with FiltersAggregation but not both")
|
||||
}
|
||||
|
||||
if len(a.unnamedFilters) > 0 {
|
||||
arr := make([]interface{}, len(a.unnamedFilters))
|
||||
for i, filter := range a.unnamedFilters {
|
||||
src, err := filter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arr[i] = src
|
||||
}
|
||||
filters["filters"] = arr
|
||||
} else {
|
||||
dict := make(map[string]interface{})
|
||||
for key, filter := range a.namedFilters {
|
||||
src, err := filter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dict[key] = src
|
||||
}
|
||||
filters["filters"] = dict
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// GeoDistanceAggregation is a multi-bucket aggregation that works on geo_point fields
|
||||
// and conceptually works very similar to the range aggregation.
|
||||
// The user can define a point of origin and a set of distance range buckets.
|
||||
// The aggregation evaluate the distance of each document value from
|
||||
// the origin point and determines the buckets it belongs to based on
|
||||
// the ranges (a document belongs to a bucket if the distance between the
|
||||
// document and the origin falls within the distance range of the bucket).
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-aggregations-bucket-geodistance-aggregation.html
|
||||
type GeoDistanceAggregation struct {
|
||||
field string
|
||||
unit string
|
||||
distanceType string
|
||||
point string
|
||||
ranges []geoDistAggRange
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
type geoDistAggRange struct {
|
||||
Key string
|
||||
From interface{}
|
||||
To interface{}
|
||||
}
|
||||
|
||||
func NewGeoDistanceAggregation() *GeoDistanceAggregation {
|
||||
return &GeoDistanceAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
ranges: make([]geoDistAggRange, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) Field(field string) *GeoDistanceAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) Unit(unit string) *GeoDistanceAggregation {
|
||||
a.unit = unit
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) DistanceType(distanceType string) *GeoDistanceAggregation {
|
||||
a.distanceType = distanceType
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) Point(latLon string) *GeoDistanceAggregation {
|
||||
a.point = latLon
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) SubAggregation(name string, subAggregation Aggregation) *GeoDistanceAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *GeoDistanceAggregation) Meta(metaData map[string]interface{}) *GeoDistanceAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
func (a *GeoDistanceAggregation) AddRange(from, to interface{}) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) AddRangeWithKey(key string, from, to interface{}) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) AddUnboundedTo(from float64) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) AddUnboundedToWithKey(key string, from float64) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{Key: key, From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) AddUnboundedFrom(to float64) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) AddUnboundedFromWithKey(key string, to float64) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{Key: key, From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) Between(from, to interface{}) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) BetweenWithKey(key string, from, to interface{}) *GeoDistanceAggregation {
|
||||
a.ranges = append(a.ranges, geoDistAggRange{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoDistanceAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "rings_around_amsterdam" : {
|
||||
// "geo_distance" : {
|
||||
// "field" : "location",
|
||||
// "origin" : "52.3760, 4.894",
|
||||
// "ranges" : [
|
||||
// { "to" : 100 },
|
||||
// { "from" : 100, "to" : 300 },
|
||||
// { "from" : 300 }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "range" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["geo_distance"] = opts
|
||||
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.unit != "" {
|
||||
opts["unit"] = a.unit
|
||||
}
|
||||
if a.distanceType != "" {
|
||||
opts["distance_type"] = a.distanceType
|
||||
}
|
||||
if a.point != "" {
|
||||
opts["origin"] = a.point
|
||||
}
|
||||
|
||||
ranges := make([]interface{}, 0)
|
||||
for _, ent := range a.ranges {
|
||||
r := make(map[string]interface{})
|
||||
if ent.Key != "" {
|
||||
r["key"] = ent.Key
|
||||
}
|
||||
if ent.From != nil {
|
||||
switch from := ent.From.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["from"] = from
|
||||
case *int, *int16, *int32, *int64, *float32, *float64:
|
||||
r["from"] = from
|
||||
case string:
|
||||
r["from"] = from
|
||||
}
|
||||
}
|
||||
if ent.To != nil {
|
||||
switch to := ent.To.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["to"] = to
|
||||
case *int, *int16, *int32, *int64, *float32, *float64:
|
||||
r["to"] = to
|
||||
case string:
|
||||
r["to"] = to
|
||||
}
|
||||
}
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
opts["ranges"] = ranges
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package elastic
|
||||
|
||||
type GeoHashGridAggregation struct {
|
||||
field string
|
||||
precision int
|
||||
size int
|
||||
shardSize int
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewGeoHashGridAggregation() *GeoHashGridAggregation {
|
||||
return &GeoHashGridAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
precision: -1,
|
||||
size: -1,
|
||||
shardSize: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) Field(field string) *GeoHashGridAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) Precision(precision int) *GeoHashGridAggregation {
|
||||
a.precision = precision
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) Size(size int) *GeoHashGridAggregation {
|
||||
a.size = size
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) ShardSize(shardSize int) *GeoHashGridAggregation {
|
||||
a.shardSize = shardSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) SubAggregation(name string, subAggregation Aggregation) *GeoHashGridAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) Meta(metaData map[string]interface{}) *GeoHashGridAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GeoHashGridAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs": {
|
||||
// "new_york": {
|
||||
// "geohash_grid": {
|
||||
// "field": "location",
|
||||
// "precision": 5
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["geohash_grid"] = opts
|
||||
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
|
||||
if a.precision != -1 {
|
||||
opts["precision"] = a.precision
|
||||
}
|
||||
|
||||
if a.size != -1 {
|
||||
opts["size"] = a.size
|
||||
}
|
||||
|
||||
if a.shardSize != -1 {
|
||||
opts["shard_size"] = a.shardSize
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// GlobalAggregation defines a single bucket of all the documents within
|
||||
// the search execution context. This context is defined by the indices
|
||||
// and the document types you’re searching on, but is not influenced
|
||||
// by the search query itself.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-global-aggregation.html
|
||||
type GlobalAggregation struct {
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewGlobalAggregation() *GlobalAggregation {
|
||||
return &GlobalAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *GlobalAggregation) SubAggregation(name string, subAggregation Aggregation) *GlobalAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *GlobalAggregation) Meta(metaData map[string]interface{}) *GlobalAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *GlobalAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "all_products" : {
|
||||
// "global" : {},
|
||||
// "aggs" : {
|
||||
// "avg_price" : { "avg" : { "field" : "price" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "global" : {} } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["global"] = opts
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// HistogramAggregation is a multi-bucket values source based aggregation
|
||||
// that can be applied on numeric values extracted from the documents.
|
||||
// It dynamically builds fixed size (a.k.a. interval) buckets over the
|
||||
// values.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-histogram-aggregation.html
|
||||
type HistogramAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
missing interface{}
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
|
||||
interval int64
|
||||
order string
|
||||
orderAsc bool
|
||||
minDocCount *int64
|
||||
extendedBoundsMin *int64
|
||||
extendedBoundsMax *int64
|
||||
offset *int64
|
||||
}
|
||||
|
||||
func NewHistogramAggregation() *HistogramAggregation {
|
||||
return &HistogramAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) Field(field string) *HistogramAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) Script(script *Script) *HistogramAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing configures the value to use when documents miss a value.
|
||||
func (a *HistogramAggregation) Missing(missing interface{}) *HistogramAggregation {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) SubAggregation(name string, subAggregation Aggregation) *HistogramAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *HistogramAggregation) Meta(metaData map[string]interface{}) *HistogramAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) Interval(interval int64) *HistogramAggregation {
|
||||
a.interval = interval
|
||||
return a
|
||||
}
|
||||
|
||||
// Order specifies the sort order. Valid values for order are:
|
||||
// "_key", "_count", a sub-aggregation name, or a sub-aggregation name
|
||||
// with a metric.
|
||||
func (a *HistogramAggregation) Order(order string, asc bool) *HistogramAggregation {
|
||||
a.order = order
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByCount(asc bool) *HistogramAggregation {
|
||||
// "order" : { "_count" : "asc" }
|
||||
a.order = "_count"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByCountAsc() *HistogramAggregation {
|
||||
return a.OrderByCount(true)
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByCountDesc() *HistogramAggregation {
|
||||
return a.OrderByCount(false)
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByKey(asc bool) *HistogramAggregation {
|
||||
// "order" : { "_key" : "asc" }
|
||||
a.order = "_key"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByKeyAsc() *HistogramAggregation {
|
||||
return a.OrderByKey(true)
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) OrderByKeyDesc() *HistogramAggregation {
|
||||
return a.OrderByKey(false)
|
||||
}
|
||||
|
||||
// OrderByAggregation creates a bucket ordering strategy which sorts buckets
|
||||
// based on a single-valued calc get.
|
||||
func (a *HistogramAggregation) OrderByAggregation(aggName string, asc bool) *HistogramAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "avg_height" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "avg_height" : { "avg" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
// OrderByAggregationAndMetric creates a bucket ordering strategy which
|
||||
// sorts buckets based on a multi-valued calc get.
|
||||
func (a *HistogramAggregation) OrderByAggregationAndMetric(aggName, metric string, asc bool) *HistogramAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "height_stats.avg" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "height_stats" : { "stats" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName + "." + metric
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) MinDocCount(minDocCount int64) *HistogramAggregation {
|
||||
a.minDocCount = &minDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) ExtendedBounds(min, max int64) *HistogramAggregation {
|
||||
a.extendedBoundsMin = &min
|
||||
a.extendedBoundsMax = &max
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) ExtendedBoundsMin(min int64) *HistogramAggregation {
|
||||
a.extendedBoundsMin = &min
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) ExtendedBoundsMax(max int64) *HistogramAggregation {
|
||||
a.extendedBoundsMax = &max
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) Offset(offset int64) *HistogramAggregation {
|
||||
a.offset = &offset
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *HistogramAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "prices" : {
|
||||
// "histogram" : {
|
||||
// "field" : "price",
|
||||
// "interval" : 50
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "histogram" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["histogram"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
if a.missing != nil {
|
||||
opts["missing"] = a.missing
|
||||
}
|
||||
|
||||
opts["interval"] = a.interval
|
||||
if a.order != "" {
|
||||
o := make(map[string]interface{})
|
||||
if a.orderAsc {
|
||||
o[a.order] = "asc"
|
||||
} else {
|
||||
o[a.order] = "desc"
|
||||
}
|
||||
opts["order"] = o
|
||||
}
|
||||
if a.offset != nil {
|
||||
opts["offset"] = *a.offset
|
||||
}
|
||||
if a.minDocCount != nil {
|
||||
opts["min_doc_count"] = *a.minDocCount
|
||||
}
|
||||
if a.extendedBoundsMin != nil || a.extendedBoundsMax != nil {
|
||||
bounds := make(map[string]interface{})
|
||||
if a.extendedBoundsMin != nil {
|
||||
bounds["min"] = a.extendedBoundsMin
|
||||
}
|
||||
if a.extendedBoundsMax != nil {
|
||||
bounds["max"] = a.extendedBoundsMax
|
||||
}
|
||||
opts["extended_bounds"] = bounds
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// MissingAggregation is a field data based single bucket aggregation,
|
||||
// that creates a bucket of all documents in the current document set context
|
||||
// that are missing a field value (effectively, missing a field or having
|
||||
// the configured NULL value set). This aggregator will often be used in
|
||||
// conjunction with other field data bucket aggregators (such as ranges)
|
||||
// to return information for all the documents that could not be placed
|
||||
// in any of the other buckets due to missing field data values.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-missing-aggregation.html
|
||||
type MissingAggregation struct {
|
||||
field string
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewMissingAggregation() *MissingAggregation {
|
||||
return &MissingAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *MissingAggregation) Field(field string) *MissingAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *MissingAggregation) SubAggregation(name string, subAggregation Aggregation) *MissingAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *MissingAggregation) Meta(metaData map[string]interface{}) *MissingAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *MissingAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "products_without_a_price" : {
|
||||
// "missing" : { "field" : "price" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "missing" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["missing"] = opts
|
||||
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// NestedAggregation is a special single bucket aggregation that enables
|
||||
// aggregating nested documents.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/search-aggregations-bucket-nested-aggregation.html
|
||||
type NestedAggregation struct {
|
||||
path string
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewNestedAggregation() *NestedAggregation {
|
||||
return &NestedAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *NestedAggregation) SubAggregation(name string, subAggregation Aggregation) *NestedAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *NestedAggregation) Meta(metaData map[string]interface{}) *NestedAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *NestedAggregation) Path(path string) *NestedAggregation {
|
||||
a.path = path
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *NestedAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "query" : {
|
||||
// "match" : { "name" : "led tv" }
|
||||
// }
|
||||
// "aggs" : {
|
||||
// "resellers" : {
|
||||
// "nested" : {
|
||||
// "path" : "resellers"
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "min_price" : { "min" : { "field" : "resellers.price" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "nested" : {} } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["nested"] = opts
|
||||
|
||||
opts["path"] = a.path
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// RangeAggregation is a multi-bucket value source based aggregation that
|
||||
// enables the user to define a set of ranges - each representing a bucket.
|
||||
// During the aggregation process, the values extracted from each document
|
||||
// will be checked against each bucket range and "bucket" the
|
||||
// relevant/matching document. Note that this aggregration includes the
|
||||
// from value and excludes the to value for each range.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-range-aggregation.html
|
||||
type RangeAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
missing interface{}
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
keyed *bool
|
||||
unmapped *bool
|
||||
entries []rangeAggregationEntry
|
||||
}
|
||||
|
||||
type rangeAggregationEntry struct {
|
||||
Key string
|
||||
From interface{}
|
||||
To interface{}
|
||||
}
|
||||
|
||||
func NewRangeAggregation() *RangeAggregation {
|
||||
return &RangeAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
entries: make([]rangeAggregationEntry, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Field(field string) *RangeAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Script(script *Script) *RangeAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing configures the value to use when documents miss a value.
|
||||
func (a *RangeAggregation) Missing(missing interface{}) *RangeAggregation {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) SubAggregation(name string, subAggregation Aggregation) *RangeAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *RangeAggregation) Meta(metaData map[string]interface{}) *RangeAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Keyed(keyed bool) *RangeAggregation {
|
||||
a.keyed = &keyed
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Unmapped(unmapped bool) *RangeAggregation {
|
||||
a.unmapped = &unmapped
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddRange(from, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddRangeWithKey(key string, from, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddUnboundedTo(from interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddUnboundedToWithKey(key string, from interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddUnboundedFrom(to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) AddUnboundedFromWithKey(key string, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Lt(to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) LtWithKey(key string, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: nil, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Between(from, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) BetweenWithKey(key string, from, to interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: from, To: to})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Gt(from interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) GtWithKey(key string, from interface{}) *RangeAggregation {
|
||||
a.entries = append(a.entries, rangeAggregationEntry{Key: key, From: from, To: nil})
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *RangeAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "price_ranges" : {
|
||||
// "range" : {
|
||||
// "field" : "price",
|
||||
// "ranges" : [
|
||||
// { "to" : 50 },
|
||||
// { "from" : 50, "to" : 100 },
|
||||
// { "from" : 100 }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "range" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["range"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
if a.missing != nil {
|
||||
opts["missing"] = a.missing
|
||||
}
|
||||
|
||||
if a.keyed != nil {
|
||||
opts["keyed"] = *a.keyed
|
||||
}
|
||||
if a.unmapped != nil {
|
||||
opts["unmapped"] = *a.unmapped
|
||||
}
|
||||
|
||||
ranges := make([]interface{}, 0)
|
||||
for _, ent := range a.entries {
|
||||
r := make(map[string]interface{})
|
||||
if ent.Key != "" {
|
||||
r["key"] = ent.Key
|
||||
}
|
||||
if ent.From != nil {
|
||||
switch from := ent.From.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["from"] = from
|
||||
case time.Time:
|
||||
r["from"] = from.Format(time.RFC3339)
|
||||
case string:
|
||||
r["from"] = from
|
||||
}
|
||||
}
|
||||
if ent.To != nil {
|
||||
switch to := ent.To.(type) {
|
||||
case int, int16, int32, int64, float32, float64:
|
||||
r["to"] = to
|
||||
case time.Time:
|
||||
r["to"] = to.Format(time.RFC3339)
|
||||
case string:
|
||||
r["to"] = to
|
||||
}
|
||||
}
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
opts["ranges"] = ranges
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// ReverseNestedAggregation defines a special single bucket aggregation
|
||||
// that enables aggregating on parent docs from nested documents.
|
||||
// Effectively this aggregation can break out of the nested block
|
||||
// structure and link to other nested structures or the root document,
|
||||
// which allows nesting other aggregations that aren’t part of
|
||||
// the nested object in a nested aggregation.
|
||||
//
|
||||
// See: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-reverse-nested-aggregation.html
|
||||
type ReverseNestedAggregation struct {
|
||||
path string
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
// NewReverseNestedAggregation initializes a new ReverseNestedAggregation
|
||||
// bucket aggregation.
|
||||
func NewReverseNestedAggregation() *ReverseNestedAggregation {
|
||||
return &ReverseNestedAggregation{
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
// Path set the path to use for this nested aggregation. The path must match
|
||||
// the path to a nested object in the mappings. If it is not specified
|
||||
// then this aggregation will go back to the root document.
|
||||
func (a *ReverseNestedAggregation) Path(path string) *ReverseNestedAggregation {
|
||||
a.path = path
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ReverseNestedAggregation) SubAggregation(name string, subAggregation Aggregation) *ReverseNestedAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *ReverseNestedAggregation) Meta(metaData map[string]interface{}) *ReverseNestedAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ReverseNestedAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "reverse_nested" : {
|
||||
// "path": "..."
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "reverse_nested" : {} } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["reverse_nested"] = opts
|
||||
|
||||
if a.path != "" {
|
||||
opts["path"] = a.path
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// Copyright 2012-2016 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// SamplerAggregation is a filtering aggregation used to limit any
|
||||
// sub aggregations' processing to a sample of the top-scoring documents.
|
||||
// Optionally, diversity settings can be used to limit the number of matches
|
||||
// that share a common value such as an "author".
|
||||
// See: https://www.elastic.co/guide/en/elasticsearch/reference/2.x/search-aggregations-bucket-sampler-aggregation.html
|
||||
type SamplerAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
missing interface{}
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
|
||||
shardSize int
|
||||
maxDocsPerValue int
|
||||
executionHint string
|
||||
}
|
||||
|
||||
func NewSamplerAggregation() *SamplerAggregation {
|
||||
return &SamplerAggregation{
|
||||
shardSize: -1,
|
||||
maxDocsPerValue: -1,
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) Field(field string) *SamplerAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) Script(script *Script) *SamplerAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing configures the value to use when documents miss a value.
|
||||
func (a *SamplerAggregation) Missing(missing interface{}) *SamplerAggregation {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) SubAggregation(name string, subAggregation Aggregation) *SamplerAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *SamplerAggregation) Meta(metaData map[string]interface{}) *SamplerAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
// ShardSize sets the maximum number of docs returned from each shard.
|
||||
func (a *SamplerAggregation) ShardSize(shardSize int) *SamplerAggregation {
|
||||
a.shardSize = shardSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) MaxDocsPerValue(maxDocsPerValue int) *SamplerAggregation {
|
||||
a.maxDocsPerValue = maxDocsPerValue
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) ExecutionHint(hint string) *SamplerAggregation {
|
||||
a.executionHint = hint
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SamplerAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "sample" : {
|
||||
// "sampler" : {
|
||||
// "field" : "user.id",
|
||||
// "shard_size" : 200
|
||||
// },
|
||||
// "aggs": {
|
||||
// "keywords": {
|
||||
// "significant_terms": {
|
||||
// "field": "text"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "sampler" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["sampler"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
if a.missing != nil {
|
||||
opts["missing"] = a.missing
|
||||
}
|
||||
|
||||
if a.shardSize >= 0 {
|
||||
opts["shard_size"] = a.shardSize
|
||||
}
|
||||
if a.maxDocsPerValue >= 0 {
|
||||
opts["max_docs_per_value"] = a.maxDocsPerValue
|
||||
}
|
||||
if a.executionHint != "" {
|
||||
opts["execution_hint"] = a.executionHint
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// SignificantSignificantTermsAggregation is an aggregation that returns interesting
|
||||
// or unusual occurrences of terms in a set.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html
|
||||
type SignificantTermsAggregation struct {
|
||||
field string
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
|
||||
minDocCount *int
|
||||
shardMinDocCount *int
|
||||
requiredSize *int
|
||||
shardSize *int
|
||||
filter Query
|
||||
executionHint string
|
||||
significanceHeuristic SignificanceHeuristic
|
||||
}
|
||||
|
||||
func NewSignificantTermsAggregation() *SignificantTermsAggregation {
|
||||
return &SignificantTermsAggregation{
|
||||
subAggregations: make(map[string]Aggregation, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) Field(field string) *SignificantTermsAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) SubAggregation(name string, subAggregation Aggregation) *SignificantTermsAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *SignificantTermsAggregation) Meta(metaData map[string]interface{}) *SignificantTermsAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) MinDocCount(minDocCount int) *SignificantTermsAggregation {
|
||||
a.minDocCount = &minDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) ShardMinDocCount(shardMinDocCount int) *SignificantTermsAggregation {
|
||||
a.shardMinDocCount = &shardMinDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) RequiredSize(requiredSize int) *SignificantTermsAggregation {
|
||||
a.requiredSize = &requiredSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) ShardSize(shardSize int) *SignificantTermsAggregation {
|
||||
a.shardSize = &shardSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) BackgroundFilter(filter Query) *SignificantTermsAggregation {
|
||||
a.filter = filter
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) ExecutionHint(hint string) *SignificantTermsAggregation {
|
||||
a.executionHint = hint
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) SignificanceHeuristic(heuristic SignificanceHeuristic) *SignificantTermsAggregation {
|
||||
a.significanceHeuristic = heuristic
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SignificantTermsAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "query" : {
|
||||
// "terms" : {"force" : [ "British Transport Police" ]}
|
||||
// },
|
||||
// "aggregations" : {
|
||||
// "significantCrimeTypes" : {
|
||||
// "significant_terms" : { "field" : "crime_type" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the
|
||||
// { "significant_terms" : { "field" : "crime_type" }
|
||||
// part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["significant_terms"] = opts
|
||||
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.requiredSize != nil {
|
||||
opts["size"] = *a.requiredSize // not a typo!
|
||||
}
|
||||
if a.shardSize != nil {
|
||||
opts["shard_size"] = *a.shardSize
|
||||
}
|
||||
if a.minDocCount != nil {
|
||||
opts["min_doc_count"] = *a.minDocCount
|
||||
}
|
||||
if a.shardMinDocCount != nil {
|
||||
opts["shard_min_doc_count"] = *a.shardMinDocCount
|
||||
}
|
||||
if a.executionHint != "" {
|
||||
opts["execution_hint"] = a.executionHint
|
||||
}
|
||||
if a.filter != nil {
|
||||
src, err := a.filter.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["background_filter"] = src
|
||||
}
|
||||
if a.significanceHeuristic != nil {
|
||||
name := a.significanceHeuristic.Name()
|
||||
src, err := a.significanceHeuristic.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts[name] = src
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Significance heuristics --
|
||||
|
||||
type SignificanceHeuristic interface {
|
||||
Name() string
|
||||
Source() (interface{}, error)
|
||||
}
|
||||
|
||||
// -- Chi Square --
|
||||
|
||||
// ChiSquareSignificanceHeuristic implements Chi square as described
|
||||
// in "Information Retrieval", Manning et al., Chapter 13.5.2.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_chi_square
|
||||
// for details.
|
||||
type ChiSquareSignificanceHeuristic struct {
|
||||
backgroundIsSuperset *bool
|
||||
includeNegatives *bool
|
||||
}
|
||||
|
||||
// NewChiSquareSignificanceHeuristic initializes a new ChiSquareSignificanceHeuristic.
|
||||
func NewChiSquareSignificanceHeuristic() *ChiSquareSignificanceHeuristic {
|
||||
return &ChiSquareSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *ChiSquareSignificanceHeuristic) Name() string {
|
||||
return "chi_square"
|
||||
}
|
||||
|
||||
// BackgroundIsSuperset indicates whether you defined a custom background
|
||||
// filter that represents a difference set of documents that you want to
|
||||
// compare to.
|
||||
func (sh *ChiSquareSignificanceHeuristic) BackgroundIsSuperset(backgroundIsSuperset bool) *ChiSquareSignificanceHeuristic {
|
||||
sh.backgroundIsSuperset = &backgroundIsSuperset
|
||||
return sh
|
||||
}
|
||||
|
||||
// IncludeNegatives indicates whether to filter out the terms that appear
|
||||
// much less in the subset than in the background without the subset.
|
||||
func (sh *ChiSquareSignificanceHeuristic) IncludeNegatives(includeNegatives bool) *ChiSquareSignificanceHeuristic {
|
||||
sh.includeNegatives = &includeNegatives
|
||||
return sh
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *ChiSquareSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if sh.backgroundIsSuperset != nil {
|
||||
source["background_is_superset"] = *sh.backgroundIsSuperset
|
||||
}
|
||||
if sh.includeNegatives != nil {
|
||||
source["include_negatives"] = *sh.includeNegatives
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- GND --
|
||||
|
||||
// GNDSignificanceHeuristic implements the "Google Normalized Distance"
|
||||
// as described in "The Google Similarity Distance", Cilibrasi and Vitanyi,
|
||||
// 2007.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_google_normalized_distance
|
||||
// for details.
|
||||
type GNDSignificanceHeuristic struct {
|
||||
backgroundIsSuperset *bool
|
||||
}
|
||||
|
||||
// NewGNDSignificanceHeuristic implements a new GNDSignificanceHeuristic.
|
||||
func NewGNDSignificanceHeuristic() *GNDSignificanceHeuristic {
|
||||
return &GNDSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *GNDSignificanceHeuristic) Name() string {
|
||||
return "gnd"
|
||||
}
|
||||
|
||||
// BackgroundIsSuperset indicates whether you defined a custom background
|
||||
// filter that represents a difference set of documents that you want to
|
||||
// compare to.
|
||||
func (sh *GNDSignificanceHeuristic) BackgroundIsSuperset(backgroundIsSuperset bool) *GNDSignificanceHeuristic {
|
||||
sh.backgroundIsSuperset = &backgroundIsSuperset
|
||||
return sh
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *GNDSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if sh.backgroundIsSuperset != nil {
|
||||
source["background_is_superset"] = *sh.backgroundIsSuperset
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- JLH Score --
|
||||
|
||||
// JLHScoreSignificanceHeuristic implements the JLH score as described in
|
||||
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_jlh_score.
|
||||
type JLHScoreSignificanceHeuristic struct{}
|
||||
|
||||
// NewJLHScoreSignificanceHeuristic initializes a new JLHScoreSignificanceHeuristic.
|
||||
func NewJLHScoreSignificanceHeuristic() *JLHScoreSignificanceHeuristic {
|
||||
return &JLHScoreSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *JLHScoreSignificanceHeuristic) Name() string {
|
||||
return "jlh"
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *JLHScoreSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Mutual Information --
|
||||
|
||||
// MutualInformationSignificanceHeuristic implements Mutual information
|
||||
// as described in "Information Retrieval", Manning et al., Chapter 13.5.1.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_mutual_information
|
||||
// for details.
|
||||
type MutualInformationSignificanceHeuristic struct {
|
||||
backgroundIsSuperset *bool
|
||||
includeNegatives *bool
|
||||
}
|
||||
|
||||
// NewMutualInformationSignificanceHeuristic initializes a new instance of
|
||||
// MutualInformationSignificanceHeuristic.
|
||||
func NewMutualInformationSignificanceHeuristic() *MutualInformationSignificanceHeuristic {
|
||||
return &MutualInformationSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *MutualInformationSignificanceHeuristic) Name() string {
|
||||
return "mutual_information"
|
||||
}
|
||||
|
||||
// BackgroundIsSuperset indicates whether you defined a custom background
|
||||
// filter that represents a difference set of documents that you want to
|
||||
// compare to.
|
||||
func (sh *MutualInformationSignificanceHeuristic) BackgroundIsSuperset(backgroundIsSuperset bool) *MutualInformationSignificanceHeuristic {
|
||||
sh.backgroundIsSuperset = &backgroundIsSuperset
|
||||
return sh
|
||||
}
|
||||
|
||||
// IncludeNegatives indicates whether to filter out the terms that appear
|
||||
// much less in the subset than in the background without the subset.
|
||||
func (sh *MutualInformationSignificanceHeuristic) IncludeNegatives(includeNegatives bool) *MutualInformationSignificanceHeuristic {
|
||||
sh.includeNegatives = &includeNegatives
|
||||
return sh
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *MutualInformationSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if sh.backgroundIsSuperset != nil {
|
||||
source["background_is_superset"] = *sh.backgroundIsSuperset
|
||||
}
|
||||
if sh.includeNegatives != nil {
|
||||
source["include_negatives"] = *sh.includeNegatives
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Percentage Score --
|
||||
|
||||
// PercentageScoreSignificanceHeuristic implements the algorithm described
|
||||
// in https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_percentage.
|
||||
type PercentageScoreSignificanceHeuristic struct{}
|
||||
|
||||
// NewPercentageScoreSignificanceHeuristic initializes a new instance of
|
||||
// PercentageScoreSignificanceHeuristic.
|
||||
func NewPercentageScoreSignificanceHeuristic() *PercentageScoreSignificanceHeuristic {
|
||||
return &PercentageScoreSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *PercentageScoreSignificanceHeuristic) Name() string {
|
||||
return "percentage"
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *PercentageScoreSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Script --
|
||||
|
||||
// ScriptSignificanceHeuristic implements a scripted significance heuristic.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_scripted
|
||||
// for details.
|
||||
type ScriptSignificanceHeuristic struct {
|
||||
script *Script
|
||||
}
|
||||
|
||||
// NewScriptSignificanceHeuristic initializes a new instance of
|
||||
// ScriptSignificanceHeuristic.
|
||||
func NewScriptSignificanceHeuristic() *ScriptSignificanceHeuristic {
|
||||
return &ScriptSignificanceHeuristic{}
|
||||
}
|
||||
|
||||
// Name returns the name of the heuristic in the REST interface.
|
||||
func (sh *ScriptSignificanceHeuristic) Name() string {
|
||||
return "script_heuristic"
|
||||
}
|
||||
|
||||
// Script specifies the script to use to get custom scores. The following
|
||||
// parameters are available in the script: `_subset_freq`, `_superset_freq`,
|
||||
// `_subset_size`, and `_superset_size`.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html#_scripted
|
||||
// for details.
|
||||
func (sh *ScriptSignificanceHeuristic) Script(script *Script) *ScriptSignificanceHeuristic {
|
||||
sh.script = script
|
||||
return sh
|
||||
}
|
||||
|
||||
// Source returns the parameters that need to be added to the REST parameters.
|
||||
func (sh *ScriptSignificanceHeuristic) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
if sh.script != nil {
|
||||
src, err := sh.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source["script"] = src
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
// Copyright 2012-2015 Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// TermsAggregation is a multi-bucket value source based aggregation
|
||||
// where buckets are dynamically built - one per unique value.
|
||||
// See: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html
|
||||
type TermsAggregation struct {
|
||||
field string
|
||||
script *Script
|
||||
missing interface{}
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
|
||||
size *int
|
||||
shardSize *int
|
||||
requiredSize *int
|
||||
minDocCount *int
|
||||
shardMinDocCount *int
|
||||
valueType string
|
||||
order string
|
||||
orderAsc bool
|
||||
includePattern string
|
||||
includeFlags *int
|
||||
excludePattern string
|
||||
excludeFlags *int
|
||||
executionHint string
|
||||
collectionMode string
|
||||
showTermDocCountError *bool
|
||||
includeTerms []string
|
||||
excludeTerms []string
|
||||
}
|
||||
|
||||
func NewTermsAggregation() *TermsAggregation {
|
||||
return &TermsAggregation{
|
||||
subAggregations: make(map[string]Aggregation, 0),
|
||||
includeTerms: make([]string, 0),
|
||||
excludeTerms: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Field(field string) *TermsAggregation {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Script(script *Script) *TermsAggregation {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing configures the value to use when documents miss a value.
|
||||
func (a *TermsAggregation) Missing(missing interface{}) *TermsAggregation {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) SubAggregation(name string, subAggregation Aggregation) *TermsAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *TermsAggregation) Meta(metaData map[string]interface{}) *TermsAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Size(size int) *TermsAggregation {
|
||||
a.size = &size
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) RequiredSize(requiredSize int) *TermsAggregation {
|
||||
a.requiredSize = &requiredSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ShardSize(shardSize int) *TermsAggregation {
|
||||
a.shardSize = &shardSize
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) MinDocCount(minDocCount int) *TermsAggregation {
|
||||
a.minDocCount = &minDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ShardMinDocCount(shardMinDocCount int) *TermsAggregation {
|
||||
a.shardMinDocCount = &shardMinDocCount
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Include(regexp string) *TermsAggregation {
|
||||
a.includePattern = regexp
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) IncludeWithFlags(regexp string, flags int) *TermsAggregation {
|
||||
a.includePattern = regexp
|
||||
a.includeFlags = &flags
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Exclude(regexp string) *TermsAggregation {
|
||||
a.excludePattern = regexp
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ExcludeWithFlags(regexp string, flags int) *TermsAggregation {
|
||||
a.excludePattern = regexp
|
||||
a.excludeFlags = &flags
|
||||
return a
|
||||
}
|
||||
|
||||
// ValueType can be string, long, or double.
|
||||
func (a *TermsAggregation) ValueType(valueType string) *TermsAggregation {
|
||||
a.valueType = valueType
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Order(order string, asc bool) *TermsAggregation {
|
||||
a.order = order
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByCount(asc bool) *TermsAggregation {
|
||||
// "order" : { "_count" : "asc" }
|
||||
a.order = "_count"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByCountAsc() *TermsAggregation {
|
||||
return a.OrderByCount(true)
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByCountDesc() *TermsAggregation {
|
||||
return a.OrderByCount(false)
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByTerm(asc bool) *TermsAggregation {
|
||||
// "order" : { "_term" : "asc" }
|
||||
a.order = "_term"
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByTermAsc() *TermsAggregation {
|
||||
return a.OrderByTerm(true)
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) OrderByTermDesc() *TermsAggregation {
|
||||
return a.OrderByTerm(false)
|
||||
}
|
||||
|
||||
// OrderByAggregation creates a bucket ordering strategy which sorts buckets
|
||||
// based on a single-valued calc get.
|
||||
func (a *TermsAggregation) OrderByAggregation(aggName string, asc bool) *TermsAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "avg_height" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "avg_height" : { "avg" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
// OrderByAggregationAndMetric creates a bucket ordering strategy which
|
||||
// sorts buckets based on a multi-valued calc get.
|
||||
func (a *TermsAggregation) OrderByAggregationAndMetric(aggName, metric string, asc bool) *TermsAggregation {
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : {
|
||||
// "field" : "gender",
|
||||
// "order" : { "height_stats.avg" : "desc" }
|
||||
// },
|
||||
// "aggs" : {
|
||||
// "height_stats" : { "stats" : { "field" : "height" } }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
a.order = aggName + "." + metric
|
||||
a.orderAsc = asc
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ExecutionHint(hint string) *TermsAggregation {
|
||||
a.executionHint = hint
|
||||
return a
|
||||
}
|
||||
|
||||
// Collection mode can be depth_first or breadth_first as of 1.4.0.
|
||||
func (a *TermsAggregation) CollectionMode(collectionMode string) *TermsAggregation {
|
||||
a.collectionMode = collectionMode
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ShowTermDocCountError(showTermDocCountError bool) *TermsAggregation {
|
||||
a.showTermDocCountError = &showTermDocCountError
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) IncludeTerms(terms ...string) *TermsAggregation {
|
||||
a.includeTerms = append(a.includeTerms, terms...)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) ExcludeTerms(terms ...string) *TermsAggregation {
|
||||
a.excludeTerms = append(a.excludeTerms, terms...)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *TermsAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "genders" : {
|
||||
// "terms" : { "field" : "gender" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// This method returns only the { "terms" : { "field" : "gender" } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["terms"] = opts
|
||||
|
||||
// ValuesSourceAggregationBuilder
|
||||
if a.field != "" {
|
||||
opts["field"] = a.field
|
||||
}
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts["script"] = src
|
||||
}
|
||||
if a.missing != nil {
|
||||
opts["missing"] = a.missing
|
||||
}
|
||||
|
||||
// TermsBuilder
|
||||
if a.size != nil && *a.size >= 0 {
|
||||
opts["size"] = *a.size
|
||||
}
|
||||
if a.shardSize != nil && *a.shardSize >= 0 {
|
||||
opts["shard_size"] = *a.shardSize
|
||||
}
|
||||
if a.requiredSize != nil && *a.requiredSize >= 0 {
|
||||
opts["required_size"] = *a.requiredSize
|
||||
}
|
||||
if a.minDocCount != nil && *a.minDocCount >= 0 {
|
||||
opts["min_doc_count"] = *a.minDocCount
|
||||
}
|
||||
if a.shardMinDocCount != nil && *a.shardMinDocCount >= 0 {
|
||||
opts["shard_min_doc_count"] = *a.shardMinDocCount
|
||||
}
|
||||
if a.showTermDocCountError != nil {
|
||||
opts["show_term_doc_count_error"] = *a.showTermDocCountError
|
||||
}
|
||||
if a.collectionMode != "" {
|
||||
opts["collect_mode"] = a.collectionMode
|
||||
}
|
||||
if a.valueType != "" {
|
||||
opts["value_type"] = a.valueType
|
||||
}
|
||||
if a.order != "" {
|
||||
o := make(map[string]interface{})
|
||||
if a.orderAsc {
|
||||
o[a.order] = "asc"
|
||||
} else {
|
||||
o[a.order] = "desc"
|
||||
}
|
||||
opts["order"] = o
|
||||
}
|
||||
if len(a.includeTerms) > 0 {
|
||||
opts["include"] = a.includeTerms
|
||||
}
|
||||
if a.includePattern != "" {
|
||||
if a.includeFlags == nil || *a.includeFlags == 0 {
|
||||
opts["include"] = a.includePattern
|
||||
} else {
|
||||
p := make(map[string]interface{})
|
||||
p["pattern"] = a.includePattern
|
||||
p["flags"] = *a.includeFlags
|
||||
opts["include"] = p
|
||||
}
|
||||
}
|
||||
if len(a.excludeTerms) > 0 {
|
||||
opts["exclude"] = a.excludeTerms
|
||||
}
|
||||
if a.excludePattern != "" {
|
||||
if a.excludeFlags == nil || *a.excludeFlags == 0 {
|
||||
opts["exclude"] = a.excludePattern
|
||||
} else {
|
||||
p := make(map[string]interface{})
|
||||
p["pattern"] = a.excludePattern
|
||||
p["flags"] = *a.excludeFlags
|
||||
opts["exclude"] = p
|
||||
}
|
||||
}
|
||||
if a.executionHint != "" {
|
||||
opts["execution_hint"] = a.executionHint
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user