From 37e71f7c634939fd521153f4b6854bc1cde3c105 Mon Sep 17 00:00:00 2001 From: Claus Matzinger Date: Sun, 12 Mar 2017 22:59:38 -0400 Subject: [PATCH 01/18] fixes #100 and improves #99 --- examples/simple_search.rs | 120 ++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/examples/simple_search.rs b/examples/simple_search.rs index 2f26ba1fb..d422b461b 100644 --- a/examples/simple_search.rs +++ b/examples/simple_search.rs @@ -10,105 +10,107 @@ use tantivy::collector::TopCollector; use tantivy::query::QueryParser; fn main() { - // Let's create a temporary directory for the + // Let's create a temporary directory for the // sake of this example if let Ok(dir) = TempDir::new("tantivy_example_dir") { run_example(dir.path()).unwrap(); dir.close().unwrap(); - } + } } fn run_example(index_path: &Path) -> tantivy::Result<()> { - - + + // # Defining the schema // // The Tantivy index requires a very strict schema. // The schema declares which fields are in the index, - // and for each field, its type and "the way it should + // and for each field, its type and "the way it should // be indexed". - - + + // first we need to define a schema ... let mut schema_builder = SchemaBuilder::default(); - + // Our first field is title. // We want full-text search for it, and we want to be able // to retrieve the document after the search. // // TEXT | STORED is some syntactic sugar to describe - // that. - // + // that. + // // `TEXT` means the field should be tokenized and indexed, // along with its term frequency and term positions. // // `STORED` means that the field will also be saved // in a compressed, row-oriented key-value store. - // This store is useful to reconstruct the + // This store is useful to reconstruct the // documents that were selected during the search phase. schema_builder.add_text_field("title", TEXT | STORED); - + // Our first field is body. // We want full-text search for it, and we want to be able // to retrieve the body after the search. schema_builder.add_text_field("body", TEXT); - - let schema = schema_builder.build(); + + let schema = schema_builder.build(); // # Indexing documents // // Let's create a brand new index. - // + // // This will actually just save a meta.json // with our schema in the directory. let index = try!(Index::create(index_path, schema.clone())); - - + + // To insert document we need an index writer. // There must be only one writer at a time. // This single `IndexWriter` is already // multithreaded. // - // Here we use a buffer of 1 GB. Using a bigger + // Here we use a buffer of 50MB. Using a bigger // heap for the indexer can increase its throughput. // This buffer will be split between the indexing // threads. - let mut index_writer = try!(index.writer(1_000_000_000)); + let mut index_writer = try!(index.writer(50_000_000)); // Let's index our documents! // We first need a handle on the title and the body field. - - + + // ### Create a document "manually". // // We can create a document manually, by setting the fields // one by one in a Document object. let title = schema.get_field("title").unwrap(); let body = schema.get_field("body").unwrap(); - + let mut old_man_doc = Document::default(); old_man_doc.add_text(title, "The Old Man and the Sea"); - old_man_doc.add_text(body, "He was an old man who fished alone in a skiff in the Gulf Stream and he had gone eighty-four days now without taking a fish."); - + old_man_doc.add_text(body, + "He was an old man who fished alone in a skiff in the Gulf Stream and \ + he had gone eighty-four days now without taking a fish."); + // ... and add it to the `IndexWriter`. index_writer.add_document(old_man_doc); - + // ### Create a document directly from json. // // Alternatively, we can use our schema to parse // a document object directly from json. - + let mice_and_men_doc = try!(schema.parse_document(r#"{ "title": "Of Mice and Men", "body": "few miles south of Soledad, the Salinas River drops in close to the hillside bank and runs deep and green. The water is warm too, for it has slipped twinkling over the yellow sands in the sunlight before reaching the narrow pool. On one side of the river the golden foothill slopes curve up to the strong and rocky Gabilan Mountains, but on the valley side the water is lined with trees—willows fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white,recumbent limbs and branches that arch over the pool" }"#)); - + index_writer.add_document(mice_and_men_doc); - + // Multi-valued field are allowed, they are // expressed in JSON by an array. // The following document has two titles. @@ -117,19 +119,19 @@ fn run_example(index_path: &Path) -> tantivy::Result<()> { "body": "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings. I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking." }"#)); index_writer.add_document(frankenstein_doc); - + // This is an example, so we will only index 3 documents // here. You can check out tantivy's tutorial to index - // the English wikipedia. Tantivy's indexing is rather fast. + // the English wikipedia. Tantivy's indexing is rather fast. // Indexing 5 million articles of the English wikipedia takes // around 4 minutes on my computer! - - + + // ### Committing - // + // // At this point our documents are not searchable. // - // + // // We need to call .commit() explicitly to force the // index_writer to finish processing the documents in the queue, // flush the current index to the disk, and advertise @@ -137,22 +139,25 @@ fn run_example(index_path: &Path) -> tantivy::Result<()> { // // This call is blocking. try!(index_writer.commit()); - + // If `.commit()` returns correctly, then all of the // documents that have been added are guaranteed to be // persistently indexed. - // + // // In the scenario of a crash or a power failure, // tantivy behaves as if has rolled back to its last // commit. - - + + // # Searching // - // Let's search our index. We start - // by creating a searcher. There can be more - // than one searcher at a time. - // + // Let's search our index. Start by reloading + // searchers in the index. This should be done + // after every commit(). + try!(index.load_searchers()); + + // Afterwards create one (or more) searchers. + // // You should create a searcher // every time you start a "search query". let searcher = index.searcher(); @@ -161,46 +166,45 @@ fn run_example(index_path: &Path) -> tantivy::Result<()> { // Here, if the user does not specify which // field they want to search, tantivy will search // in both title and body. - let query_parser = QueryParser::new(index.schema(), vec!(title, body)); - + let query_parser = QueryParser::new(index.schema(), vec![title, body]); + // QueryParser may fail if the query is not in the right // format. For user facing applications, this can be a problem. // A ticket has been opened regarding this problem. let query = try!(query_parser.parse_query("sea whale")); - - + + // A query defines a set of documents, as // well as the way they should be scored. - // + // // A query created by the query parser is scored according // to a metric called Tf-Idf, and will consider // any document matching at least one of our terms. - - // ### Collectors + + // ### Collectors // - // We are not interested in all of the documents but + // We are not interested in all of the documents but // only in the top 10. Keeping track of our top 10 best documents // is the role of the TopCollector. - let mut top_collector = TopCollector::with_limit(10); - + // We can now perform our query. try!(searcher.search(&*query, &mut top_collector)); - // Our top collector now contains the 10 + // Our top collector now contains the 10 // most relevant doc ids... let doc_addresses = top_collector.docs(); - // The actual documents still need to be + // The actual documents still need to be // retrieved from Tantivy's store. - // + // // Since the body field was not configured as stored, // the document returned will only contain // a title. - + for doc_address in doc_addresses { - let retrieved_doc = try!(searcher.doc(&doc_address)); - println!("{}", schema.to_json(&retrieved_doc)); + let retrieved_doc = try!(searcher.doc(&doc_address)); + println!("{}", schema.to_json(&retrieved_doc)); } Ok(()) From 292dd6dcb65d993fc59704290ae0a2ead5fc2a03 Mon Sep 17 00:00:00 2001 From: Claus Matzinger Date: Mon, 13 Mar 2017 00:24:54 -0400 Subject: [PATCH 02/18] fixup --- examples/simple_search.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/simple_search.rs b/examples/simple_search.rs index d422b461b..430d7abf0 100644 --- a/examples/simple_search.rs +++ b/examples/simple_search.rs @@ -73,10 +73,8 @@ fn run_example(index_path: &Path) -> tantivy::Result<()> { // This single `IndexWriter` is already // multithreaded. // - // Here we use a buffer of 50MB. Using a bigger + // Here we use a buffer of 50MB per thread. Using a bigger // heap for the indexer can increase its throughput. - // This buffer will be split between the indexing - // threads. let mut index_writer = try!(index.writer(50_000_000)); // Let's index our documents! From 50659147d12da30ff805740374744e5eaa8d24c6 Mon Sep 17 00:00:00 2001 From: Paul Masurel Date: Tue, 14 Mar 2017 12:04:21 +0900 Subject: [PATCH 03/18] NOBUG updated simple_search.html --- examples/html/simple_search.html | 134 +++++++++++++++++-------------- 1 file changed, 74 insertions(+), 60 deletions(-) diff --git a/examples/html/simple_search.html b/examples/html/simple_search.html index 1cfc7ac6e..1aa6b63ab 100644 --- a/examples/html/simple_search.html +++ b/examples/html/simple_search.html @@ -52,7 +52,7 @@
-

Let’s create a temporary directory for the +

Let’s create a temporary directory for the sake of this example

@@ -60,7 +60,7 @@ sake of this example

    if let Ok(dir) = TempDir::new("tantivy_example_dir") {
         run_example(dir.path()).unwrap();
         dir.close().unwrap();
-    }   
+    }
 }
 
 
@@ -78,7 +78,7 @@ sake of this example

Defining the schema

The Tantivy index requires a very strict schema. The schema declares which fields are in the index, -and for each field, its type and “the way it should +and for each field, its type and “the way it should be indexed”.

@@ -111,12 +111,12 @@ be indexed”.

We want full-text search for it, and we want to be able to retrieve the document after the search.

TEXT | STORED is some syntactic sugar to describe -that.

+that.

TEXT means the field should be tokenized and indexed, along with its term frequency and term positions.

STORED means that the field will also be saved in a compressed, row-oriented key-value store. -This store is useful to reconstruct the +This store is useful to reconstruct the documents that were selected during the search phase.

@@ -139,7 +139,7 @@ to retrieve the body after the search.

    schema_builder.add_text_field("body", TEXT);
-    
+
     let schema = schema_builder.build();
@@ -173,14 +173,12 @@ with our schema in the directory.

There must be only one writer at a time. This single IndexWriter is already multithreaded.

-

Here we use a buffer of 1 GB. Using a bigger -heap for the indexer can increase its throughput. -This buffer will be split between the indexing -threads.

+

Here we use a buffer of 50MB per thread. Using a bigger +heap for the indexer can increase its throughput.

-
    let mut index_writer = try!(index.writer(1_000_000_000));
+
    let mut index_writer = try!(index.writer(50_000_000));
@@ -213,10 +211,12 @@ one by one in a Document object.

    let title = schema.get_field("title").unwrap();
     let body = schema.get_field("body").unwrap();
-     
+
     let mut old_man_doc = Document::default();
     old_man_doc.add_text(title, "The Old Man and the Sea");
-    old_man_doc.add_text(body, "He was an old man who fished alone in a skiff in the Gulf Stream and he had gone eighty-four days now without taking a fish.");
+ old_man_doc.add_text(body, + "He was an old man who fished alone in a skiff in the Gulf Stream and \ + he had gone eighty-four days now without taking a fish."); @@ -231,7 +231,7 @@ one by one in a Document object.

-
    try!(index_writer.add_document(old_man_doc));
+
    index_writer.add_document(old_man_doc);
@@ -248,13 +248,13 @@ a document object directly from json.

-
    
+            
     let mice_and_men_doc = try!(schema.parse_document(r#"{
        "title": "Of Mice and Men",
        "body": "few miles south of Soledad, the Salinas River drops in close to the hillside bank and runs deep and green. The water is warm too, for it has slipped twinkling over the yellow sands in the sunlight before reaching the narrow pool. On one side of the river the golden foothill slopes curve up to the strong and rocky Gabilan Mountains, but on the valley side the water is lined with trees—willows fresh and green with every spring, carrying in their lower leaf junctures the debris of the winter’s flooding; and sycamores with mottled, white,recumbent limbs and branches that arch over the pool"  
     }"#));
-    
-    try!(index_writer.add_document(mice_and_men_doc));
+ + index_writer.add_document(mice_and_men_doc);
@@ -275,7 +275,7 @@ The following document has two titles.

"title": ["Frankenstein", "The Modern Promotheus"], "body": "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings. I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking." }"#)); - try!(index_writer.add_document(frankenstein_doc)); + index_writer.add_document(frankenstein_doc); @@ -288,7 +288,7 @@ The following document has two titles.

This is an example, so we will only index 3 documents here. You can check out tantivy’s tutorial to index -the English wikipedia. Tantivy’s indexing is rather fast. +the English wikipedia. Tantivy’s indexing is rather fast. Indexing 5 million articles of the English wikipedia takes around 4 minutes on my computer!

@@ -343,15 +343,13 @@ commit.

Searching

-

Let’s search our index. We start -by creating a searcher. There can be more -than one searcher at a time.

-

You should create a searcher -every time you start a “search query”.

+

Let’s search our index. Start by reloading +searchers in the index. This should be done +after every commit().

-
    let searcher = index.searcher();
+
    try!(index.load_searchers());
@@ -362,14 +360,13 @@ every time you start a “search query”.

-

The query parser can interpret human queries. -Here, if the user does not specify which -field they want to search, tantivy will search -in both title and body.

+

Afterwards create one (or more) searchers.

+

You should create a searcher +every time you start a “search query”.

-
    let query_parser = QueryParser::new(index.schema(), vec!(title, body));
+
    let searcher = index.searcher();
@@ -380,6 +377,24 @@ in both title and body.

+

The query parser can interpret human queries. +Here, if the user does not specify which +field they want to search, tantivy will search +in both title and body.

+ + + +
    let query_parser = QueryParser::new(index.schema(), vec![title, body]);
+ + + + +
  • +
    + +
    + +

    QueryParser may fail if the query is not in the right format. For user facing applications, this can be a problem. A ticket has been opened regarding this problem.

    @@ -391,11 +406,11 @@ A ticket has been opened regarding this problem.

  • -
  • +
  • - +

    A query defines a set of documents, as well as the way they should be scored.

    @@ -408,36 +423,20 @@ any document matching at least one of our terms.

  • -
  • -
    - -
    - -
    -

    Collectors

    -

    We are not interested in all of the documents but -only in the top 10. Keeping track of our top 10 best documents -is the role of the TopCollector.

    - -
    - -
        
    -    let mut top_collector = TopCollector::with_limit(10);
    - -
  • - -
  • -

    We can now perform our query.

    +

    Collectors

    +

    We are not interested in all of the documents but +only in the top 10. Keeping track of our top 10 best documents +is the role of the TopCollector.

    -
        try!(searcher.search(&query, &mut top_collector)));
    +
        let mut top_collector = TopCollector::with_limit(10);
  • @@ -448,12 +447,11 @@ is the role of the TopCollector.

    -

    Our top collector now contains the 10 -most relevant doc ids…

    +

    We can now perform our query.

    -
        let doc_addresses = top_collector.docs();
    +
        try!(searcher.search(&*query, &mut top_collector));
    @@ -464,7 +462,23 @@ most relevant doc ids…

    -

    The actual documents still need to be +

    Our top collector now contains the 10 +most relevant doc ids…

    + + + +
        let doc_addresses = top_collector.docs();
    + + + + +
  • +
    + +
    + +
    +

    The actual documents still need to be retrieved from Tantivy’s store.

    Since the body field was not configured as stored, the document returned will only contain @@ -472,10 +486,10 @@ a title.

    -
        
    +            
         for doc_address in doc_addresses {
    -         let retrieved_doc = try!(searcher.doc(&doc_address));
    -         println!("{}", schema.to_json(&retrieved_doc));
    +        let retrieved_doc = try!(searcher.doc(&doc_address));
    +        println!("{}", schema.to_json(&retrieved_doc));
         }
     
         Ok(())
    
    From 7c114b602de85c7772b2ecb43793e1ff9fedb15a Mon Sep 17 00:00:00 2001
    From: Laurentiu Nicola 
    Date: Sat, 18 Mar 2017 16:11:10 +0200
    Subject: [PATCH 04/18] Make directory syncing work on Windows
    
    ---
     src/directory/mmap_directory.rs | 22 +++++++++++++++++++---
     1 file changed, 19 insertions(+), 3 deletions(-)
    
    diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs
    index 5015f3bb6..9b466f0dc 100644
    --- a/src/directory/mmap_directory.rs
    +++ b/src/directory/mmap_directory.rs
    @@ -219,12 +219,28 @@ impl MmapDirectory {
         /// Sync the root directory.
         /// In certain FS, this is required to persistently create
         /// a file.
    -    fn sync_directory(&self,) -> Result<(), io::Error> {
    -        let fd = try!(File::open(&self.root_path));
    +    fn sync_directory(&self) -> Result<(), io::Error> {
    +        let mut open_opts = OpenOptions::new();
    +
    +        // Linux needs read to be set, or otherwise returns EINVAL
    +        // and fails with EISDIR if write is set
    +        open_opts.read(true);
    +
    +        // On Windows, opening a directory requires FILE_FLAG_BACKUP_SEMANTICS
    +        // and calling sync_all() only works if write access is requested.
    +        #[cfg(windows)]
    +        {
    +            use std::os::windows::fs::OpenOptionsExt;
    +            const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x02000000;
    +
    +            open_opts.write(true)
    +                .custom_flags(FILE_FLAG_BACKUP_SEMANTICS);
    +        };
    +
    +        let fd = try!(open_opts.open(&self.root_path));
             try!(fd.sync_all());
             Ok(())
         }
    -
         /// Returns some statistical information
         /// about the Mmap cache.
         /// 
    
    From ebcea0128c83e6ace0fe98ff6a01a315f0cde325 Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Sun, 19 Mar 2017 11:09:15 +0900
    Subject: [PATCH 05/18] Getting the FLAG from the winapi module.
    
    ---
     Cargo.toml                      | 6 +++---
     src/directory/mmap_directory.rs | 8 +++++---
     src/lib.rs                      | 3 +++
     3 files changed, 11 insertions(+), 6 deletions(-)
    
    diff --git a/Cargo.toml b/Cargo.toml
    index 7df8882db..f6fd20cc2 100644
    --- a/Cargo.toml
    +++ b/Cargo.toml
    @@ -24,8 +24,6 @@ rustc-serialize = "0.3"
     log = "0.3.6"
     combine = "2.2"
     tempdir = "0.3"
    -
    -
     bincode = "0.5"
     libc = {version = "0.2.20", optional=true}
     num_cpus = "1.2"
    @@ -37,10 +35,12 @@ uuid = { version = "0.4", features = ["v4", "rustc-serialize"] }
     chan = "0.1"
     version = "2"
     crossbeam = "0.2"
    -
     futures = "0.1.9"
     futures-cpupool = "0.1.2"
     
    +[target.'cfg(windows)'.dependencies]
    +winapi = "*"
    +
     [dev-dependencies]
     rand = "0.3"
     
    diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs
    index 9b466f0dc..2f6224185 100644
    --- a/src/directory/mmap_directory.rs
    +++ b/src/directory/mmap_directory.rs
    @@ -23,6 +23,10 @@ use std::sync::RwLock;
     use std::sync::Weak;
     use tempdir::TempDir;
     
    +#[cfg(windows)]
    +use winapi::winbase::FILE_FLAG_BACKUP_SEMANTICS;
    +
    +
     
     fn open_mmap(full_path: &PathBuf) -> result::Result>, FileError> {
         let convert_file_error = |err: io::Error| {
    @@ -231,11 +235,9 @@ impl MmapDirectory {
             #[cfg(windows)]
             {
                 use std::os::windows::fs::OpenOptionsExt;
    -            const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x02000000;
    -
                 open_opts.write(true)
                     .custom_flags(FILE_FLAG_BACKUP_SEMANTICS);
    -        };
    +        }
     
             let fd = try!(open_opts.open(&self.root_path));
             try!(fd.sync_all());
    diff --git a/src/lib.rs b/src/lib.rs
    index 81d9acc24..46fb75abc 100644
    --- a/src/lib.rs
    +++ b/src/lib.rs
    @@ -51,6 +51,9 @@ extern crate futures_cpupool;
     #[cfg(feature="simdcompression")]
     extern crate libc;
     
    +#[cfg(windows)]
    +extern crate winapi;
    +
     #[cfg(test)] extern crate test;
     #[cfg(test)] extern crate rand;
     
    
    From 1e0ac31e11426d71238fcb86d43febf360f79e06 Mon Sep 17 00:00:00 2001
    From: Laurentiu Nicola 
    Date: Mon, 20 Mar 2017 23:12:48 +0200
    Subject: [PATCH 06/18] Clarify comment and use qualified import for the flag
    
    ---
     src/directory/mmap_directory.rs | 13 +++++--------
     1 file changed, 5 insertions(+), 8 deletions(-)
    
    diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs
    index 2f6224185..6b2730a4b 100644
    --- a/src/directory/mmap_directory.rs
    +++ b/src/directory/mmap_directory.rs
    @@ -23,11 +23,6 @@ use std::sync::RwLock;
     use std::sync::Weak;
     use tempdir::TempDir;
     
    -#[cfg(windows)]
    -use winapi::winbase::FILE_FLAG_BACKUP_SEMANTICS;
    -
    -
    -
     fn open_mmap(full_path: &PathBuf) -> result::Result>, FileError> {
         let convert_file_error = |err: io::Error| {
             if err.kind() == io::ErrorKind::NotFound {
    @@ -226,8 +221,8 @@ impl MmapDirectory {
         fn sync_directory(&self) -> Result<(), io::Error> {
             let mut open_opts = OpenOptions::new();
     
    -        // Linux needs read to be set, or otherwise returns EINVAL
    -        // and fails with EISDIR if write is set
    +        // Linux needs read to be set, otherwise returns EINVAL
    +        // write must not be set, or it fails with EISDIR
             open_opts.read(true);
     
             // On Windows, opening a directory requires FILE_FLAG_BACKUP_SEMANTICS
    @@ -235,8 +230,10 @@ impl MmapDirectory {
             #[cfg(windows)]
             {
                 use std::os::windows::fs::OpenOptionsExt;
    +            use winapi::winbase;
    +
                 open_opts.write(true)
    -                .custom_flags(FILE_FLAG_BACKUP_SEMANTICS);
    +                .custom_flags(winbase::FILE_FLAG_BACKUP_SEMANTICS);
             }
     
             let fd = try!(open_opts.open(&self.root_path));
    
    From 92ce9b906b555074f9b730b4ed300c824b974c43 Mon Sep 17 00:00:00 2001
    From: Laurentiu Nicola 
    Date: Tue, 21 Mar 2017 00:25:04 +0200
    Subject: [PATCH 07/18] Avoid using make for building simdcomp
    
    ---
     build.rs                            | 61 +++++++++++++++++------------
     src/compression/compression_simd.rs |  1 -
     2 files changed, 37 insertions(+), 25 deletions(-)
    
    diff --git a/build.rs b/build.rs
    index fb7ba8110..639bbe93f 100644
    --- a/build.rs
    +++ b/build.rs
    @@ -1,37 +1,50 @@
    -#[cfg(feature= "simdcompression")]
    +#[cfg(feature = "simdcompression")]
     mod build {
         extern crate gcc;
     
    -    use std::process::Command;
    -
         pub fn build() {
    -        Command::new("make")
    -            .current_dir("cpp/simdcomp")
    -            .output()
    -            .unwrap_or_else(|e| { panic!("Failed to make simdcomp: {}", e) });
    -        gcc::Config::new()
    -                    .flag("-O3")
    -                    .flag("-mssse3")
    -                    .include("./cpp/simdcomp/include")
    -                    .object("cpp/simdcomp/avxbitpacking.o")
    -                    .object("cpp/simdcomp/simdintegratedbitpacking.o")
    -                    .object("cpp/simdcomp/simdbitpacking.o")
    -                    .object("cpp/simdcomp/simdpackedsearch.o")
    -                    .object("cpp/simdcomp/simdcomputil.o")
    -                    .object("cpp/simdcomp/simdpackedselect.o")
    -                    .object("cpp/simdcomp/simdfor.o")
    -                    .file("cpp/simdcomp_wrapper.c")
    -                    .compile("libsimdcomp.a");
    +        let mut config = gcc::Config::new();
    +        config.include("./cpp/simdcomp/include")
    +            .file("cpp/simdcomp/src/avxbitpacking.c")
    +            .file("cpp/simdcomp/src/simdintegratedbitpacking.c")
    +            .file("cpp/simdcomp/src/simdbitpacking.c")
    +            .file("cpp/simdcomp/src/simdpackedsearch.c")
    +            .file("cpp/simdcomp/src/simdcomputil.c")
    +            .file("cpp/simdcomp/src/simdpackedselect.c")
    +            .file("cpp/simdcomp/src/simdfor.c")
    +            .file("cpp/simdcomp_wrapper.c");
    +
    +        if !cfg!(debug_assertions) {
    +            config.opt_level(3);
    +
    +            if cfg!(target_env = "msvc") {
    +                config.define("NDEBUG", None)
    +                    .flag("/Gm-")
    +                    .flag("/GS-")
    +                    .flag("/Gy")
    +                    .flag("/Oi")
    +                    .flag("/GL");
    +            } else {
    +                config.flag("-msse4.1")
    +                    .flag("-march=native");
    +            }
    +        }
    +
    +        config.compile("libsimdcomp.a");
    +
    +        // Workaround for linking static libraries built with /GL
    +        // https://github.com/rust-lang/rust/issues/26003
    +        if !cfg!(debug_assertions) && cfg!(target_env = "msvc") {
    +            println!("cargo:rustc-link-lib=dylib=simdcomp");
    +        }
         }
     }
     
    -#[cfg(not(feature= "simdcompression"))]
    +#[cfg(not(feature = "simdcompression"))]
     mod build {
    -    pub fn build() {
    -    }
    +    pub fn build() {}
     }
     
    -
     fn main() {
         build::build();
     }
    diff --git a/src/compression/compression_simd.rs b/src/compression/compression_simd.rs
    index 9b8802927..308e13445 100644
    --- a/src/compression/compression_simd.rs
    +++ b/src/compression/compression_simd.rs
    @@ -5,7 +5,6 @@ const COMPRESSED_BLOCK_MAX_SIZE: usize = NUM_DOCS_PER_BLOCK * 4 + 1;
     mod simdcomp {
         use libc::size_t;
     
    -    #[link(name = "simdcomp")]
         extern {
             pub fn compress_sorted(
                 data: *const u32,
    
    From 2d169c44542667af913fbaf1f9a4b66d205f48ce Mon Sep 17 00:00:00 2001
    From: Laurentiu Nicola 
    Date: Tue, 21 Mar 2017 07:37:28 +0200
    Subject: [PATCH 08/18] Delay deleting the files in the test suite to make it
     work on Windows
    
    ---
     src/directory/mod.rs | 60 ++++++++++++++++++++++++++++----------------
     1 file changed, 38 insertions(+), 22 deletions(-)
    
    diff --git a/src/directory/mod.rs b/src/directory/mod.rs
    index 760c2c0d5..66c3ce7c7 100644
    --- a/src/directory/mod.rs
    +++ b/src/directory/mod.rs
    @@ -60,31 +60,37 @@ mod tests {
     
         fn test_simple(directory: &mut Directory) {
             {
    -            let mut write_file = directory.open_write(*TEST_PATH).unwrap();
    -            assert!(directory.exists(*TEST_PATH));
    -            write_file.write_all(&[4]).unwrap();
    -            write_file.write_all(&[3]).unwrap();
    -            write_file.write_all(&[7,3,5]).unwrap();
    -            write_file.flush().unwrap();
    +            {
    +                let mut write_file = directory.open_write(*TEST_PATH).unwrap();
    +                assert!(directory.exists(*TEST_PATH));
    +                write_file.write_all(&[4]).unwrap();
    +                write_file.write_all(&[3]).unwrap();
    +                write_file.write_all(&[7,3,5]).unwrap();
    +                write_file.flush().unwrap();
    +            }
    +            let read_file = directory.open_read(*TEST_PATH).unwrap();
    +            let data: &[u8] = &*read_file;
    +            assert_eq!(data, &[4u8, 3u8, 7u8, 3u8, 5u8]);
             }
    -        let read_file = directory.open_read(*TEST_PATH).unwrap();
    -        let data: &[u8] = &*read_file;
    -        assert_eq!(data, &[4u8, 3u8, 7u8, 3u8, 5u8]);
    +
             assert!(directory.delete(*TEST_PATH).is_ok());
             assert!(!directory.exists(*TEST_PATH));
         }
     
         fn test_seek(directory: &mut Directory) {
             {
    -            let mut write_file = directory.open_write(*TEST_PATH).unwrap();
    -            write_file.write_all(&[4, 3, 7,3,5]).unwrap();
    -            write_file.seek(SeekFrom::Start(0)).unwrap();
    -            write_file.write_all(&[3,1]).unwrap();
    -            write_file.flush().unwrap();
    +            {
    +                let mut write_file = directory.open_write(*TEST_PATH).unwrap();
    +                write_file.write_all(&[4, 3, 7,3,5]).unwrap();
    +                write_file.seek(SeekFrom::Start(0)).unwrap();
    +                write_file.write_all(&[3,1]).unwrap();
    +                write_file.flush().unwrap();
    +            }
    +            let read_file = directory.open_read(*TEST_PATH).unwrap();
    +            let data: &[u8] = &*read_file;
    +            assert_eq!(data, &[3u8, 1u8, 7u8, 3u8, 5u8]);
             }
    -        let read_file = directory.open_read(*TEST_PATH).unwrap();
    -        let data: &[u8] = &*read_file;
    -        assert_eq!(data, &[3u8, 1u8, 7u8, 3u8, 5u8]);
    +
             assert!(directory.delete(*TEST_PATH).is_ok());
         }
     
    @@ -118,14 +124,24 @@ mod tests {
             let mut write_file = directory.open_write(*TEST_PATH).unwrap();
             write_file.write_all(&[1, 2, 3, 4]).unwrap();
             write_file.flush().unwrap();
    -        let read_handle = directory.open_read(*TEST_PATH).unwrap();  
             {
    -            assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
    -            assert!(directory.delete(*TEST_PATH).is_ok());
    -            assert!(directory.delete(Path::new("SomeOtherPath")).is_err());
    -            assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
    +            let read_handle = directory.open_read(*TEST_PATH).unwrap();
    +            {
    +                assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
    +
    +                // Mapped files can't be deleted on Windows
    +                if !cfg!(windows) {
    +                    assert!(directory.delete(*TEST_PATH).is_ok());
    +                    assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
    +                }
    +
    +                assert!(directory.delete(Path::new("SomeOtherPath")).is_err());
    +            }
             }
    +
    +        assert!(directory.delete(*TEST_PATH).is_ok());
             assert!(directory.open_read(*TEST_PATH).is_err());
    +        assert!(directory.delete(*TEST_PATH).is_err());
         }
     
         fn test_directory(directory: &mut Directory) {
    
    From 2b5a4bbde2f181ad7a6a42f922038b0ec4dfee0c Mon Sep 17 00:00:00 2001
    From: Laurentiu Nicola 
    Date: Tue, 21 Mar 2017 07:45:35 +0200
    Subject: [PATCH 09/18] Don't delete twice on not(windows)
    
    ---
     src/directory/mod.rs | 5 ++++-
     1 file changed, 4 insertions(+), 1 deletion(-)
    
    diff --git a/src/directory/mod.rs b/src/directory/mod.rs
    index 66c3ce7c7..f0bf91101 100644
    --- a/src/directory/mod.rs
    +++ b/src/directory/mod.rs
    @@ -139,7 +139,10 @@ mod tests {
                 }
             }
     
    -        assert!(directory.delete(*TEST_PATH).is_ok());
    +        if cfg!(windows) {
    +            assert!(directory.delete(*TEST_PATH).is_ok());
    +        }
    +
             assert!(directory.open_read(*TEST_PATH).is_err());
             assert!(directory.delete(*TEST_PATH).is_err());
         }
    
    From b12a97abe4558b46be7291f2c0b37bc4c42f4ae7 Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Wed, 22 Mar 2017 08:57:09 +0900
    Subject: [PATCH 10/18] Add unit test for when deleting fails
    
    Test that when delete fails, we still keep
    the file as managed.
    
    Remove the error log for windows, as failing
    to delete is expected.
    ---
     src/directory/managed_directory.rs | 33 +++++++++++++++++++++++++++++-
     1 file changed, 32 insertions(+), 1 deletion(-)
    
    diff --git a/src/directory/managed_directory.rs b/src/directory/managed_directory.rs
    index 3eefcf996..f0cf5657e 100644
    --- a/src/directory/managed_directory.rs
    +++ b/src/directory/managed_directory.rs
    @@ -86,7 +86,9 @@ impl ManagedDirectory {
                                     managed_has_changed |= managed_paths_write.remove(&file_to_delete);
                                 }
                                 FileError::IOError(_) => {
    -                                error!("Failed to delete {:?}", file_to_delete);
    +                                if !cfg!(target_os = "windows") {
    +                                    error!("Failed to delete {:?}", file_to_delete);
    +                                }
                                 }
                                 
                             }
    @@ -244,4 +246,33 @@ mod tests {
             }   
         }
     
    +    #[test]
    +    fn test_managed_directory_gc_while_mmapped() {
    +        let tempdir = TempDir::new("index").unwrap();
    +        let tempdir_path = PathBuf::from(tempdir.path());
    +        let living_files = HashSet::new();
    +
    +        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
    +        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
    +        managed_directory.atomic_write(*TEST_PATH1, &vec!(0u8,1u8)).unwrap();
    +        assert!(managed_directory.exists(*TEST_PATH1));
    +
    +        let _mmap_read = managed_directory.open_read(*TEST_PATH1).unwrap();            
    +        managed_directory.garbage_collect(living_files.clone());
    +        if cfg!(target_os = "windows") {
    +            // On Windows, gc should try and fail the file as it is mmapped.
    +            assert!(managed_directory.exists(*TEST_PATH1));
    +            // unmap should happen here.
    +            drop(_mmap_read);
    +            // The file should still be in the list of managed file and
    +            // eventually be deleted once mmap is released.
    +            managed_directory.garbage_collect(living_files);
    +            assert!(!managed_directory.exists(*TEST_PATH1));
    +        }
    +        else {
    +            assert!(!managed_directory.exists(*TEST_PATH1));
    +        }
    +
    +    }
    +
     }
    
    From b44a9cb89d456bc0addac092bcd463e386337d3f Mon Sep 17 00:00:00 2001
    From: Ashley Mannix 
    Date: Fri, 24 Mar 2017 16:11:51 +1000
    Subject: [PATCH 11/18] add appveyor config
    
    ---
     appveyor.yml | 23 +++++++++++++++++++++++
     1 file changed, 23 insertions(+)
     create mode 100644 appveyor.yml
    
    diff --git a/appveyor.yml b/appveyor.yml
    new file mode 100644
    index 000000000..d9b6472d6
    --- /dev/null
    +++ b/appveyor.yml
    @@ -0,0 +1,23 @@
    +# Appveyor configuration template for Rust using rustup for Rust installation
    +# https://github.com/starkat99/appveyor-rust
    +
    +os: Visual Studio 2017
    +
    +environment:
    +  matrix:
    +    - channel: nightly
    +      target: x86_64-pc-windows-msvc
    +    - channel: nightly
    +      target: x86_64-pc-windows-gnu
    +
    +install:
    +  - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
    +  - rustup-init -yv --default-toolchain %channel% --default-host %target%
    +  - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
    +  - rustc -vV
    +  - cargo -vV
    +
    +build: false
    +
    +test_script:
    +  - cargo test --verbose
    \ No newline at end of file
    
    From c8e12b6847295a61b0a8ea9225b2a1381cc9dc0b Mon Sep 17 00:00:00 2001
    From: Ashley Mannix 
    Date: Fri, 24 Mar 2017 16:22:32 +1000
    Subject: [PATCH 12/18] try set mingw path
    
    ---
     appveyor.yml | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/appveyor.yml b/appveyor.yml
    index d9b6472d6..f95671e9a 100644
    --- a/appveyor.yml
    +++ b/appveyor.yml
    @@ -9,11 +9,13 @@ environment:
           target: x86_64-pc-windows-msvc
         - channel: nightly
           target: x86_64-pc-windows-gnu
    +      msys_bits: 64
     
     install:
       - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
       - rustup-init -yv --default-toolchain %channel% --default-host %target%
       - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
    +  - if defined msys_bits set PATH=%PATH%;C:\msys64\mingw%msys_bits%\bin
       - rustc -vV
       - cargo -vV
     
    
    From 51cab391861045b200c4941d0e188a0757ed8bee Mon Sep 17 00:00:00 2001
    From: Ashley Mannix 
    Date: Fri, 24 Mar 2017 16:37:30 +1000
    Subject: [PATCH 13/18] drop to vs2015 image
    
    ---
     appveyor.yml | 5 ++---
     1 file changed, 2 insertions(+), 3 deletions(-)
    
    diff --git a/appveyor.yml b/appveyor.yml
    index f95671e9a..d3812cc52 100644
    --- a/appveyor.yml
    +++ b/appveyor.yml
    @@ -1,8 +1,7 @@
     # Appveyor configuration template for Rust using rustup for Rust installation
     # https://github.com/starkat99/appveyor-rust
     
    -os: Visual Studio 2017
    -
    +os: Visual Studio 2015
     environment:
       matrix:
         - channel: nightly
    @@ -22,4 +21,4 @@ install:
     build: false
     
     test_script:
    -  - cargo test --verbose
    \ No newline at end of file
    +  - cargo test --verbose
    
    From daa19b770a38f0e8cf8427b3231cd51dc8eca7eb Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Fri, 24 Mar 2017 17:42:24 +0900
    Subject: [PATCH 14/18] (hopefully) bugfix race condition on wait merging
     threadwq.
    
    ---
     src/indexer/segment_updater.rs | 10 +++++++---
     1 file changed, 7 insertions(+), 3 deletions(-)
    
    diff --git a/src/indexer/segment_updater.rs b/src/indexer/segment_updater.rs
    index 663e36609..eb9f11578 100644
    --- a/src/indexer/segment_updater.rs
    +++ b/src/indexer/segment_updater.rs
    @@ -308,7 +308,7 @@ impl SegmentUpdater {
             
         }
     
    -    pub fn wait_merging_thread(&self) -> thread::Result<()> {
    +    pub fn wait_merging_thread(&self) -> Result<()> {
             let mut new_merging_threads = HashMap::new();
             {
                 let mut merging_threads = self.0.merging_threads.write().unwrap();
    @@ -317,9 +317,13 @@ impl SegmentUpdater {
             for (_, merging_thread_handle) in new_merging_threads {
                 merging_thread_handle
                     .join()
    -                .map(|_| ())?
    +                .map(|_| ())
    +                .map_err(|_| {
    +                    Error::ErrorInThread("Merging thread failed.".to_string())
    +                })?
             }
    -        Ok(())
    +        // Our merging thread may have queued their completed
    +        self.run_async(move |_| {}).wait()
         }
     
     }
    
    From f50f557cfc0b12d5619cb99d6323daa238423cfa Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Sat, 25 Mar 2017 19:35:58 +0900
    Subject: [PATCH 15/18] issue/109 Remove futures from most of segment_updater
     API.
    
    ---
     src/indexer/index_writer.rs    | 10 +++-----
     src/indexer/segment_updater.rs | 46 ++++++++++++++++++----------------
     2 files changed, 28 insertions(+), 28 deletions(-)
    
    diff --git a/src/indexer/index_writer.rs b/src/indexer/index_writer.rs
    index 0ba779ec3..e75861d35 100644
    --- a/src/indexer/index_writer.rs
    +++ b/src/indexer/index_writer.rs
    @@ -248,10 +248,7 @@ fn index_documents(heap: &mut Heap,
         let mut segment_entry = SegmentEntry::new(segment_meta);
         segment_entry.set_doc_to_opstamp(DocToOpstampMapping::from(doc_opstamps));
     
    -    segment_updater
    -        .add_segment(generation, segment_entry)
    -        .wait()
    -        .map_err(|_| Error::ErrorInThread("Could not add segment.".to_string()))
    +    Ok(segment_updater.add_segment(generation, segment_entry))
     
     }
     
    @@ -420,7 +417,7 @@ impl IndexWriter {
             // No new document have been added in the meanwhile because `IndexWriter`
             // is not shared by different threads.
             
    -        rollback_future.wait().map_err(|_|
    +        rollback_future.map_err(|_|
                 Error::ErrorInThread("Error while waiting for rollback.".to_string())
             )?;
     
    @@ -480,8 +477,7 @@ impl IndexWriter {
     
             // wait for the segment update thread to have processed the info
             self.segment_updater
    -            .commit(self.committed_opstamp)
    -            .wait()?;
    +            .commit(self.committed_opstamp)?;
             
             self.delete_queue.clear();
             Ok(self.committed_opstamp)
    diff --git a/src/indexer/segment_updater.rs b/src/indexer/segment_updater.rs
    index eb9f11578..5a4de3ed1 100644
    --- a/src/indexer/segment_updater.rs
    +++ b/src/indexer/segment_updater.rs
    @@ -9,8 +9,9 @@ use core::SegmentMeta;
     use core::SerializableSegment;
     use directory::Directory;
     use Error;
    +use std::result;
     use futures_cpupool::CpuPool;
    -use futures::{Future, future};
    +use futures::Future;
     use futures::Canceled;
     use futures::oneshot;
     use indexer::{MergePolicy, DefaultMergePolicy};
    @@ -21,6 +22,7 @@ use indexer::merger::IndexMerger;
     use indexer::SegmentEntry;
     use indexer::SegmentSerializer;
     use Result;
    +use futures_cpupool::CpuFuture;
     use rustc_serialize::json;
     use schema::Schema;
     use std::borrow::BorrowMut;
    @@ -124,9 +126,7 @@ impl SegmentUpdater {
         pub fn new_segment(&self) -> Segment {
             let new_segment = self.0.index.new_segment();
             let segment_id = new_segment.id();
    -        self.run_async(move |segment_updater| {
    -            segment_updater.0.segment_manager.write_segment(segment_id);
    -        });
    +        self.0.segment_manager.write_segment(segment_id);
             new_segment
         }
     
    @@ -141,32 +141,32 @@ impl SegmentUpdater {
         fn get_merging_thread_id(&self) -> usize {
             self.0.merging_thread_id.fetch_add(1, Ordering::SeqCst)
         }
    -
    -
    -    fn run_async T>(&self, f: F) -> impl Future {
    +    
    +    fn run_async T>(&self, f: F) -> CpuFuture {
             let me_clone = self.clone();
             self.0.pool.spawn_fn(move || {
                 Ok(f(me_clone))
             })
         }
     
    -    pub fn rollback(&mut self, generation: usize) -> impl Future {
    +    pub fn rollback(&mut self, generation: usize) -> result::Result<(), Error> {
             self.0.generation.store(generation, Ordering::Release);
             self.run_async(|segment_updater| {
                 segment_updater.0.segment_manager.rollback();
    -        })
    +        }).wait()
         }
     
    -    pub fn add_segment(&self, generation: usize, segment_entry: SegmentEntry) -> impl Future {
    +    pub fn add_segment(&self, generation: usize, segment_entry: SegmentEntry) -> bool {
             if generation >= self.0.generation.load(Ordering::Acquire) {
    -            future::Either::A(self.run_async(|segment_updater| {
    +            self.run_async(|segment_updater| {
                     segment_updater.0.segment_manager.add_segment(segment_entry);
                     segment_updater.consider_merge_options();
                     true
    -            }))
    +            }).forget();
    +            true
             }
             else {
    -            future::Either::B(future::ok(false))
    +            false
             }
         }
     
    @@ -181,7 +181,7 @@ impl SegmentUpdater {
                 .collect()
         }
     
    -    pub fn commit(&self, opstamp: u64) -> impl Future {
    +    pub fn commit(&self, opstamp: u64) -> Result<()> {
             self.run_async(move |segment_updater| {
                 let segment_metas = segment_updater.purge_deletes().expect("Failed purge deletes");
                 segment_updater.0.segment_manager.commit(segment_metas);
    @@ -197,8 +197,7 @@ impl SegmentUpdater {
                 let living_files = segment_updater.0.segment_manager.list_files();
                 index.directory_mut().garbage_collect(living_files);
                 segment_updater.consider_merge_options();
    -            
    -        })
    +        }).wait()
         }
     
     
    @@ -266,9 +265,15 @@ impl SegmentUpdater {
                 let segment_entry = SegmentEntry::new(segment_meta);
                 segment_updater_clone
                     .end_merge(segment_metas.clone(), segment_entry.clone())
    -                .wait()
                     .unwrap();
    -            merging_future_send.complete(segment_entry.clone());
    +            
    +            // Send will fail if nobody is waiting for the result and
    +            // the receiver side got destroyed.
    +            //
    +            // This is not a problem.
    +            let _send_result = merging_future_send
    +                .send(segment_entry.clone());
    +            
                 segment_updater_clone.0.merging_threads.write().unwrap().remove(&merging_thread_id);
                 Ok(segment_entry)
             });
    @@ -293,7 +298,7 @@ impl SegmentUpdater {
         
         fn end_merge(&self, 
             merged_segment_metas: Vec,
    -        resulting_segment_entry: SegmentEntry) -> impl Future {
    +        resulting_segment_entry: SegmentEntry) -> Result<()> {
             
             self.run_async(move |segment_updater| {
                 segment_updater.0.segment_manager.end_merge(&merged_segment_metas, resulting_segment_entry);
    @@ -304,8 +309,7 @@ impl SegmentUpdater {
                     segment_updater.0.index.schema(),
                     segment_updater.0.index.opstamp(),
                     directory.borrow_mut()).expect("Could not save metas.");
    -        })
    -        
    +        }).wait()
         }
     
         pub fn wait_merging_thread(&self) -> Result<()> {
    
    From 68a956c6e75a03405e468f241715afd917eb32bc Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Sat, 25 Mar 2017 21:54:17 +0900
    Subject: [PATCH 16/18] issue/109  Showing debug! if test fails
    
    ---
     Cargo.toml                     |  1 +
     appveyor.yml                   |  2 ++
     src/indexer/index_writer.rs    | 12 ++++++++++--
     src/indexer/segment_updater.rs |  6 ++++--
     src/lib.rs                     |  3 +++
     5 files changed, 20 insertions(+), 4 deletions(-)
    
    diff --git a/Cargo.toml b/Cargo.toml
    index f6fd20cc2..85fd68405 100644
    --- a/Cargo.toml
    +++ b/Cargo.toml
    @@ -43,6 +43,7 @@ winapi = "*"
     
     [dev-dependencies]
     rand = "0.3"
    +env_logger = "0.4"
     
     [build-dependencies]
     gcc = {version = "0.3", optional=true}
    diff --git a/appveyor.yml b/appveyor.yml
    index d3812cc52..f7c19e093 100644
    --- a/appveyor.yml
    +++ b/appveyor.yml
    @@ -21,4 +21,6 @@ install:
     build: false
     
     test_script:
    +  - cargo test --no-run
    +  - SET RUST_LOG=debug
       - cargo test --verbose
    diff --git a/src/indexer/index_writer.rs b/src/indexer/index_writer.rs
    index e75861d35..24753654b 100644
    --- a/src/indexer/index_writer.rs
    +++ b/src/indexer/index_writer.rs
    @@ -263,6 +263,8 @@ impl IndexWriter {
             
             let mut v = Vec::new();
             mem::swap(&mut v, &mut self.workers_join_handle);
    +
    +        debug!("wait {} merging threads START", v.len());
             for join_handle in v {
                 try!(join_handle.join()
                     .expect("Indexing Worker thread panicked")
    @@ -272,11 +274,14 @@ impl IndexWriter {
             }
             drop(self.workers_join_handle);
     
    -        self.segment_updater
    +        let result = self.segment_updater
                 .wait_merging_thread()
                 .map_err(|_| 
                     Error::ErrorInThread("Failed to join merging thread.".to_string())
    -            )
    +            );
    +        
    +        debug!("wait merging threads DONE");
    +        result
         }
     
         /// Spawns a new worker thread for indexing.
    @@ -539,6 +544,7 @@ mod tests {
         use Index;
         use Term;
         use Error;
    +    use env_logger;
     
         #[test]
         fn test_lockfile_stops_duplicates() {
    @@ -619,6 +625,7 @@ mod tests {
     
         #[test]
         fn test_with_merges() {
    +        let _ = env_logger::init();
             let mut schema_builder = schema::SchemaBuilder::default();
             let text_field = schema_builder.add_text_field("text", schema::TEXT);
             let index = Index::create_in_ram(schema_builder.build());
    @@ -646,6 +653,7 @@ mod tests {
                 index_writer.commit().expect("commit failed");
                 index_writer.wait_merging_threads().expect("waiting merging thread failed");
                 index.load_searchers().unwrap();
    +            
                 assert_eq!(num_docs_containing("a"), 200);
                 assert_eq!(index.searchable_segments().unwrap().len(), 1);
             }
    diff --git a/src/indexer/segment_updater.rs b/src/indexer/segment_updater.rs
    index 5a4de3ed1..8dbc7eca5 100644
    --- a/src/indexer/segment_updater.rs
    +++ b/src/indexer/segment_updater.rs
    @@ -78,8 +78,9 @@ pub fn save_metas(segment_metas: Vec,
         };
         let mut w = vec!();
         try!(write!(&mut w, "{}\n", json::as_pretty_json(&metas)));
    -    Ok(directory
    -        .atomic_write(&META_FILEPATH, &w[..])?)
    +    let res = directory.atomic_write(&META_FILEPATH, &w[..])?;
    +    debug!("Saved metas {}", json::as_pretty_json(&metas));
    +    Ok(res)
             
     }
     
    @@ -301,6 +302,7 @@ impl SegmentUpdater {
             resulting_segment_entry: SegmentEntry) -> Result<()> {
             
             self.run_async(move |segment_updater| {
    +            debug!("End merge {:?}", merged_segment_metas);
                 segment_updater.0.segment_manager.end_merge(&merged_segment_metas, resulting_segment_entry);
                 let mut directory = segment_updater.0.index.directory().box_clone();
                 let segment_metas = segment_updater.0.segment_manager.committed_segment_metas();
    diff --git a/src/lib.rs b/src/lib.rs
    index 46fb75abc..f16305500 100644
    --- a/src/lib.rs
    +++ b/src/lib.rs
    @@ -48,6 +48,9 @@ extern crate bit_set;
     extern crate futures;
     extern crate futures_cpupool;
     
    +#[cfg(test)]
    +extern crate env_logger;
    +
     #[cfg(feature="simdcompression")]
     extern crate libc;
     
    
    From 84a060552da0837902ddd770a00de5ff19989261 Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Sat, 25 Mar 2017 22:34:34 +0900
    Subject: [PATCH 17/18] issue/109 trying to get proper logging in appveyor
    
    ---
     appveyor.yml | 4 +---
     1 file changed, 1 insertion(+), 3 deletions(-)
    
    diff --git a/appveyor.yml b/appveyor.yml
    index f7c19e093..fe19f58e5 100644
    --- a/appveyor.yml
    +++ b/appveyor.yml
    @@ -21,6 +21,4 @@ install:
     build: false
     
     test_script:
    -  - cargo test --no-run
    -  - SET RUST_LOG=debug
    -  - cargo test --verbose
    +  - REM SET RUST_LOG=tantivy,test & SET RUST_TEST_THREADS=1 & cargo test --verbose
    \ No newline at end of file
    
    From 45806951b1f00c7a66fd8c72232a17a1bb3b322b Mon Sep 17 00:00:00 2001
    From: Paul Masurel 
    Date: Sat, 25 Mar 2017 22:43:07 +0900
    Subject: [PATCH 18/18] added quotation mark
    
    ---
     appveyor.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/appveyor.yml b/appveyor.yml
    index fe19f58e5..789e24400 100644
    --- a/appveyor.yml
    +++ b/appveyor.yml
    @@ -21,4 +21,4 @@ install:
     build: false
     
     test_script:
    -  - REM SET RUST_LOG=tantivy,test & SET RUST_TEST_THREADS=1 & cargo test --verbose
    \ No newline at end of file
    +  - REM SET RUST_LOG=tantivy,test & cargo test --verbose
    \ No newline at end of file