1// Copyright 2012-present Oliver Eilhard. All rights reserved. 2// Use of this source code is governed by a MIT-license. 3// See http://olivere.mit-license.org/license.txt for details. 4 5package elastic 6 7import ( 8 "context" 9 "fmt" 10 "log" 11 "math/rand" 12 "os" 13 "time" 14) 15 16const ( 17 testIndexName = "elastic-test" 18 testIndexName2 = "elastic-test2" 19 testMapping = ` 20{ 21 "settings":{ 22 "number_of_shards":1, 23 "number_of_replicas":0 24 }, 25 "mappings":{ 26 "_default_": { 27 "_all": { 28 "enabled": true 29 } 30 }, 31 "tweet":{ 32 "properties":{ 33 "user":{ 34 "type":"keyword" 35 }, 36 "message":{ 37 "type":"text", 38 "store": true, 39 "fielddata": true 40 }, 41 "tags":{ 42 "type":"keyword" 43 }, 44 "location":{ 45 "type":"geo_point" 46 }, 47 "suggest_field":{ 48 "type":"completion", 49 "contexts":[ 50 { 51 "name": "user_name", 52 "type": "category" 53 } 54 ] 55 } 56 } 57 }, 58 "comment":{ 59 "_parent": { 60 "type": "tweet" 61 } 62 }, 63 "order":{ 64 "properties":{ 65 "article":{ 66 "type":"text" 67 }, 68 "manufacturer":{ 69 "type":"keyword" 70 }, 71 "price":{ 72 "type":"float" 73 }, 74 "time":{ 75 "type":"date", 76 "format": "YYYY-MM-dd" 77 } 78 } 79 }, 80 "doctype":{ 81 "properties":{ 82 "message":{ 83 "type":"text", 84 "store": true, 85 "fielddata": true 86 } 87 } 88 }, 89 "queries":{ 90 "properties": { 91 "query": { 92 "type": "percolator" 93 } 94 } 95 }, 96 "tweet-nosource":{ 97 "_source": { 98 "enabled": false 99 }, 100 "properties":{ 101 "user":{ 102 "type":"keyword" 103 }, 104 "message":{ 105 "type":"text", 106 "store": true, 107 "fielddata": true 108 }, 109 "tags":{ 110 "type":"keyword" 111 }, 112 "location":{ 113 "type":"geo_point" 114 }, 115 "suggest_field":{ 116 "type":"completion", 117 "contexts":[ 118 { 119 "name":"user_name", 120 "type":"category" 121 } 122 ] 123 } 124 } 125 } 126 } 127} 128` 129) 130 131type tweet struct { 132 User string `json:"user"` 133 Message string `json:"message"` 134 Retweets int `json:"retweets"` 135 Image string `json:"image,omitempty"` 136 Created time.Time `json:"created,omitempty"` 137 Tags []string `json:"tags,omitempty"` 138 Location string `json:"location,omitempty"` 139 Suggest *SuggestField `json:"suggest_field,omitempty"` 140} 141 142func (t tweet) String() string { 143 return fmt.Sprintf("tweet{User:%q,Message:%q,Retweets:%d}", t.User, t.Message, t.Retweets) 144} 145 146type comment struct { 147 User string `json:"user"` 148 Comment string `json:"comment"` 149 Created time.Time `json:"created,omitempty"` 150} 151 152func (c comment) String() string { 153 return fmt.Sprintf("comment{User:%q,Comment:%q}", c.User, c.Comment) 154} 155 156type order struct { 157 Article string `json:"article"` 158 Manufacturer string `json:"manufacturer"` 159 Price float64 `json:"price"` 160 Time string `json:"time,omitempty"` 161} 162 163func (o order) String() string { 164 return fmt.Sprintf("order{Article:%q,Manufacturer:%q,Price:%v,Time:%v}", o.Article, o.Manufacturer, o.Price, o.Time) 165} 166 167// doctype is required for Percolate tests. 168type doctype struct { 169 Message string `json:"message"` 170} 171 172// queries is required for Percolate tests. 173type queries struct { 174 Query string `json:"query"` 175} 176 177func isTravis() bool { 178 return os.Getenv("TRAVIS") != "" 179} 180 181func travisGoVersion() string { 182 return os.Getenv("TRAVIS_GO_VERSION") 183} 184 185type logger interface { 186 Error(args ...interface{}) 187 Errorf(format string, args ...interface{}) 188 Fatal(args ...interface{}) 189 Fatalf(format string, args ...interface{}) 190 Fail() 191 FailNow() 192 Log(args ...interface{}) 193 Logf(format string, args ...interface{}) 194} 195 196func setupTestClient(t logger, options ...ClientOptionFunc) (client *Client) { 197 var err error 198 199 client, err = NewClient(options...) 200 if err != nil { 201 t.Fatal(err) 202 } 203 204 client.DeleteIndex(testIndexName).Do(context.TODO()) 205 client.DeleteIndex(testIndexName2).Do(context.TODO()) 206 207 return client 208} 209 210func setupTestClientAndCreateIndex(t logger, options ...ClientOptionFunc) *Client { 211 client := setupTestClient(t, options...) 212 213 // Create index 214 createIndex, err := client.CreateIndex(testIndexName).Body(testMapping).Do(context.TODO()) 215 if err != nil { 216 t.Fatal(err) 217 } 218 if createIndex == nil { 219 t.Errorf("expected result to be != nil; got: %v", createIndex) 220 } 221 222 // Create second index 223 createIndex2, err := client.CreateIndex(testIndexName2).Body(testMapping).Do(context.TODO()) 224 if err != nil { 225 t.Fatal(err) 226 } 227 if createIndex2 == nil { 228 t.Errorf("expected result to be != nil; got: %v", createIndex2) 229 } 230 231 return client 232} 233 234func setupTestClientAndCreateIndexAndLog(t logger, options ...ClientOptionFunc) *Client { 235 return setupTestClientAndCreateIndex(t, SetTraceLog(log.New(os.Stdout, "", 0))) 236} 237 238func setupTestClientAndCreateIndexAndAddDocs(t logger, options ...ClientOptionFunc) *Client { 239 client := setupTestClientAndCreateIndex(t, options...) 240 241 // Add tweets 242 tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."} 243 tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."} 244 tweet3 := tweet{User: "sandrae", Message: "Cycling is fun."} 245 comment1 := comment{User: "nico", Comment: "You bet."} 246 247 _, err := client.Index().Index(testIndexName).Type("tweet").Id("1").BodyJson(&tweet1).Do(context.TODO()) 248 if err != nil { 249 t.Fatal(err) 250 } 251 _, err = client.Index().Index(testIndexName).Type("tweet").Id("2").BodyJson(&tweet2).Do(context.TODO()) 252 if err != nil { 253 t.Fatal(err) 254 } 255 _, err = client.Index().Index(testIndexName).Type("tweet").Id("3").Routing("someroutingkey").BodyJson(&tweet3).Do(context.TODO()) 256 if err != nil { 257 t.Fatal(err) 258 } 259 _, err = client.Index().Index(testIndexName).Type("comment").Id("1").Parent("3").BodyJson(&comment1).Do(context.TODO()) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 // Add orders 265 var orders []order 266 orders = append(orders, order{Article: "Apple MacBook", Manufacturer: "Apple", Price: 1290, Time: "2015-01-18"}) 267 orders = append(orders, order{Article: "Paper", Manufacturer: "Canon", Price: 100, Time: "2015-03-01"}) 268 orders = append(orders, order{Article: "Apple iPad", Manufacturer: "Apple", Price: 499, Time: "2015-04-12"}) 269 orders = append(orders, order{Article: "Dell XPS 13", Manufacturer: "Dell", Price: 1600, Time: "2015-04-18"}) 270 orders = append(orders, order{Article: "Apple Watch", Manufacturer: "Apple", Price: 349, Time: "2015-04-29"}) 271 orders = append(orders, order{Article: "Samsung TV", Manufacturer: "Samsung", Price: 790, Time: "2015-05-03"}) 272 orders = append(orders, order{Article: "Hoodie", Manufacturer: "h&m", Price: 49, Time: "2015-06-03"}) 273 orders = append(orders, order{Article: "T-Shirt", Manufacturer: "h&m", Price: 19, Time: "2015-06-18"}) 274 for i, o := range orders { 275 id := fmt.Sprintf("%d", i) 276 _, err = client.Index().Index(testIndexName).Type("order").Id(id).BodyJson(&o).Do(context.TODO()) 277 if err != nil { 278 t.Fatal(err) 279 } 280 } 281 282 // Flush 283 _, err = client.Flush().Index(testIndexName).Do(context.TODO()) 284 if err != nil { 285 t.Fatal(err) 286 } 287 return client 288} 289 290func setupTestClientAndCreateIndexAndAddDocsNoSource(t logger, options ...ClientOptionFunc) *Client { 291 client := setupTestClientAndCreateIndex(t, options...) 292 293 // Add tweets 294 tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."} 295 tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."} 296 297 _, err := client.Index().Index(testIndexName).Type("tweet-nosource").Id("1").BodyJson(&tweet1).Do(context.TODO()) 298 if err != nil { 299 t.Fatal(err) 300 } 301 _, err = client.Index().Index(testIndexName).Type("tweet-nosource").Id("2").BodyJson(&tweet2).Do(context.TODO()) 302 if err != nil { 303 t.Fatal(err) 304 } 305 // Flush 306 _, err = client.Flush().Index(testIndexName).Do(context.TODO()) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 return client 312} 313 314var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 315 316func randomString(n int) string { 317 b := make([]rune, n) 318 for i := range b { 319 b[i] = letters[rand.Intn(len(letters))] 320 } 321 return string(b) 322} 323