1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! A simple library for searching objects.
//! # Basic Usage
//!
//! ```rust
//! use simple_search::search_engine::SearchEngine;
//! use simple_search::levenshtein::base::weighted_levenshtein_similarity;
//!
//!fn main() {
//!     let engine = SearchEngine::new()
//!         .with_values(vec!["hello", "world", "foo", "bar"])
//!         .with(|v, q| weighted_levenshtein_similarity(v, q));
//!
//!     let results = engine.search("hallo");
//!
//!     println!("search for hallo: {:?}", results);
//!}
//! ```
//!
//! # Advanced Usage
//! The following example shows how to use the library with a custom type.
//! The [SearchEngine](simple_search::search_engine::SearchEngine) is configured to search for books by title, author and description.
//! Each of those is weighted differently and the [IncrementalLevenshtein](simple_search::levenshtein::incremental::IncrementalLevenshtein) is used to calculate the similarity.
//!
//!```rust
//!use simple_search::search_engine::SearchEngine;
//!use simple_search::levenshtein::incremental::IncrementalLevenshtein;
//!
//!#[derive(Debug)]
//!struct Book {
//!    title: String,
//!    description: String,
//!    author: String,
//!}
//!
//!fn main() {
//!    let book1 = Book {
//!        title: "The Winds of Winter".to_string(),
//!        description: "The sixth book in the A Song of Ice and Fire series.".to_string(),
//!        author: "George R. R. Martin".to_string(),
//!    };
//!
//!    let book2 = Book {
//!        title: "The Great Gatsby".to_string(),
//!        description: "A classic novel of the roaring twenties.".to_string(),
//!        author: "F. Scott Fitzgerald".to_string(),
//!    };
//!
//!    let book3 = Book {
//!        title: "Brave New World".to_string(),
//!        description: "A visionary and disturbing novel about a dystopian future.".to_string(),
//!        author: "Aldous Huxley".to_string(),
//!    };
//!
//!    let book4 = Book {
//!        title: "To Kill a Mockingbird".to_string(),
//!        description: "A novel that deals with issues like injustice and moral growth.".to_string(),
//!        author: "Harper Lee".to_string(),
//!    };
//!
//!    let engine = SearchEngine::new()
//!        .with_values(vec![book1, book2, book3, book4])
//!        .with_state(
//!            |book| IncrementalLevenshtein::new("", &book.title),
//!            |s, _, q| s.weighted_similarity(q),
//!        )
//!        .with_state_and_weight(
//!            0.8,
//!            |book| IncrementalLevenshtein::new("", &book.author),
//!            |s, _, q| s.weighted_similarity(q),
//!        )
//!        .with_state_and_weight(
//!            0.5,
//!            |book| IncrementalLevenshtein::new("", &book.description),
//!            |s, _, q| s.weighted_similarity(q),
//!        );
//!
//!    let mut engine = engine.erase_type();
//!
//!    let results = engine.similarities("Fire adn water");
//!
//!    println!("search for Fire and Ice:");
//!    for result in results {
//!        println!("{:?}", result);
//!    }
//!
//!    println!();
//!
//!    let results = engine.similarities("Fitzereld");
//!
//!    println!("Fitzgerald");
//!    for result in results {
//!        println!("{:?}", result);
//!    }
//!
//!    println!();
//!}
//!```
//!
//! # Storing an engine
//!
//! The [SearchEngine](simple_search::search_engine::SearchEngine) most often has a very complicated type, that can't easily be expressed.
//! To work around this, the [type_erasure](simple_search::type_erasure) module provides a way to store the engine, by using a trait object in a [Box](std::boxed::Box). \
//! This solution is not ideal, as it requires dynamic dispatch, but the overhead is minimal
//! Once the approved [RFC 2515](https://rust-lang.github.io/impl-trait-initiative/RFC.html) is part of stable rust, this will be replaced with a more elegant solution.
//! For more details on this see the [type_erasure](simple_search::type_erasure) module.
//!
//!```rust
//! use simple_search::search_engine::SearchEngine;
//! use simple_search::levenshtein::incremental::IncrementalLevenshtein;
//! use simple_search::type_erasure::non_cloneable::MutableSearchEngine;
//!
//! fn main() {
//!     let engine = SearchEngine::new()
//!         .with_values(vec!["hello", "world", "foo", "bar"])
//!         .with_state(
//!                 |v| IncrementalLevenshtein::new("", v),
//!                 |s, _, q| s.weighted_similarity(q),
//!         );
//!
//!     let mut engine: MutableSearchEngine<&str, str> = engine.erase_type();
//!     
//!     let results = engine.search("hallo");
//!
//!     println!("search for hallo: {:?}", results);
//! }
//! ```
//!
//! # Parallelization
//!
//! The [SearchEngine](simple_search::search_engine::SearchEngine) can be used in parallel, using [rayon](https://docs.rs/rayon/latest/rayon/) iterators.
//! This simply involved calling the parallel version of the respective function \
//! (As long as the values and query are [Send] + [Sync]).
//!
//! ```rust
//! use simple_search::search_engine::SearchEngine;
//! use simple_search::levenshtein::base::weighted_levenshtein_similarity;
//!
//!fn main() {
//!     let engine = SearchEngine::new()
//!         .with_values(vec!["hello", "world", "foo", "bar"])
//!         .with(|v, q| weighted_levenshtein_similarity(v, q));
//!
//!     let results = engine.par_search("hallo");
//!
//!     println!("search for hallo: {:?}", results);
//!}
//! ```

pub mod levenshtein;
pub mod search_engine;
pub mod similarity;
pub mod type_erasure;