├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── examples └── main.rs └── src └── elasticsearch └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use something that's not 'ruby' so we don't set up things like 2 | # RVM/bundler/ruby and whatnot. Right now 'rust' isn't a language on 3 | # travis and it treats unknown languages as ruby 4 | language: c 5 | 6 | before_install: 7 | - sudo apt-get update 8 | install: 9 | - curl -s http://www.rust-lang.org/rustup.sh | sudo sh 10 | - curl -O http://static.rust-lang.org/cargo-dist/cargo-nightly-linux.tar.gz 11 | - tar xf cargo-nightly-linux.tar.gz 12 | script: 13 | - ./cargo-nightly/bin/cargo build --verbose 14 | - ./cargo-nightly/bin/cargo test --verbose 15 | #- ./target/test/example 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elasticsearch" 3 | version = "0.2.0" 4 | authors = [ "erick.tryzelaar@gmail.com" ] 5 | 6 | [[lib]] 7 | name = "elasticsearch" 8 | path = "src/elasticsearch/lib.rs" 9 | 10 | [[example]] 11 | name = "example" 12 | path = "examples/main.rs" 13 | 14 | [dependencies.http] 15 | git = "https://github.com/chris-morgan/rust-http" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Erick Tryzelaar 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | rustpkg install elasticsearch 3 | 4 | test: all 5 | rustpkg install example 6 | 7 | clean: 8 | rm -rf bin build lib 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rust-elasticsearch is a [Rust language](http://rust-lang.org) binding for 2 | the [Elasticsearch](http://elasticsearch.org) fulltext search engine. 3 | 4 | Installation 5 | ------------ 6 | 7 | Install for the users of rust-elasticsearch: 8 | 9 | % cargo install zmq 10 | % cargo install uri 11 | % cargo install elasticseach 12 | 13 | Install for developers: 14 | 15 | % git clone https://github.com/erickt/rust-elasticsearch 16 | % cd rust-elasticsearch 17 | % make deps 18 | % make 19 | 20 | Setting up Elasticsearch 21 | ------------------------ 22 | 23 | rust-elasticsearch uses the 24 | [transport-zeromq](https://github.com/tlrx/transport-zeromq) plugin to 25 | connect with Elasticsearch. Unfortunately it can be a little tricky to set 26 | up. Here is how I got it to work. First, install the [Java 27 | Zeromq](https://github.com/zeromq/jzmq) bindings: 28 | 29 | % wget https://github.com/zeromq/jzmq/zipball/v1.0.0 30 | % unzip zeromq-jzmq-semver-0-gdaf4775 31 | % cd zeromq-jzmq-8522576 32 | % ./configure 33 | % make 34 | % make install 35 | 36 | Next, download Elasticsearch: 37 | 38 | % wget https://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.4.zip 39 | % unzip elasticsearch-0.19.4.zip 40 | 41 | After that, install the 42 | [transport-zeromq](https://github.com/tlrx/transport-zeromq) plugin: 43 | 44 | % cd elasticsearch-0.19.4 45 | % ./bin/plugin -install tlrx/transport-zeromq/0.0.3 46 | 47 | Finally, start Elasticsearch. You may need to explicitly set the shared library path. On Linux, do: 48 | 49 | % cd $elasticsearch_dir 50 | % export LD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib 51 | % ./bin/elasticsearch -f 52 | 53 | And Macs, do: 54 | 55 | % cd $elasticsearch_dir 56 | % export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib 57 | % ./bin/elasticsearch -f 58 | -------------------------------------------------------------------------------- /examples/main.rs: -------------------------------------------------------------------------------- 1 | extern crate elasticsearch; 2 | 3 | use elasticsearch::JsonObjectBuilder; 4 | 5 | fn main() { 6 | let mut client = elasticsearch::Client::new("http://localhost:9200"); 7 | /* 8 | println!("{}\n", client.transport.head("/")); 9 | println!("{}\n", client.transport.get("/")); 10 | */ 11 | 12 | println!("create an index"); 13 | println!("{}\n", client.prepare_create_index("test".to_string()) 14 | .set_source(JsonObjectBuilder::new() 15 | .insert_object("settings".to_string(), |bld| { 16 | bld. 17 | insert("index.number_of_shards".to_string(), 1u). 18 | insert("index.number_of_replicas".to_string(), 0u) 19 | }) 20 | .unwrap() 21 | ) 22 | .execute()); 23 | 24 | println!("try to fetch a non-existing document"); 25 | println!("{}\n", client.get("test", "test", "1")); 26 | 27 | println!("make that document"); 28 | println!("{}\n", client.prepare_index("test".to_string(), "test".to_string()) 29 | .set_id("1".to_string()) 30 | //.set_version(2u) 31 | .set_source(JsonObjectBuilder::new() 32 | .insert("foo".to_string(), 5.0f64) 33 | .insert("bar".to_string(), "wee".to_string()) 34 | .insert_object("baz".to_string(), |bld| bld.insert("a".to_string(), 2.0f64)) 35 | .insert_list("boo".to_string(), |bld| bld.push("aaa".to_string()).push("zzz".to_string())) 36 | .unwrap() 37 | ) 38 | .set_refresh(true) 39 | .execute()); 40 | 41 | println!("we should conflict if we have a conflicting version"); 42 | println!("{}\n", client.prepare_index("test".to_string(), "test".to_string()) 43 | .set_id("1".to_string()) 44 | .set_version(2u) 45 | .set_source(JsonObjectBuilder::new() 46 | .insert("foo".to_string(), 5.0f64) 47 | .insert("bar".to_string(), "wee".to_string()) 48 | .insert_object("baz".to_string(), |bld| bld.insert("a".to_string(), 2.0f64)) 49 | .insert_list("boo".to_string(), |bld| bld.push("aaa".to_string()).push("zzz".to_string())) 50 | .unwrap() 51 | ) 52 | .set_refresh(true) 53 | .execute()); 54 | 55 | println!("try to fetch that document"); 56 | println!("{}\n", client.get("test", "test", "1")); 57 | 58 | println!("{}\n", client.prepare_search() 59 | .set_indices(vec!["test".to_string()]) 60 | .set_source(JsonObjectBuilder::new() 61 | .insert("fields".to_string(), vec!["foo".to_string(), "bar".to_string()]) 62 | .unwrap() 63 | ) 64 | .execute()); 65 | 66 | println!("try to delete that document"); 67 | println!("{}\n", client.delete("test".to_string(), "test".to_string(), "1".to_string())); 68 | 69 | println!("index another document"); 70 | println!("{}\n", client.prepare_index("test".to_string(), "test".to_string()) 71 | .set_id("2".to_string()) 72 | .set_source(JsonObjectBuilder::new() 73 | .insert("bar".to_string(), "lala".to_string()) 74 | .unwrap() 75 | ) 76 | .set_refresh(true) 77 | .execute()); 78 | 79 | println!("delete that document with a query"); 80 | println!("{}\n", client.prepare_delete_by_query() 81 | .set_indices(vec!["test".to_string()]) 82 | .set_source(JsonObjectBuilder::new() 83 | .insert_object("term".to_string(), |bld| bld.insert("bar".to_string(), "lala".to_string())) 84 | .unwrap() 85 | ) 86 | .execute()); 87 | 88 | println!("finally, delete our test index"); 89 | println!("{}\n", client.prepare_delete_index() 90 | .set_indices(vec!["test".to_string()]) 91 | .execute()); 92 | } 93 | -------------------------------------------------------------------------------- /src/elasticsearch/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "elasticsearch"] 2 | 3 | #![feature(phase)] 4 | 5 | #[phase(plugin, link)] 6 | extern crate log; 7 | 8 | extern crate serialize; 9 | extern crate url; 10 | extern crate http; 11 | 12 | use std::collections::TreeMap; 13 | 14 | use http::client::RequestWriter; 15 | use http::headers::content_type; 16 | use http::method; 17 | 18 | use serialize::json::{Json, ToJson}; 19 | use serialize::json; 20 | 21 | /// The high level interface to elasticsearch 22 | pub struct Client { 23 | addr: String, 24 | } 25 | 26 | impl Client { 27 | /// Create an elasticsearch client 28 | pub fn new(addr: &str) -> Client { 29 | Client { 30 | addr: addr.to_string(), 31 | } 32 | } 33 | 34 | /// Create an index 35 | pub fn prepare_create_index<'a>(&'a mut self, index: String) -> CreateIndexBuilder<'a> { 36 | CreateIndexBuilder::new(self, index) 37 | } 38 | 39 | /// Delete indices 40 | pub fn prepare_delete_index<'a>(&'a mut self) -> DeleteIndexBuilder<'a> { 41 | DeleteIndexBuilder::new(self) 42 | } 43 | 44 | /// Get a specific document 45 | pub fn get(&mut self, index: &str, typ: &str, id: &str) -> Response { 46 | let path = [ 47 | url::encode_component(index), 48 | url::encode_component(typ), 49 | url::encode_component(id) 50 | ].connect("/"); 51 | 52 | self.request(method::Get, path.as_slice(), None) 53 | } 54 | 55 | /// Create an index builder that will create documents 56 | pub fn prepare_index<'a>(&'a mut self, index: String, typ: String) -> IndexBuilder<'a> { 57 | IndexBuilder::new(self, index, typ) 58 | } 59 | 60 | /// Create a search builder that will query elasticsearch 61 | pub fn prepare_search<'a>(&'a mut self) -> SearchBuilder<'a> { 62 | SearchBuilder::new(self) 63 | } 64 | 65 | /// Delete a document 66 | pub fn delete(&mut self, index: String, typ: String, id: String) -> Response { 67 | self.prepare_delete(index, typ, id).execute() 68 | } 69 | 70 | /// Delete a document 71 | pub fn prepare_delete<'a>(&'a mut self, index: String, typ: String, id: String) -> DeleteBuilder<'a> { 72 | DeleteBuilder::new(self, index, typ, id) 73 | } 74 | 75 | /// Create a search builder that will query elasticsearch 76 | pub fn prepare_delete_by_query<'a>(&'a mut self) -> DeleteByQueryBuilder<'a> { 77 | DeleteByQueryBuilder::new(self) 78 | } 79 | 80 | fn request(&mut self, method: method::Method, request: &str, body: Option) -> Response { 81 | debug!("request: {} {} {} {}", self.addr, method, request, body); 82 | 83 | let url = format!("{}/{}", self.addr, request); 84 | let url = from_str(url.as_slice()).unwrap(); 85 | let mut request: RequestWriter = RequestWriter::new(method, url).unwrap(); 86 | 87 | match body { 88 | Some(body) => { 89 | let body = json::Object(body).to_string(); 90 | request.headers.content_length = Some(body.len()); 91 | request.headers.content_type = Some( 92 | content_type::MediaType::new( 93 | "application".to_string(), 94 | "json".to_string(), 95 | vec!() 96 | ) 97 | ); 98 | request.write(body.as_bytes()).unwrap(); 99 | } 100 | None => { } 101 | } 102 | 103 | let mut response = match request.read_response() { 104 | Ok(response) => response, 105 | Err(_) => { fail!() } 106 | }; 107 | 108 | Response { 109 | code: response.status.code(), 110 | status: response.status.reason().to_string(), 111 | body: json::from_reader(&mut response).unwrap(), 112 | } 113 | } 114 | } 115 | 116 | pub enum Consistency { One, Quorum, All } 117 | pub enum Replication { Sync, Async } 118 | pub enum OpType { CREATE, INDEX } 119 | pub enum VersionType { INTERNAL, EXTERNAL } 120 | 121 | pub struct CreateIndexBuilder<'a> { 122 | client: &'a mut Client, 123 | index: String, 124 | timeout: Option, 125 | source: Option, 126 | } 127 | 128 | impl<'a> CreateIndexBuilder<'a> { 129 | pub fn new(client: &'a mut Client, index: String) -> CreateIndexBuilder<'a> { 130 | CreateIndexBuilder { 131 | client: client, 132 | index: index, 133 | timeout: None, 134 | source: None, 135 | } 136 | } 137 | 138 | pub fn set_timeout(self, timeout: String) -> CreateIndexBuilder<'a> { 139 | let mut builder = self; 140 | builder.timeout = Some(timeout); 141 | builder 142 | } 143 | pub fn set_source(self, source: json::Object) -> CreateIndexBuilder<'a> { 144 | let mut builder = self; 145 | builder.source = Some(source); 146 | builder 147 | } 148 | pub fn execute(self) -> Response { 149 | let CreateIndexBuilder { 150 | client, 151 | index, 152 | timeout, 153 | source, 154 | } = self; 155 | 156 | let mut path = url::encode_component(index.as_slice()); 157 | 158 | let mut params = vec!(); 159 | 160 | match timeout { 161 | None => { }, 162 | Some(ref s) => { 163 | params.push(format!("timeout={}", s)); 164 | } 165 | } 166 | 167 | if !params.is_empty() { 168 | path.push_str("?"); 169 | path.push_str(params.connect("&").as_slice()); 170 | } 171 | 172 | let source = match source { 173 | None => TreeMap::new(), 174 | Some(source) => source, 175 | }; 176 | 177 | client.request(method::Put, path.as_slice(), Some(source)) 178 | } 179 | } 180 | 181 | pub struct DeleteIndexBuilder<'a> { 182 | client: &'a mut Client, 183 | indices: Vec, 184 | timeout: Option, 185 | } 186 | 187 | impl<'a> DeleteIndexBuilder<'a> { 188 | pub fn new(client: &'a mut Client) -> DeleteIndexBuilder<'a> { 189 | DeleteIndexBuilder { 190 | client: client, 191 | indices: vec!(), 192 | timeout: None, 193 | } 194 | } 195 | 196 | pub fn set_indices(self, indices: Vec) -> DeleteIndexBuilder<'a> { 197 | let mut builder = self; 198 | builder.indices = indices; 199 | builder 200 | } 201 | pub fn set_timeout(self, timeout: String) -> DeleteIndexBuilder<'a> { 202 | let mut builder = self; 203 | builder.timeout = Some(timeout); 204 | builder 205 | } 206 | pub fn execute(self) -> Response { 207 | let DeleteIndexBuilder { 208 | client, 209 | indices, 210 | timeout, 211 | } = self; 212 | 213 | let indices: Vec = indices.iter().map(|i| { 214 | url::encode_component(i.as_slice()) 215 | }).collect(); 216 | let mut path = indices.connect(","); 217 | 218 | // Build the query parameters. 219 | let mut params = vec!(); 220 | 221 | match timeout { 222 | None => { }, 223 | Some(ref timeout) => params.push(format!("timeout={}", timeout)), 224 | } 225 | 226 | if !params.is_empty() { 227 | path.push_str("?"); 228 | path.push_str(params.connect("&").as_slice()); 229 | } 230 | 231 | client.request(method::Delete, path.as_slice(), None) 232 | } 233 | } 234 | 235 | pub struct IndexBuilder<'a> { 236 | client: &'a mut Client, 237 | index: String, 238 | typ: String, 239 | id: Option, 240 | 241 | consistency: Option, 242 | op_type: OpType, 243 | parent: Option, 244 | percolate: Option, 245 | refresh: bool, 246 | replication: Option, 247 | routing: Option, 248 | timeout: Option, 249 | timestamp: Option, 250 | ttl: Option, 251 | version: Option, 252 | version_type: VersionType, 253 | 254 | source: Option, 255 | } 256 | 257 | impl<'a> IndexBuilder<'a> { 258 | pub fn new(client: &'a mut Client, index: String, typ: String) -> IndexBuilder<'a> { 259 | IndexBuilder { 260 | client: client, 261 | index: index, 262 | typ: typ, 263 | id: None, 264 | 265 | consistency: None, 266 | op_type: INDEX, 267 | parent: None, 268 | percolate: None, 269 | refresh: false, 270 | replication: None, 271 | routing: None, 272 | timeout: None, 273 | timestamp: None, 274 | ttl: None, 275 | version: None, 276 | version_type: INTERNAL, 277 | 278 | source: None, 279 | } 280 | } 281 | 282 | pub fn set_id(self, id: String) -> IndexBuilder<'a> { 283 | let mut builder = self; 284 | builder.id = Some(id); 285 | builder 286 | } 287 | pub fn set_consistency(self, consistency: Consistency) -> IndexBuilder<'a> { 288 | let mut builder = self; 289 | builder.consistency = Some(consistency); 290 | builder 291 | } 292 | pub fn set_op_type(self, op_type: OpType) -> IndexBuilder<'a> { 293 | let mut builder = self; 294 | builder.op_type = op_type; 295 | builder 296 | } 297 | pub fn set_parent(self, parent: String) -> IndexBuilder<'a> { 298 | let mut builder = self; 299 | builder.parent = Some(parent); 300 | builder 301 | } 302 | pub fn set_percolate(self, percolate: String) -> IndexBuilder<'a> { 303 | let mut builder = self; 304 | builder.percolate = Some(percolate); 305 | builder 306 | } 307 | pub fn set_refresh(self, refresh: bool) -> IndexBuilder<'a> { 308 | let mut builder = self; 309 | builder.refresh = refresh; 310 | builder 311 | } 312 | pub fn set_replication(self, replication: Replication) -> IndexBuilder<'a> { 313 | let mut builder = self; 314 | builder.replication = Some(replication); 315 | builder 316 | } 317 | pub fn set_routing(self, routing: String) -> IndexBuilder<'a> { 318 | let mut builder = self; 319 | builder.routing = Some(routing); 320 | builder 321 | } 322 | pub fn set_timeout(self, timeout: String) -> IndexBuilder<'a> { 323 | let mut builder = self; 324 | builder.timeout = Some(timeout); 325 | builder 326 | } 327 | pub fn set_timestamp(self, timestamp: String) -> IndexBuilder<'a> { 328 | let mut builder = self; 329 | builder.timestamp = Some(timestamp); 330 | builder 331 | } 332 | pub fn set_ttl(self, ttl: String) -> IndexBuilder<'a> { 333 | let mut builder = self; 334 | builder.ttl = Some(ttl); 335 | builder 336 | } 337 | pub fn set_version(self, version: uint) -> IndexBuilder<'a> { 338 | let mut builder = self; 339 | builder.version = Some(version); 340 | builder 341 | } 342 | pub fn set_version_type(self, version_type: VersionType) -> IndexBuilder<'a> { 343 | let mut builder = self; 344 | builder.version_type = version_type; 345 | builder 346 | } 347 | pub fn set_source(self, source: json::Object) -> IndexBuilder<'a> { 348 | let mut builder = self; 349 | builder.source = Some(source); 350 | builder 351 | } 352 | pub fn execute(self) -> Response { 353 | let IndexBuilder { 354 | client, 355 | index, 356 | typ, 357 | id, 358 | consistency, 359 | op_type, 360 | parent, 361 | percolate, 362 | refresh, 363 | replication, 364 | routing, 365 | timeout, 366 | timestamp, 367 | ttl, 368 | version, 369 | version_type, 370 | source, 371 | } = self; 372 | 373 | let mut path = vec!( 374 | url::encode_component(index.as_slice()), 375 | url::encode_component(typ.as_slice()), 376 | ); 377 | 378 | // FIXME: https://github.com/mozilla/rust/issues/2549 379 | match id { 380 | None => { }, 381 | Some(ref id) => path.push(url::encode_component(id.as_slice())), 382 | } 383 | 384 | let mut path = path.connect("/"); 385 | let mut params = vec!(); 386 | 387 | match consistency { 388 | None => { }, 389 | Some(One) => params.push("consistency=one".to_string()), 390 | Some(Quorum) => params.push("consistency=quorum".to_string()), 391 | Some(All) => params.push("consistency=all".to_string()), 392 | } 393 | 394 | match op_type { 395 | CREATE => params.push("op_type=create".to_string()), 396 | INDEX => { } 397 | } 398 | 399 | match parent { 400 | None => { }, 401 | Some(ref s) => params.push(format!("parent={}", s)), 402 | } 403 | 404 | match percolate { 405 | None => { } 406 | Some(ref s) => params.push(format!("percolate={}", s)), 407 | } 408 | 409 | if refresh { params.push("refresh=true".to_string()); } 410 | 411 | match replication { 412 | None => { }, 413 | Some(Sync) => params.push("replication=sync".to_string()), 414 | Some(Async) => params.push("replication=async".to_string()), 415 | } 416 | 417 | match routing { 418 | None => { }, 419 | Some(ref s) => params.push(format!("routing={}", s)), 420 | } 421 | 422 | match timeout { 423 | None => { }, 424 | Some(ref s) => params.push(format!("timeout={}", s)), 425 | } 426 | 427 | match timestamp { 428 | None => { }, 429 | Some(ref s) => params.push(format!("timestamp={}", s)), 430 | } 431 | 432 | match ttl { 433 | None => { }, 434 | Some(ref s) => params.push(format!("ttl={}", s)), 435 | } 436 | 437 | match version { 438 | None => { }, 439 | Some(ref i) => { params.push(format!("version={}", i)); } 440 | } 441 | 442 | match version_type { 443 | INTERNAL => { }, 444 | EXTERNAL => params.push("version_type=external".to_string()), 445 | } 446 | 447 | if !params.is_empty() { 448 | path.push_str("?"); 449 | path.push_str(params.connect("&").as_slice()); 450 | } 451 | 452 | let source = match source { 453 | None => TreeMap::new(), 454 | Some(source) => source, 455 | }; 456 | 457 | match id { 458 | None => client.request(method::Post, path.as_slice(), Some(source)), 459 | Some(_) => client.request(method::Put, path.as_slice(), Some(source)), 460 | } 461 | } 462 | } 463 | 464 | pub enum SearchType { 465 | DfsQueryThenFetch, 466 | QueryThenFetch, 467 | DfsQueryAndFetch, 468 | QueryAndFetch, 469 | Scan, 470 | Count, 471 | } 472 | 473 | pub struct SearchBuilder<'a> { 474 | client: &'a mut Client, 475 | indices: Vec, 476 | types: Vec, 477 | 478 | preference: Option, 479 | routing: Option, 480 | scroll: Option, 481 | search_type: Option, 482 | timeout: Option, 483 | 484 | source: Option 485 | } 486 | 487 | impl<'a> SearchBuilder<'a> { 488 | pub fn new(client: &'a mut Client) -> SearchBuilder<'a> { 489 | SearchBuilder { 490 | client: client, 491 | indices: vec!(), 492 | types: vec!(), 493 | 494 | preference: None, 495 | routing: None, 496 | scroll: None, 497 | search_type: None, 498 | timeout: None, 499 | 500 | source: None, 501 | } 502 | } 503 | 504 | pub fn set_indices(self, indices: Vec) -> SearchBuilder<'a> { 505 | let mut builder = self; 506 | builder.indices = indices; 507 | builder 508 | } 509 | pub fn set_types(self, types: Vec) -> SearchBuilder<'a> { 510 | let mut builder = self; 511 | builder.types = types; 512 | builder 513 | } 514 | pub fn set_preference(self, preference: String) -> SearchBuilder<'a> { 515 | let mut builder = self; 516 | builder.preference = Some(preference); 517 | builder 518 | } 519 | pub fn set_routing(self, routing: String) -> SearchBuilder<'a> { 520 | let mut builder = self; 521 | builder.routing = Some(routing); 522 | builder 523 | } 524 | pub fn set_scroll(self, scroll: String) -> SearchBuilder<'a> { 525 | let mut builder = self; 526 | builder.scroll = Some(scroll); 527 | builder 528 | } 529 | pub fn set_search_type(self, search_type: SearchType) -> SearchBuilder<'a> { 530 | let mut builder = self; 531 | builder.search_type = Some(search_type); 532 | builder 533 | } 534 | pub fn set_timeout(self, timeout: String) -> SearchBuilder<'a> { 535 | let mut builder = self; 536 | builder.timeout = Some(timeout); 537 | builder 538 | } 539 | pub fn set_source(self, source: json::Object) -> SearchBuilder<'a> { 540 | let mut builder = self; 541 | builder.source = Some(source); 542 | builder 543 | } 544 | pub fn execute(self) -> Response { 545 | let SearchBuilder { 546 | client, 547 | indices, 548 | types, 549 | preference, 550 | routing, 551 | scroll, 552 | search_type, 553 | timeout, 554 | source, 555 | } = self; 556 | 557 | let indices: Vec = indices.iter().map(|i| { 558 | url::encode_component(i.as_slice()) 559 | }).collect(); 560 | 561 | let types: Vec = types.iter().map(|t| { 562 | url::encode_component(t.as_slice()) 563 | }).collect(); 564 | 565 | let mut path = vec!(); 566 | 567 | if indices.is_empty() { 568 | path.push("_all".to_string()); 569 | } else { 570 | path.push(indices.connect(",")); 571 | } 572 | 573 | if !types.is_empty() { 574 | path.push(types.connect(",")); 575 | } 576 | 577 | path.push("_search".to_string()); 578 | 579 | let mut path = path.connect("/"); 580 | 581 | // Build the query parameters. 582 | let mut params = vec!(); 583 | 584 | match preference { 585 | None => { }, 586 | Some(ref s) => params.push(format!("preference={}", s)), 587 | } 588 | 589 | match routing { 590 | None => { }, 591 | Some(ref s) => params.push(format!("routing={}", s)), 592 | } 593 | 594 | match scroll { 595 | None => { }, 596 | Some(ref s) => params.push(format!("scroll={}", s)), 597 | } 598 | 599 | match search_type { 600 | None => { }, 601 | Some(DfsQueryThenFetch) => 602 | params.push("search_type=dfs_query_then_fetch".to_string()), 603 | Some(QueryThenFetch) => 604 | params.push("search_type=query_then_fetch".to_string()), 605 | Some(DfsQueryAndFetch) => 606 | params.push("search_type=dfs_query_and_fetch".to_string()), 607 | Some(QueryAndFetch) => 608 | params.push("search_type=query_and_fetch".to_string()), 609 | Some(Scan) => params.push("search_type=scan".to_string()), 610 | Some(Count) => params.push("search_type=count".to_string()), 611 | } 612 | 613 | match timeout { 614 | None => { } 615 | Some(ref s) => params.push(format!("timeout={}", s)), 616 | } 617 | 618 | if !params.is_empty() { 619 | path.push_str("?"); 620 | path.push_str(params.connect("&").as_slice()); 621 | } 622 | 623 | client.request(method::Post, path.as_slice(), source) 624 | } 625 | } 626 | 627 | pub struct DeleteBuilder<'a> { 628 | client: &'a mut Client, 629 | index: String, 630 | typ: String, 631 | id: String, 632 | 633 | consistency: Option, 634 | refresh: bool, 635 | replication: Option, 636 | routing: Option, 637 | timeout: Option, 638 | version: Option, 639 | version_type: VersionType, 640 | } 641 | 642 | impl<'a> DeleteBuilder<'a> { 643 | pub fn new( 644 | client: &'a mut Client, 645 | index: String, 646 | typ: String, 647 | id: String 648 | ) -> DeleteBuilder<'a> { 649 | DeleteBuilder { 650 | client: client, 651 | index: index, 652 | typ: typ, 653 | id: id, 654 | consistency: None, 655 | refresh: false, 656 | replication: None, 657 | routing: None, 658 | timeout: None, 659 | version: None, 660 | version_type: INTERNAL, 661 | } 662 | } 663 | 664 | pub fn set_consistency(self, consistency: Consistency) -> DeleteBuilder<'a> { 665 | let mut builder = self; 666 | builder.consistency = Some(consistency); 667 | builder 668 | } 669 | pub fn set_parent(self, parent: String) -> DeleteBuilder<'a> { 670 | // We use the parent for routing. 671 | let mut builder = self; 672 | builder.routing = Some(parent); 673 | builder 674 | } 675 | pub fn set_refresh(self, refresh: bool) -> DeleteBuilder<'a> { 676 | let mut builder = self; 677 | builder.refresh = refresh; 678 | builder 679 | } 680 | pub fn set_replication(self, replication: Replication) -> DeleteBuilder<'a> { 681 | let mut builder = self; 682 | builder.replication = Some(replication); 683 | builder 684 | } 685 | pub fn set_routing(self, routing: String) -> DeleteBuilder<'a> { 686 | let mut builder = self; 687 | builder.routing = Some(routing); 688 | builder 689 | } 690 | pub fn set_timeout(self, timeout: String) -> DeleteBuilder<'a> { 691 | let mut builder = self; 692 | builder.timeout = Some(timeout); 693 | builder 694 | } 695 | pub fn set_version(self, version: uint) -> DeleteBuilder<'a> { 696 | let mut builder = self; 697 | builder.version = Some(version); 698 | builder 699 | } 700 | pub fn set_version_type(self, version_type: VersionType) -> DeleteBuilder<'a> { 701 | let mut builder = self; 702 | builder.version_type = version_type; 703 | builder 704 | } 705 | pub fn execute(self) -> Response { 706 | let DeleteBuilder { 707 | client, 708 | index, 709 | typ, 710 | id, 711 | consistency, 712 | refresh, 713 | replication, 714 | routing, 715 | timeout, 716 | version, 717 | version_type, 718 | } = self; 719 | 720 | let mut path = [ 721 | url::encode_component(index.as_slice()), 722 | url::encode_component(typ.as_slice()), 723 | url::encode_component(id.as_slice()) 724 | ].connect("/"); 725 | 726 | // Build the query parameters. 727 | let mut params = vec!(); 728 | 729 | match consistency { 730 | None => { }, 731 | Some(One) => params.push("consistency=one".to_string()), 732 | Some(Quorum) => params.push("consistency=quorum".to_string()), 733 | Some(All) => params.push("consistency=all".to_string()), 734 | } 735 | 736 | if refresh { params.push("refresh=true".to_string()); } 737 | 738 | match replication { 739 | None => { } 740 | Some(Sync) => params.push("replication=sync".to_string()), 741 | Some(Async) => params.push("replication=async".to_string()), 742 | } 743 | 744 | match routing { 745 | None => { } 746 | Some(ref s) => params.push(format!("routing={}", *s)), 747 | } 748 | 749 | match timeout { 750 | None => { } 751 | Some(ref s) => params.push(format!("timeout={}", *s)), 752 | } 753 | 754 | match version { 755 | None => { } 756 | Some(ref s) => params.push(format!("version={}", *s)), 757 | } 758 | 759 | match version_type { 760 | INTERNAL => { } 761 | EXTERNAL => params.push("version_type=external".to_string()), 762 | } 763 | 764 | if !params.is_empty() { 765 | path.push_str("?"); 766 | path.push_str(params.connect("&").as_slice()); 767 | } 768 | 769 | client.request(method::Delete, path.as_slice(), None) 770 | } 771 | } 772 | 773 | pub struct DeleteByQueryBuilder<'a> { 774 | client: &'a mut Client, 775 | indices: Vec, 776 | types: Vec, 777 | 778 | consistency: Option, 779 | refresh: bool, 780 | replication: Option, 781 | routing: Option, 782 | timeout: Option, 783 | 784 | source: Option, 785 | } 786 | 787 | impl<'a> DeleteByQueryBuilder<'a> { 788 | pub fn new(client: &'a mut Client) -> DeleteByQueryBuilder<'a> { 789 | DeleteByQueryBuilder { 790 | client: client, 791 | indices: vec!(), 792 | types: vec!(), 793 | 794 | consistency: None, 795 | refresh: false, 796 | replication: None, 797 | routing: None, 798 | timeout: None, 799 | 800 | source: None, 801 | } 802 | } 803 | 804 | pub fn set_indices(self, indices: Vec) -> DeleteByQueryBuilder<'a> { 805 | let mut builder = self; 806 | builder.indices = indices; 807 | builder 808 | } 809 | pub fn set_types(self, types: Vec) -> DeleteByQueryBuilder<'a> { 810 | let mut builder = self; 811 | builder.types = types; 812 | builder 813 | } 814 | pub fn set_consistency(self, consistency: Consistency) -> DeleteByQueryBuilder<'a> { 815 | let mut builder = self; 816 | builder.consistency = Some(consistency); 817 | builder 818 | } 819 | pub fn set_refresh(self, refresh: bool) -> DeleteByQueryBuilder<'a> { 820 | let mut builder = self; 821 | builder.refresh = refresh; 822 | builder 823 | } 824 | pub fn set_replication(self, replication: Replication) -> DeleteByQueryBuilder<'a> { 825 | let mut builder = self; 826 | builder.replication = Some(replication); 827 | builder 828 | } 829 | pub fn set_routing(self, routing: String) -> DeleteByQueryBuilder<'a> { 830 | let mut builder = self; 831 | builder.routing = Some(routing); 832 | builder 833 | } 834 | pub fn set_timeout(self, timeout: String) -> DeleteByQueryBuilder<'a> { 835 | let mut builder = self; 836 | builder.timeout = Some(timeout); 837 | builder 838 | } 839 | pub fn set_source(self, source: json::Object) -> DeleteByQueryBuilder<'a> { 840 | let mut builder = self; 841 | builder.source = Some(source); 842 | builder 843 | } 844 | 845 | pub fn execute(self) -> Response { 846 | let DeleteByQueryBuilder { 847 | client, 848 | indices, 849 | types, 850 | consistency, 851 | refresh, 852 | replication, 853 | routing, 854 | timeout, 855 | source, 856 | } = self; 857 | 858 | let mut path = vec!(); 859 | 860 | if indices.is_empty() { 861 | path.push("_all".to_string()); 862 | } else { 863 | path.push(indices.connect(",")); 864 | } 865 | 866 | if !types.is_empty() { 867 | path.push(types.connect(",")); 868 | } 869 | 870 | path.push("_query".to_string()); 871 | 872 | let mut path = path.connect("/"); 873 | 874 | // Build the query parameters. 875 | let mut params = vec!(); 876 | 877 | match consistency { 878 | None => {} 879 | Some(One) => params.push("consistency=one".to_string()), 880 | Some(Quorum) => params.push("consistency=quorum".to_string()), 881 | Some(All) => params.push("consistency=all".to_string()), 882 | } 883 | 884 | if refresh { params.push("refresh=true".to_string()); } 885 | 886 | match replication { 887 | None => { } 888 | Some(Sync) => params.push("replication=sync".to_string()), 889 | Some(Async) => params.push("replication=async".to_string()), 890 | } 891 | 892 | match routing { 893 | None => { } 894 | Some(ref routing) => params.push(format!("routing={}", routing)), 895 | } 896 | 897 | match timeout { 898 | None => { } 899 | Some(ref timeout) => params.push(format!("timeout={}", timeout)), 900 | } 901 | 902 | if !params.is_empty() { 903 | path.push_str("?"); 904 | path.push_str(params.connect("&").as_slice()); 905 | } 906 | 907 | client.request(method::Delete, path.as_slice(), source) 908 | } 909 | } 910 | 911 | pub struct JsonListBuilder { 912 | list: Vec 913 | } 914 | 915 | impl JsonListBuilder { 916 | pub fn new() -> JsonListBuilder { 917 | JsonListBuilder { list: vec!() } 918 | } 919 | 920 | pub fn unwrap(self) -> Vec { 921 | let JsonListBuilder { list } = self; 922 | list 923 | } 924 | 925 | pub fn push(self, value: T) -> JsonListBuilder { 926 | let mut builder = self; 927 | builder.list.push(value.to_json()); 928 | builder 929 | } 930 | 931 | pub fn push_list(self, f: |JsonListBuilder| -> JsonListBuilder) -> JsonListBuilder { 932 | let builder = JsonListBuilder::new(); 933 | self.push(f(builder).unwrap()) 934 | } 935 | 936 | pub fn push_object(self, f: |JsonObjectBuilder| -> JsonObjectBuilder) -> JsonListBuilder { 937 | let builder = JsonObjectBuilder::new(); 938 | self.push(json::Object(f(builder).unwrap())) 939 | } 940 | } 941 | 942 | pub struct JsonObjectBuilder { 943 | object: json::Object 944 | } 945 | 946 | impl JsonObjectBuilder { 947 | pub fn new() -> JsonObjectBuilder { 948 | JsonObjectBuilder { object: TreeMap::new() } 949 | } 950 | 951 | pub fn unwrap(self) -> json::Object { 952 | let JsonObjectBuilder { object } = self; 953 | object 954 | } 955 | 956 | pub fn insert(self, key: String, value: T) -> JsonObjectBuilder { 957 | let mut builder = self; 958 | builder.object.insert(key, value.to_json()); 959 | builder 960 | } 961 | 962 | pub fn insert_list<'a>(self, key: String, f: |JsonListBuilder| -> JsonListBuilder) -> JsonObjectBuilder { 963 | let builder = JsonListBuilder::new(); 964 | self.insert(key, f(builder).unwrap()) 965 | } 966 | 967 | pub fn insert_object<'a>(self, key: String, f: |JsonObjectBuilder| -> JsonObjectBuilder) -> JsonObjectBuilder { 968 | let builder = JsonObjectBuilder::new(); 969 | self.insert(key, json::Object(f(builder).unwrap())) 970 | } 971 | } 972 | 973 | #[deriving(PartialEq, Clone, Show)] 974 | pub struct Response { 975 | pub code: u16, 976 | pub status: String, 977 | pub body: json::Json, 978 | } 979 | --------------------------------------------------------------------------------