// +build cluster // This file is only built and run if you specify // -tags=cluster as part of the 'go test' invocation. // http://golang.org/pkg/go/build/#Build_Constraints package elasticsearch_test import ( "bytes" "encoding/json" "fmt" es "github.com/peterbourgon/elasticsearch" "io/ioutil" "net/http" "testing" "time" ) func init() { waitForCluster(15 * time.Second) } // Just tests es.Cluster internals; doesn't make any real connection. func TestClusterShutdown(t *testing.T) { endpoints := []string{"http://host1:9200", "http://host2:9200"} pingInterval, pingTimeout := 30*time.Second, 3*time.Second c := es.NewCluster(endpoints, pingInterval, pingTimeout) e := make(chan error) go func() { c.Shutdown() e <- nil }() go func() { <-time.After(1 * time.Second) e <- fmt.Errorf("timeout") }() if err := <-e; err != nil { t.Fatalf("%s", err) } } func TestClusterIndex(t *testing.T) { c := newCluster(t, []string{"twitter"}, nil) defer c.Shutdown() defer deleteIndices(t, []string{"twitter"}) response, err := c.Index(es.IndexRequest{ es.IndexParams{ Index: "twitter", Type: "tweet", Id: "1", Refresh: "true", }, map[string]interface{}{ "name": "John", }, }) if err != nil { t.Fatal(err) } if response.Error != "" { t.Error(response.Error) } if expected, got := 1, response.Version; expected != got { t.Errorf("expected version to be %d; got %d", expected, got) } } func TestClusterCreate(t *testing.T) { c := newCluster(t, []string{"twitter"}, nil) defer c.Shutdown() defer deleteIndices(t, []string{"twitter"}) response, err := c.Create(es.CreateRequest{ es.IndexParams{ Index: "twitter", Type: "tweet", Id: "1", Refresh: "true", }, map[string]interface{}{ "name": "John", }, }) if err != nil { t.Fatal(err) } if response.Error != "" { t.Error(response.Error) } if expected, got := 1, response.Version; expected != got { t.Errorf("expected version to be %d; got %d", expected, got) } } func TestClusterUpdate(t *testing.T) { c := newCluster(t, []string{"twitter"}, map[string]interface{}{ "/twitter/tweet/1": map[string]string{ "name": "John", }, }) defer c.Shutdown() defer deleteIndices(t, []string{"twitter"}) response, err := c.Update(es.UpdateRequest{ es.IndexParams{ Index: "twitter", Type: "tweet", Id: "1", Refresh: "true", }, map[string]interface{}{ "script": `ctx._source.text = "some text"`, }, }) if err != nil { t.Fatal(err) } if response.Error != "" { t.Error(response.Error) } if expected, got := 2, response.Version; expected != got { t.Errorf("expected version to be %d; got %d", expected, got) } } func TestClusterDelete(t *testing.T) { c := newCluster(t, []string{"twitter"}, map[string]interface{}{ "/twitter/tweet/1": map[string]string{ "name": "John", }, }) defer c.Shutdown() defer deleteIndices(t, []string{"twitter"}) response, err := c.Delete(es.DeleteRequest{ es.IndexParams{ Index: "twitter", Type: "tweet", Id: "1", Refresh: "true", }, }) if err != nil { t.Fatal(err) } if response.Error != "" { t.Error(response.Error) } if expected, got := 2, response.Version; expected != got { t.Errorf("expected version to be %d; got %d", expected, got) } } func TestClusterBulk(t *testing.T) { c := newCluster(t, []string{"twitter"}, map[string]interface{}{ "/twitter/tweet/1": map[string]string{ "name": "John", }, }) defer c.Shutdown() defer deleteIndices(t, []string{"twitter"}) response, err := c.Bulk(es.BulkRequest{ es.BulkParams{Refresh: "true"}, []es.BulkIndexable{ es.IndexRequest{ es.IndexParams{Index: "twitter", Type: "tweet", Id: "1"}, map[string]interface{}{"name": "James"}, }, es.DeleteRequest{ es.IndexParams{Index: "twitter", Type: "tweet", Id: "2"}, }, es.CreateRequest{ es.IndexParams{Index: "twitter", Type: "tweet", Id: "3"}, map[string]interface{}{"name": "John"}, }, }, }) if err != nil { t.Fatal(err) } if len(response.Items) != 3 { t.Fatalf("expected 3 responses, got %d", len(response.Items)) } if expected, got := 2, response.Items[0].Version; expected != got { t.Errorf("expected version of doc to be %d; got %d", expected, got) } if expected, got := false, response.Items[1].Found; expected != got { t.Errorf("expected delete op to return found = false") } if expected, got := 1, response.Items[2].Version; expected != got { t.Errorf("expected version of doc to be %d; got %d", expected, got) } } func TestSimpleTermQuery(t *testing.T) { indices := []string{"twitter"} c := newCluster(t, indices, map[string]interface{}{ "/twitter/tweet/1": map[string]string{ "user": "kimchy", "post_date": "2009-11-15T14:12:12", "message": "trying out Elastic Search", }, }) defer c.Shutdown() defer deleteIndices(t, indices) // comment out to leave data after test q := es.QueryWrapper(es.TermQuery(es.TermQueryParams{ Query: &es.Wrapper{ Name: "user", Wrapped: "kimchy", }, })) request := es.SearchRequest{ es.SearchParams{ Indices: []string{"twitter"}, Types: []string{"tweet"}, }, q, } response, err := c.Search(request) if err != nil { t.Error(err) } if response.Error != "" { t.Error(response.Error) } if expected, got := 1, response.HitsWrapper.Total; expected != got { t.Fatalf("expected %d, got %d", expected, got) } t.Logf("OK, %d hit(s), %dms", response.HitsWrapper.Total, response.Took) } func TestMultiSearch(t *testing.T) { indices := []string{"index1", "index2"} c := newCluster(t, indices, map[string]interface{}{ "/index1/foo/1": map[string]string{ "user": "alice", "description": "index=index1 type=foo id=1 user=alice", }, "/index2/bar/2": map[string]string{ "user": "bob", "description": "index=index2 type=bar id=2 user=bob", }, }) defer c.Shutdown() defer deleteIndices(t, indices) // comment out to leave data after test q1 := es.QueryWrapper(es.TermQuery(es.TermQueryParams{ Query: &es.Wrapper{ Name: "user", Wrapped: "alice", }, })) q2 := es.QueryWrapper(es.TermQuery(es.TermQueryParams{ Query: &es.Wrapper{ Name: "user", Wrapped: "bob", }, })) q3 := es.QueryWrapper(es.MatchAllQuery()) request := es.MultiSearchRequest{ Requests: []es.SearchRequest{ es.SearchRequest{ es.SearchParams{ Indices: []string{"index1"}, Types: []string{"foo"}, }, q1, }, es.SearchRequest{ es.SearchParams{ Indices: []string{"index2"}, Types: []string{"bar"}, }, q2, }, es.SearchRequest{ es.SearchParams{ Indices: []string{}, // "index1", "index2" is not supported (!) Types: []string{}, // "type1", "type2" is not supported (!) }, q3, }, }, } response, err := c.MultiSearch(request) if err != nil { t.Fatal(err) } if expected, got := 3, len(response.Responses); expected != got { t.Fatalf("expected %d response(s), got %d", expected, got) } r1 := response.Responses[0] if r1.Error != "" { t.Fatalf("response 1: %s", r1.Error) } if expected, got := 1, r1.HitsWrapper.Total; expected != got { t.Fatalf("response 1: expected %d hit(s), got %d", expected, got) } buf, _ := json.Marshal(r1) t.Logf("response 1 OK: %s", buf) r2 := response.Responses[1] if r2.Error != "" { t.Fatalf("response 2: %s", r1.Error) } if expected, got := 1, r2.HitsWrapper.Total; expected != got { t.Fatalf("response 2: expected %d hit(s), got %d", expected, got) } buf, _ = json.Marshal(r2) t.Logf("response 2 OK: %s", buf) r3 := response.Responses[2] if r3.Error != "" { t.Fatalf("response 3: %s", r1.Error) } if expected, got := 2, r3.HitsWrapper.Total; expected != got { t.Fatalf("response 3: expected %d hit(s), got %d", expected, got) } buf, _ = json.Marshal(r3) t.Logf("response 3 OK: %s", buf) } func TestConstantScoreNoScore(t *testing.T) { indices := []string{"twitter"} c := newCluster(t, indices, map[string]interface{}{ "/twitter/tweet/1": map[string]string{ "user": "kimchy", "post_date": "2009-11-15T14:12:12", "message": "trying out Elastic Search", }, }) defer c.Shutdown() defer deleteIndices(t, indices) // comment out to leave data after test q := map[string]interface{}{ "size": 20, "sort": []string{"post_date"}, "filter": map[string]interface{}{ "and": []map[string]interface{}{ map[string]interface{}{ "type": map[string]string{"value": "tweet"}, }, }, }, "query": map[string]interface{}{ "constant_score": map[string]interface{}{ "filter": map[string]interface{}{ "term": map[string]string{"user": "kimchy"}, }, }, }, } request := es.SearchRequest{ es.SearchParams{ Indices: []string{"twitter"}, Types: []string{"tweet"}, }, q, } response, err := c.Search(request) if err != nil { t.Fatalf("Search: %s", err) } buf, _ := json.Marshal(response) t.Logf("got response: %s", buf) if response.Error != "" { t.Error(response.Error) } if expected, got := 1, response.HitsWrapper.Total; expected != got { t.Fatalf("expected %d, got %d", expected, got) } if response.HitsWrapper.Hits[0].Score != nil { t.Fatalf("score: expected nil, got something") } t.Logf("OK, %d hit(s), %dms", response.HitsWrapper.Total, response.Took) } // // // func waitForCluster(timeout time.Duration) { giveUp := time.After(timeout) delay := 100 * time.Millisecond for { _, err := http.Get("http://127.0.0.1:9200") if err == nil { fmt.Printf("ElasticSearch now available\n") return // great } fmt.Printf("ElasticSearch not ready yet; waiting %s\n", delay) select { case <-time.After(delay): delay *= 2 case <-giveUp: panic("ElasticSearch didn't come up in time") } } } func newCluster(t *testing.T, indices []string, m map[string]interface{}) *es.Cluster { deleteIndices(t, indices) loadData(t, m) endpoints := []string{"http://localhost:9200"} pingInterval, pingTimeout := 10*time.Second, 3*time.Second return es.NewCluster(endpoints, pingInterval, pingTimeout) } func deleteIndices(t *testing.T, indices []string) { for _, index := range indices { // refresh=true to make document(s) immediately deleted url := "http://127.0.0.1:9200/" + index + "?refresh=true" req, err := http.NewRequest("DELETE", url, nil) if err != nil { t.Fatal(err) } resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } respBuf, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { t.Fatal(err) } t.Logf("DELETE %s: %s", index, respBuf) } } func loadData(t *testing.T, m map[string]interface{}) { for path, body := range m { reqBytes, err := json.Marshal(body) if err != nil { t.Fatal(err) } // refresh=true to make document(s) immediately searchable url := "http://127.0.0.1:9200" + path + "?refresh=true" req, err := http.NewRequest("PUT", url, bytes.NewBuffer(reqBytes)) if err != nil { t.Fatal(err) } resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } respBuf, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { t.Fatal(err) } t.Logf("PUT %s: %s", path, respBuf) } }