Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stable JSON output after replacing HashMaps with BTreeMaps #15

Merged
merged 1 commit into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 42 additions & 42 deletions src/document_store.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//! Implements an elasticlunr.js document store. Most users do not need to use this module directly.

use std::collections::HashMap;
use std::collections::BTreeMap;

/// The document store saves the complete text of each item saved to the index, if enabled.
/// Most users do not need to use this type directly.
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DocumentStore {
pub save: bool,
pub docs: HashMap<String, HashMap<String, String>>,
pub doc_info: HashMap<String, HashMap<String, usize>>,
pub docs: BTreeMap<String, BTreeMap<String, String>>,
pub doc_info: BTreeMap<String, BTreeMap<String, usize>>,
// Redundant with docs.len(), but needed for serialization
pub length: usize,
}
Expand All @@ -18,8 +18,8 @@ impl DocumentStore {
pub fn new(save: bool) -> Self {
DocumentStore {
save: save,
docs: HashMap::new(),
doc_info: HashMap::new(),
docs: BTreeMap::new(),
doc_info: BTreeMap::new(),
length: 0,
}
}
Expand All @@ -36,16 +36,16 @@ impl DocumentStore {
self.docs.contains_key(doc_ref)
}

pub fn add_doc(&mut self, doc_ref: &str, doc: HashMap<String, String>) {
pub fn add_doc(&mut self, doc_ref: &str, doc: BTreeMap<String, String>) {
if !self.has_doc(doc_ref) {
self.length += 1;
}

self.docs
.insert(doc_ref.into(), if self.save { doc } else { HashMap::new() });
.insert(doc_ref.into(), if self.save { doc } else { BTreeMap::new() });
}

pub fn get_doc(&self, doc_ref: &str) -> Option<HashMap<String, String>> {
pub fn get_doc(&self, doc_ref: &str) -> Option<BTreeMap<String, String>> {
self.docs.get(doc_ref.into()).cloned()
}

Expand All @@ -60,7 +60,7 @@ impl DocumentStore {
pub fn add_field_length(&mut self, doc_ref: &str, field: &str, length: usize) {
self.doc_info
.entry(doc_ref.into())
.or_insert(HashMap::new())
.or_insert(BTreeMap::new())
.insert(field.into(), length);
}

Expand All @@ -84,7 +84,7 @@ mod tests {
#[test]
fn add_doc_tokens() {
let mut store = DocumentStore::new(true);
let doc = hashmap!{ "title".into() => "eggs bread".into() };
let doc = btreemap!{ "title".into() => "eggs bread".into() };

store.add_doc("1", doc.clone());
assert_eq!(store.get_doc("1").unwrap(), doc);
Expand All @@ -93,7 +93,7 @@ mod tests {
#[test]
fn create_doc_no_store() {
let mut store = DocumentStore::new(false);
let doc = hashmap!{ "title".into() => "eggs bread".into() };
let doc = btreemap!{ "title".into() => "eggs bread".into() };

store.add_doc("1", doc);
assert_eq!(store.len(), 1);
Expand All @@ -104,8 +104,8 @@ mod tests {
#[test]
fn add_doc_no_store() {
let mut store = DocumentStore::new(false);
let doc1 = hashmap!{ "title".into() => "eggs bread".into() };
let doc2 = hashmap!{ "title".into() => "hello world".into() };
let doc1 = btreemap!{ "title".into() => "eggs bread".into() };
let doc2 = btreemap!{ "title".into() => "hello world".into() };

store.add_doc("1", doc1);
store.add_doc("2", doc2);
Expand All @@ -130,67 +130,67 @@ mod tests {
#[test]
fn get_doc_no_store() {
let mut store = DocumentStore::new(false);
let doc1 = hashmap!{ "title".into() => "eggs bread".into() };
let doc2 = hashmap!{ "title".into() => "hello world".into() };
let doc1 = btreemap!{ "title".into() => "eggs bread".into() };
let doc2 = btreemap!{ "title".into() => "hello world".into() };

store.add_doc("1", doc1);
store.add_doc("2", doc2);
assert_eq!(store.len(), 2);
assert_eq!(store.is_stored(), false);
assert_eq!(store.get_doc("1").unwrap(), HashMap::new());
assert_eq!(store.get_doc("2").unwrap(), HashMap::new());
assert_eq!(store.get_doc("1").unwrap(), BTreeMap::new());
assert_eq!(store.get_doc("2").unwrap(), BTreeMap::new());
}

#[test]
fn get_nonexistant_doc_no_store() {
let mut store = DocumentStore::new(false);
let doc1 = hashmap!{ "title".into() => "eggs bread".into() };
let doc2 = hashmap!{ "title".into() => "hello world".into() };
let doc1 = btreemap!{ "title".into() => "eggs bread".into() };
let doc2 = btreemap!{ "title".into() => "hello world".into() };

store.add_doc("1", doc1);
store.add_doc("2", doc2);
assert_eq!(store.len(), 2);
assert_eq!(store.is_stored(), false);
assert_eq!(store.get_doc("6"), None);
assert_eq!(store.get_doc("2").unwrap(), HashMap::new());
assert_eq!(store.get_doc("2").unwrap(), BTreeMap::new());
}

#[test]
fn remove_doc_no_store() {
let mut store = DocumentStore::new(false);
let doc1 = hashmap!{ "title".into() => "eggs bread".into() };
let doc2 = hashmap!{ "title".into() => "hello world".into() };
let doc1 = btreemap!{ "title".into() => "eggs bread".into() };
let doc2 = btreemap!{ "title".into() => "hello world".into() };

store.add_doc("1", doc1);
store.add_doc("2", doc2);
store.remove_doc("1");
assert_eq!(store.len(), 1);
assert_eq!(store.is_stored(), false);
assert_eq!(store.get_doc("2").unwrap(), HashMap::new());
assert_eq!(store.get_doc("2").unwrap(), BTreeMap::new());
assert_eq!(store.get_doc("1"), None);
}

#[test]
fn remove_nonexistant_doc() {
let mut store = DocumentStore::new(false);
let doc1 = hashmap!{ "title".into() => "eggs bread".into() };
let doc2 = hashmap!{ "title".into() => "hello world".into() };
let doc1 = btreemap!{ "title".into() => "eggs bread".into() };
let doc2 = btreemap!{ "title".into() => "hello world".into() };

store.add_doc("1", doc1);
store.add_doc("2", doc2);
store.remove_doc("8");
assert_eq!(store.len(), 2);
assert_eq!(store.is_stored(), false);
assert_eq!(store.get_doc("2").unwrap(), HashMap::new());
assert_eq!(store.get_doc("1").unwrap(), HashMap::new());
assert_eq!(store.get_doc("2").unwrap(), BTreeMap::new());
assert_eq!(store.get_doc("1").unwrap(), BTreeMap::new());
}

#[test]
fn get_num_docs() {
let mut store = DocumentStore::new(true);

assert_eq!(store.len(), 0);
store.add_doc("1", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("1", btreemap!{ "title".into() => "eggs bread".into() });
assert_eq!(store.len(), 1);
}

Expand All @@ -199,10 +199,10 @@ mod tests {
let mut store = DocumentStore::new(true);

assert_eq!(store.len(), 0);
store.add_doc("1", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("1", btreemap!{ "title".into() => "eggs bread".into() });
assert_eq!(
store.get_doc("1").unwrap(),
hashmap!{ "title".into() => "eggs bread".into() }
btreemap!{ "title".into() => "eggs bread".into() }
);
}

Expand All @@ -213,26 +213,26 @@ mod tests {
assert_eq!(store.len(), 0);
store.add_doc(
"1",
hashmap!{
btreemap!{
"title".into() => "eggs bread".into()
},
);
store.add_doc(
"2",
hashmap!{
btreemap!{
"title".into() => "boo bar".into()
},
);
store.add_doc(
"3",
hashmap!{
btreemap!{
"title".into() => "oracle".into(),
"body".into() => "Oracle is demonspawn".into()
},
);
assert_eq!(
store.get_doc("3").unwrap(),
hashmap!{
btreemap!{
"title".into() => "oracle".into(),
"body".into() => "Oracle is demonspawn".into()
}
Expand All @@ -247,19 +247,19 @@ mod tests {
assert_eq!(store.len(), 0);
store.add_doc(
"1",
hashmap!{
btreemap!{
"title".into() => "eggs bread".into()
},
);
store.add_doc(
"2",
hashmap!{
btreemap!{
"title".into() => "boo bar".into()
},
);
store.add_doc(
"3",
hashmap!{
btreemap!{
"title".into() => "oracle".into(),
"body".into() => "Oracle is demonspawn".into()
},
Expand All @@ -274,15 +274,15 @@ mod tests {
let mut store = DocumentStore::new(true);

assert!(!store.has_doc("foo"));
store.add_doc("foo", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("foo", btreemap!{ "title".into() => "eggs bread".into() });
assert!(store.has_doc("foo"));
}

#[test]
fn remove_doc() {
let mut store = DocumentStore::new(true);

store.add_doc("foo", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("foo", btreemap!{ "title".into() => "eggs bread".into() });
assert!(store.has_doc("foo"));
assert_eq!(store.len(), 1);
store.remove_doc("foo");
Expand All @@ -294,7 +294,7 @@ mod tests {
fn remove_nonexistant_store() {
let mut store = DocumentStore::new(true);

store.add_doc("foo", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("foo", btreemap!{ "title".into() => "eggs bread".into() });
assert!(store.has_doc("foo"));
assert_eq!(store.len(), 1);
store.remove_doc("bar");
Expand All @@ -306,7 +306,7 @@ mod tests {
fn add_field_len() {
let mut store = DocumentStore::new(true);

store.add_doc("foo", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("foo", btreemap!{ "title".into() => "eggs bread".into() });
store.add_field_length("foo", "title", 2);
assert_eq!(store.get_field_length("foo", "title"), 2);
}
Expand All @@ -315,7 +315,7 @@ mod tests {
fn add_field_length_multiple() {
let mut store = DocumentStore::new(true);

store.add_doc("foo", hashmap!{ "title".into() => "eggs bread".into() });
store.add_doc("foo", btreemap!{ "title".into() => "eggs bread".into() });
store.add_field_length("foo", "title", 2);
store.add_field_length("foo", "body", 10);
assert_eq!(store.get_field_length("foo", "title"), 2);
Expand Down
26 changes: 13 additions & 13 deletions src/inverted_index.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Implements an elasticlunr.js inverted index. Most users do not need to use this module directly.

use std::collections::HashMap;
use std::collections::BTreeMap;

#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
struct TermFrequency {
Expand All @@ -10,19 +10,19 @@ struct TermFrequency {

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
struct IndexItem {
pub docs: HashMap<String, TermFrequency>,
pub docs: BTreeMap<String, TermFrequency>,
#[serde(rename = "df")]
pub doc_freq: i64,
#[serde(flatten)]
pub children: HashMap<String, IndexItem>,
pub children: BTreeMap<String, IndexItem>,
}

impl IndexItem {
fn new() -> Self {
IndexItem {
docs: HashMap::new(),
docs: BTreeMap::new(),
doc_freq: 0,
children: HashMap::new(),
children: BTreeMap::new(),
}
}

Expand Down Expand Up @@ -105,7 +105,7 @@ impl InvertedIndex {
self.root.remove_token(doc_ref, token)
}

pub fn get_docs(&self, token: &str) -> Option<HashMap<String, f64>> {
pub fn get_docs(&self, token: &str) -> Option<BTreeMap<String, f64>> {
self.root.get_node(token).map(|node| {
node.docs
.iter()
Expand Down Expand Up @@ -222,19 +222,19 @@ mod tests {
inverted_index.add_token("123", token, 1.);
assert_eq!(
inverted_index.get_docs(token).unwrap(),
hashmap!{
btreemap!{
"123".into() => 1.
}
);

assert_eq!(inverted_index.get_docs(""), Some(HashMap::new()));
assert_eq!(inverted_index.get_docs(""), Some(BTreeMap::new()));

inverted_index.add_token("234", "boo", 100.);
inverted_index.add_token("345", "too", 101.);

assert_eq!(
inverted_index.get_docs(token).unwrap(),
hashmap!{
btreemap!{
"123".into() => 1.
}
);
Expand All @@ -244,7 +244,7 @@ mod tests {

assert_eq!(
inverted_index.get_docs(token).unwrap(),
hashmap!{
btreemap!{
"123".into() => 1.,
"234".into() => 100.,
"345".into() => 101.,
Expand Down Expand Up @@ -285,13 +285,13 @@ mod tests {
inverted_index.add_token("123", "foo", 1.);
assert_eq!(
inverted_index.get_docs("foo").unwrap(),
hashmap!{
btreemap!{
"123".into() => 1.,
}
);

inverted_index.remove_token("123", "foo");
assert_eq!(inverted_index.get_docs("foo"), Some(HashMap::new()));
assert_eq!(inverted_index.get_docs("foo"), Some(BTreeMap::new()));
assert_eq!(inverted_index.get_doc_frequency("foo"), 0);
assert_eq!(inverted_index.has_token("foo"), true);
}
Expand All @@ -306,7 +306,7 @@ mod tests {

assert_eq!(
inverted_index.get_docs("foo").unwrap(),
hashmap!{
btreemap!{
"123".into() => 1.
}
);
Expand Down
Loading