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 testIndexName3 = "elastic-test3" 20 testIndexName4 = "elastic-test4" 21 testMapping = ` 22{ 23 "settings":{ 24 "number_of_shards":1, 25 "number_of_replicas":0 26 }, 27 "mappings":{ 28 "doc":{ 29 "properties":{ 30 "user":{ 31 "type":"keyword" 32 }, 33 "message":{ 34 "type":"text", 35 "store": true, 36 "fielddata": true 37 }, 38 "tags":{ 39 "type":"keyword" 40 }, 41 "location":{ 42 "type":"geo_point" 43 }, 44 "suggest_field":{ 45 "type":"completion", 46 "contexts":[ 47 { 48 "name":"user_name", 49 "type":"category" 50 } 51 ] 52 } 53 } 54 } 55 } 56} 57` 58 59 testNoSourceIndexName = "elastic-nosource-test" 60 testNoSourceMapping = ` 61{ 62 "settings":{ 63 "number_of_shards":1, 64 "number_of_replicas":0 65 }, 66 "mappings":{ 67 "doc":{ 68 "_source": { 69 "enabled": false 70 }, 71 "properties":{ 72 "user":{ 73 "type":"keyword" 74 }, 75 "message":{ 76 "type":"text", 77 "store": true, 78 "fielddata": true 79 }, 80 "tags":{ 81 "type":"keyword" 82 }, 83 "location":{ 84 "type":"geo_point" 85 }, 86 "suggest_field":{ 87 "type":"completion", 88 "contexts":[ 89 { 90 "name":"user_name", 91 "type":"category" 92 } 93 ] 94 } 95 } 96 } 97 } 98} 99` 100 101 testJoinIndex = "elastic-joins" 102 testJoinMapping = ` 103 { 104 "settings":{ 105 "number_of_shards":1, 106 "number_of_replicas":0 107 }, 108 "mappings":{ 109 "doc":{ 110 "properties":{ 111 "message":{ 112 "type":"text" 113 }, 114 "my_join_field": { 115 "type": "join", 116 "relations": { 117 "question": "answer" 118 } 119 } 120 } 121 } 122 } 123 } 124` 125 126 testOrderIndex = "elastic-orders" 127 testOrderMapping = ` 128{ 129"settings":{ 130 "number_of_shards":1, 131 "number_of_replicas":0 132}, 133"mappings":{ 134 "doc":{ 135 "properties":{ 136 "article":{ 137 "type":"text" 138 }, 139 "manufacturer":{ 140 "type":"keyword" 141 }, 142 "price":{ 143 "type":"float" 144 }, 145 "time":{ 146 "type":"date", 147 "format": "YYYY-MM-dd" 148 } 149 } 150 } 151} 152} 153` 154 155 /* 156 testDoctypeIndex = "elastic-doctypes" 157 testDoctypeMapping = ` 158 { 159 "settings":{ 160 "number_of_shards":1, 161 "number_of_replicas":0 162 }, 163 "mappings":{ 164 "doc":{ 165 "properties":{ 166 "message":{ 167 "type":"text", 168 "store": true, 169 "fielddata": true 170 } 171 } 172 } 173 } 174 } 175 ` 176 */ 177 178 testQueryIndex = "elastic-queries" 179 testQueryMapping = ` 180{ 181 "settings":{ 182 "number_of_shards":1, 183 "number_of_replicas":0 184 }, 185 "mappings":{ 186 "doc":{ 187 "properties":{ 188 "message":{ 189 "type":"text", 190 "store": true, 191 "fielddata": true 192 }, 193 "query": { 194 "type": "percolator" 195 } 196 } 197 } 198 } 199} 200` 201) 202 203type tweet struct { 204 User string `json:"user"` 205 Message string `json:"message"` 206 Retweets int `json:"retweets"` 207 Image string `json:"image,omitempty"` 208 Created time.Time `json:"created,omitempty"` 209 Tags []string `json:"tags,omitempty"` 210 Location string `json:"location,omitempty"` 211 Suggest *SuggestField `json:"suggest_field,omitempty"` 212} 213 214func (t tweet) String() string { 215 return fmt.Sprintf("tweet{User:%q,Message:%q,Retweets:%d}", t.User, t.Message, t.Retweets) 216} 217 218type comment struct { 219 User string `json:"user"` 220 Comment string `json:"comment"` 221 Created time.Time `json:"created,omitempty"` 222} 223 224func (c comment) String() string { 225 return fmt.Sprintf("comment{User:%q,Comment:%q}", c.User, c.Comment) 226} 227 228type joinDoc struct { 229 Message string `json:"message"` 230 JoinField interface{} `json:"my_join_field,omitempty"` 231} 232 233type joinField struct { 234 Name string `json:"name"` 235 Parent string `json:"parent,omitempty"` 236} 237 238type order struct { 239 Article string `json:"article"` 240 Manufacturer string `json:"manufacturer"` 241 Price float64 `json:"price"` 242 Time string `json:"time,omitempty"` 243} 244 245func (o order) String() string { 246 return fmt.Sprintf("order{Article:%q,Manufacturer:%q,Price:%v,Time:%v}", o.Article, o.Manufacturer, o.Price, o.Time) 247} 248 249// doctype is required for Percolate tests. 250type doctype struct { 251 Message string `json:"message"` 252} 253 254// queries is required for Percolate tests. 255type queries struct { 256 Query string `json:"query"` 257} 258 259func isTravis() bool { 260 return os.Getenv("TRAVIS") != "" 261} 262 263func travisGoVersion() string { 264 return os.Getenv("TRAVIS_GO_VERSION") 265} 266 267type logger interface { 268 Error(args ...interface{}) 269 Errorf(format string, args ...interface{}) 270 Fatal(args ...interface{}) 271 Fatalf(format string, args ...interface{}) 272 Fail() 273 FailNow() 274 Log(args ...interface{}) 275 Logf(format string, args ...interface{}) 276} 277 278func setupTestClient(t logger, options ...ClientOptionFunc) (client *Client) { 279 var err error 280 281 client, err = NewClient(options...) 282 if err != nil { 283 t.Fatal(err) 284 } 285 286 client.DeleteIndex(testIndexName).Do(context.TODO()) 287 client.DeleteIndex(testIndexName2).Do(context.TODO()) 288 client.DeleteIndex(testIndexName3).Do(context.TODO()) 289 client.DeleteIndex(testIndexName4).Do(context.TODO()) 290 client.DeleteIndex(testOrderIndex).Do(context.TODO()) 291 client.DeleteIndex(testNoSourceIndexName).Do(context.TODO()) 292 //client.DeleteIndex(testDoctypeIndex).Do(context.TODO()) 293 client.DeleteIndex(testQueryIndex).Do(context.TODO()) 294 client.DeleteIndex(testJoinIndex).Do(context.TODO()) 295 296 return client 297} 298 299func setupTestClientAndCreateIndex(t logger, options ...ClientOptionFunc) *Client { 300 client := setupTestClient(t, options...) 301 302 // Create index 303 createIndex, err := client.CreateIndex(testIndexName).Body(testMapping).Do(context.TODO()) 304 if err != nil { 305 t.Fatal(err) 306 } 307 if createIndex == nil { 308 t.Errorf("expected result to be != nil; got: %v", createIndex) 309 } 310 311 // Create second index 312 createIndex2, err := client.CreateIndex(testIndexName2).Body(testMapping).Do(context.TODO()) 313 if err != nil { 314 t.Fatal(err) 315 } 316 if createIndex2 == nil { 317 t.Errorf("expected result to be != nil; got: %v", createIndex2) 318 } 319 320 // Create no source index 321 createNoSourceIndex, err := client.CreateIndex(testNoSourceIndexName).Body(testNoSourceMapping).Do(context.TODO()) 322 if err != nil { 323 t.Fatal(err) 324 } 325 if createNoSourceIndex == nil { 326 t.Errorf("expected result to be != nil; got: %v", createNoSourceIndex) 327 } 328 329 // Create order index 330 createOrderIndex, err := client.CreateIndex(testOrderIndex).Body(testOrderMapping).Do(context.TODO()) 331 if err != nil { 332 t.Fatal(err) 333 } 334 if createOrderIndex == nil { 335 t.Errorf("expected result to be != nil; got: %v", createOrderIndex) 336 } 337 338 return client 339} 340 341func setupTestClientAndCreateIndexAndLog(t logger, options ...ClientOptionFunc) *Client { 342 return setupTestClientAndCreateIndex(t, SetTraceLog(log.New(os.Stdout, "", 0))) 343} 344 345func setupTestClientAndCreateIndexAndAddDocs(t logger, options ...ClientOptionFunc) *Client { 346 client := setupTestClientAndCreateIndex(t, options...) 347 348 // Add tweets 349 tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."} 350 tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."} 351 tweet3 := tweet{User: "sandrae", Message: "Cycling is fun."} 352 //comment1 := comment{User: "nico", Comment: "You bet."} 353 354 _, err := client.Index().Index(testIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO()) 355 if err != nil { 356 t.Fatal(err) 357 } 358 _, err = client.Index().Index(testIndexName).Type("doc").Id("2").BodyJson(&tweet2).Do(context.TODO()) 359 if err != nil { 360 t.Fatal(err) 361 } 362 _, err = client.Index().Index(testIndexName).Type("doc").Id("3").Routing("someroutingkey").BodyJson(&tweet3).Do(context.TODO()) 363 if err != nil { 364 t.Fatal(err) 365 } 366 /* 367 _, err = client.Index().Index(testIndexName).Type("comment").Id("1").Parent("3").BodyJson(&comment1).Do(context.TODO()) 368 if err != nil { 369 t.Fatal(err) 370 } 371 */ 372 373 // Add orders 374 var orders []order 375 orders = append(orders, order{Article: "Apple MacBook", Manufacturer: "Apple", Price: 1290, Time: "2015-01-18"}) 376 orders = append(orders, order{Article: "Paper", Manufacturer: "Canon", Price: 100, Time: "2015-03-01"}) 377 orders = append(orders, order{Article: "Apple iPad", Manufacturer: "Apple", Price: 499, Time: "2015-04-12"}) 378 orders = append(orders, order{Article: "Dell XPS 13", Manufacturer: "Dell", Price: 1600, Time: "2015-04-18"}) 379 orders = append(orders, order{Article: "Apple Watch", Manufacturer: "Apple", Price: 349, Time: "2015-04-29"}) 380 orders = append(orders, order{Article: "Samsung TV", Manufacturer: "Samsung", Price: 790, Time: "2015-05-03"}) 381 orders = append(orders, order{Article: "Hoodie", Manufacturer: "h&m", Price: 49, Time: "2015-06-03"}) 382 orders = append(orders, order{Article: "T-Shirt", Manufacturer: "h&m", Price: 19, Time: "2015-06-18"}) 383 for i, o := range orders { 384 id := fmt.Sprintf("%d", i) 385 _, err = client.Index().Index(testOrderIndex).Type("doc").Id(id).BodyJson(&o).Do(context.TODO()) 386 if err != nil { 387 t.Fatal(err) 388 } 389 } 390 391 // Flush 392 _, err = client.Flush().Index(testIndexName, testOrderIndex).Do(context.TODO()) 393 if err != nil { 394 t.Fatal(err) 395 } 396 return client 397} 398 399func setupTestClientAndCreateIndexAndAddDocsNoSource(t logger, options ...ClientOptionFunc) *Client { 400 client := setupTestClientAndCreateIndex(t, options...) 401 402 // Add tweets 403 tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."} 404 tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."} 405 406 _, err := client.Index().Index(testNoSourceIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO()) 407 if err != nil { 408 t.Fatal(err) 409 } 410 _, err = client.Index().Index(testNoSourceIndexName).Type("doc").Id("2").BodyJson(&tweet2).Do(context.TODO()) 411 if err != nil { 412 t.Fatal(err) 413 } 414 // Flush 415 _, err = client.Flush().Index(testNoSourceIndexName).Do(context.TODO()) 416 if err != nil { 417 t.Fatal(err) 418 } 419 420 return client 421} 422 423func setupTestClientForXpackSecurity(t logger) (client *Client) { 424 var err error 425 // Set URL and Auth to use the platinum ES cluster 426 options := []ClientOptionFunc{SetURL("http://127.0.0.1:9210"), SetBasicAuth("elastic", "elastic")} 427 428 client, err = NewClient(options...) 429 if err != nil { 430 t.Fatal(err) 431 } 432 433 return client 434} 435 436var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 437 438func randomString(n int) string { 439 b := make([]rune, n) 440 for i := range b { 441 b[i] = letters[rand.Intn(len(letters))] 442 } 443 return string(b) 444} 445 446type lexicographically struct { 447 strings []string 448} 449 450func (l lexicographically) Len() int { 451 return len(l.strings) 452} 453 454func (l lexicographically) Less(i, j int) bool { 455 return l.strings[i] < l.strings[j] 456} 457 458func (l lexicographically) Swap(i, j int) { 459 l.strings[i], l.strings[j] = l.strings[j], l.strings[i] 460} 461