Skip to content

Commit

Permalink
Added missing features for source filtering
Browse files Browse the repository at this point in the history
One can use source filtering now in the Get API.
See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html#get-source-filtering
for details on source filtering.

Examples:

```go
res, err := client.Get().Index("test").Type("tweet").Id("1").FetchSource(false).Do()
// res.Source should be nil here
```

```go
fsc := NewFetchSourceContext(true).Exclude("message")
res, err := client.Get().Index("test").Type("tweet").Id("1").FetchSourceContext(fsc).Do()
// res.Source should not be nil, but missing the "message" field
```

A more flexible approach is to use the `FetchSourceContext` also used in
the Java API. See
https://github.com/elasticsearch/elasticsearch/blob/master/src/main/java/org/elasticsearch/search/fetch/source/FetchSourceContext.java.
It is available in Elastic as well in
https://github.com/olivere/elastic/blob/master/fetch_source_context.go.

This commit also adds missing fields `version` and `version_type` to the
Get and MGet APIs.

Also fixed the fact that [1.3.8,1.4.0) and 1.4.3+ disabled Groovy
scripting by default. A test in the update API will be skipped for these
versions.

Closes olivere#40
  • Loading branch information
olivere committed Feb 18, 2015
1 parent fe7d91a commit 258273e
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 20 deletions.
21 changes: 21 additions & 0 deletions fetch_source_context.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package elastic

import (
"net/url"
"strings"
)

type FetchSourceContext struct {
fetchSource bool
transformSource bool
Expand Down Expand Up @@ -47,3 +52,19 @@ func (fsc *FetchSourceContext) Source() interface{} {
"excludes": fsc.excludes,
}
}

// 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
}
30 changes: 30 additions & 0 deletions fetch_source_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,33 @@ func TestFetchSourceContextFetchSourceWithIncludesAndExcludes(t *testing.T) {
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
}
}

func TestFetchSourceContextQueryDefaults(t *testing.T) {
builder := NewFetchSourceContext(true)
values := builder.Query()
got := values.Encode()
expected := ""
if got != expected {
t.Errorf("expected %q; got: %q", expected, got)
}
}

func TestFetchSourceContextQueryNoFetchSource(t *testing.T) {
builder := NewFetchSourceContext(false)
values := builder.Query()
got := values.Encode()
expected := "_source=false"
if got != expected {
t.Errorf("expected %q; got: %q", expected, got)
}
}

func TestFetchSourceContextQueryFetchSourceWithIncludesAndExcludes(t *testing.T) {
builder := NewFetchSourceContext(true).Include("a", "b").Exclude("c")
values := builder.Query()
got := values.Encode()
expected := "_source_exclude=c&_source_include=a%2Cb"
if got != expected {
t.Errorf("expected %q; got: %q", expected, got)
}
}
65 changes: 54 additions & 11 deletions get.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ import (
)

type GetService struct {
client *Client
index string
_type string
id string
routing string
preference string
fields []string
refresh *bool
realtime *bool
client *Client
index string
_type string
id string
routing string
preference string
fields []string
fsc *FetchSourceContext
refresh *bool
realtime *bool
version *int64 // see org.elasticsearch.common.lucene.uid.Versions
versionType string // see org.elasticsearch.index.VersionType
ignoreErrOnGeneratedFields *bool
}

func NewGetService(client *Client) *GetService {
Expand Down Expand Up @@ -82,6 +86,20 @@ func (b *GetService) Fields(fields ...string) *GetService {
return b
}

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
}

func (b *GetService) Refresh(refresh bool) *GetService {
b.refresh = &refresh
return b
Expand All @@ -92,6 +110,22 @@ func (b *GetService) Realtime(realtime bool) *GetService {
return b
}

// Version can be MatchAny (-3), MatchAnyPre120 (0), NotFound (-1),
// or NotSet (-2). These are specified in org.elasticsearch.common.lucene.uid.Versions.
// The default is MatchAny (-3).
func (b *GetService) Version(version int64) *GetService {
b.version = &version
return b
}

// 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 (b *GetService) VersionType(versionType string) *GetService {
b.versionType = versionType
return b
}

func (b *GetService) Do() (*GetResult, error) {
// Build url
urls, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{
Expand Down Expand Up @@ -119,6 +153,17 @@ func (b *GetService) Do() (*GetResult, error) {
if b.refresh != nil {
params.Add("refresh", fmt.Sprintf("%v", *b.refresh))
}
if b.version != nil {
params.Add("_version", fmt.Sprintf("%d", *b.version))
}
if b.versionType != "" {
params.Add("_version_type", b.versionType)
}
if b.fsc != nil {
for k, values := range b.fsc.Query() {
params.Add(k, strings.Join(values, ","))
}
}
if len(params) > 0 {
urls += "?" + params.Encode()
}
Expand All @@ -129,8 +174,6 @@ func (b *GetService) Do() (*GetResult, error) {
return nil, err
}

// No body in get requests

// Get response
res, err := b.client.c.Do((*http.Request)(req))
if err != nil {
Expand Down
66 changes: 66 additions & 0 deletions get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package elastic

import (
"encoding/json"
"testing"
)

Expand Down Expand Up @@ -69,3 +70,68 @@ func TestGet(t *testing.T) {
t.Errorf("expected Source == nil; got %v", res.Source)
}
}

func TestGetWithSourceFiltering(t *testing.T) {
client := setupTestClientAndCreateIndex(t)

tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."}
tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."}
tweet3 := tweet{User: "sandrae", Message: "Cycling is fun."}

// Add all documents
_, err := client.Index().Index(testIndexName).Type("tweet").Id("1").BodyJson(&tweet1).Do()
if err != nil {
t.Fatal(err)
}

_, err = client.Index().Index(testIndexName).Type("tweet").Id("2").BodyJson(&tweet2).Do()
if err != nil {
t.Fatal(err)
}

_, err = client.Index().Index(testIndexName).Type("tweet").Id("3").BodyJson(&tweet3).Do()
if err != nil {
t.Fatal(err)
}

_, err = client.Flush().Index(testIndexName).Do()
if err != nil {
t.Fatal(err)
}

// Get document 1, without source
res, err := client.Get().Index(testIndexName).Type("tweet").Id("1").FetchSource(false).Do()
if err != nil {
t.Fatal(err)
}
if res.Found != true {
t.Errorf("expected Found = true; got %v", res.Found)
}
if res.Source != nil {
t.Errorf("expected Source == nil; got %v", res.Source)
}

// Get document 1, exclude Message field
fsc := NewFetchSourceContext(true).Exclude("message")
res, err = client.Get().Index(testIndexName).Type("tweet").Id("1").FetchSourceContext(fsc).Do()
if err != nil {
t.Fatal(err)
}
if res.Found != true {
t.Errorf("expected Found = true; got %v", res.Found)
}
if res.Source == nil {
t.Errorf("expected Source != nil; got %v", res.Source)
}
var tw tweet
err = json.Unmarshal(*res.Source, &tw)
if err != nil {
t.Fatal(err)
}
if tw.User != "olivere" {
t.Errorf("expected user %q; got: %q", "olivere", tw.User)
}
if tw.Message != "" {
t.Errorf("expected message %q; got: %q", "", tw.Message)
}
}
20 changes: 11 additions & 9 deletions multi_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,13 @@ type MultiGetItem struct {
id string
routing string
fields []string
version int64 // see org.elasticsearch.common.lucene.uid.Versions
version *int64 // see org.elasticsearch.common.lucene.uid.Versions
versionType string // see org.elasticsearch.index.VersionType
fsc *FetchSourceContext
}

func NewMultiGetItem() *MultiGetItem {
return &MultiGetItem{
version: -3, // MatchAny (see Version below)
}
return &MultiGetItem{}
}

func (item *MultiGetItem) Index(index string) *MultiGetItem {
Expand Down Expand Up @@ -152,7 +150,7 @@ func (item *MultiGetItem) Fields(fields ...string) *MultiGetItem {
// or NotSet (-2). These are specified in org.elasticsearch.common.lucene.uid.Versions.
// The default is MatchAny (-3).
func (item *MultiGetItem) Version(version int64) *MultiGetItem {
item.version = version
item.version = &version
return item
}

Expand All @@ -174,25 +172,29 @@ func (item *MultiGetItem) FetchSource(fetchSourceContext *FetchSourceContext) *M
func (item *MultiGetItem) Source() interface{} {
source := make(map[string]interface{})

source["_id"] = item.id

if item.index != "" {
source["_index"] = item.index
}
if item.typ != "" {
source["_type"] = item.typ
}
source["_id"] = item.id

if item.fsc != nil {
source["_source"] = item.fsc.Source()
}

if item.fields != nil {
source["_fields"] = item.fields
}

if item.routing != "" {
source["_routing"] = item.routing
}
if item.version != nil {
source["_version"] = *item.version
}
if item.versionType != "" {
source["_version_type"] = item.versionType
}

return source
}
Expand Down
10 changes: 10 additions & 0 deletions update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ func TestUpdateViaDocAndUpsert(t *testing.T) {
func TestUpdateViaScriptIntegration(t *testing.T) {
client := setupTestClientAndCreateIndex(t)

// Groovy is disabled for [1.3.8,1.4.0) and 1.4.3+, so skip tests in that case
esversion, err := client.ElasticsearchVersion(defaultUrl)
if err != nil {
t.Fatal(err)
}
if esversion >= "1.4.3" || (esversion < "1.4.0" && esversion >= "1.3.8") {
t.Skip("groovy scripting has been disabled as for [1.3.8,1.4.0) and 1.4.3+")
return
}

tweet1 := tweet{User: "olivere", Retweets: 10, Message: "Welcome to Golang and Elasticsearch."}

// Add a document
Expand Down

0 comments on commit 258273e

Please sign in to comment.