{"id":13688428,"url":"https://github.com/leontoeides/indicium","last_synced_at":"2026-03-05T19:38:23.747Z","repository":{"id":41207716,"uuid":"403449752","full_name":"leontoeides/indicium","owner":"leontoeides","description":"A simple in-memory search for collections and key-value stores.","archived":false,"fork":false,"pushed_at":"2025-02-09T20:53:45.000Z","size":796,"stargazers_count":72,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-10T19:24:19.497Z","etag":null,"topics":["autocomplete","btreemap","collection","collections","hashmap","predictive","rust","search","struct","text","typeahead","vec"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/indicium","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leontoeides.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-06T01:23:22.000Z","updated_at":"2025-03-28T05:19:58.000Z","dependencies_parsed_at":"2024-05-13T02:24:59.905Z","dependency_job_id":"57f1e2a8-5fdf-46c0-90f4-1341bc036084","html_url":"https://github.com/leontoeides/indicium","commit_stats":{"total_commits":220,"total_committers":2,"mean_commits":110.0,"dds":"0.013636363636363669","last_synced_commit":"1b575e73b25c356e4c5cf8ace6e6b02ed505131f"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leontoeides%2Findicium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leontoeides%2Findicium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leontoeides%2Findicium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leontoeides%2Findicium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leontoeides","download_url":"https://codeload.github.com/leontoeides/indicium/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251932626,"owners_count":21667183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autocomplete","btreemap","collection","collections","hashmap","predictive","rust","search","struct","text","typeahead","vec"],"created_at":"2024-08-02T15:01:13.587Z","updated_at":"2026-03-05T19:38:18.688Z","avatar_url":"https://github.com/leontoeides.png","language":"Rust","readme":"# indicium\r\n![Crates.io Version](https://img.shields.io/crates/v/indicium)\r\n![Crates.io MSRV](https://img.shields.io/crates/msrv/indicium)\r\n![Crates.io License](https://img.shields.io/crates/l/indicium)\r\n![Crates.io Total Downloads](https://img.shields.io/crates/d/indicium)\r\n\r\nA simple in-memory search for collections (`Vec`, `HashMap`, `BTreeMap`, etc.)\r\nand key-value stores. Features autocompletion and fuzzy matching.\r\n\r\nThere are many incredible search engines available for Rust. Many seem to\r\nrequire compiling a separate server binary. I wanted something simple and\r\nlight-weight - an easy-to-use crate that could conveniently search structs and\r\ncollections within my own binary. So, I made `indicium`.\r\n\r\n\u003cimg src=\"https://www.arkiteq.io/crates/indicium/banner.jpg\" alt=\"Indicium: A Simple In-Memory Search for Rust\" width=\"400\"/\u003e\r\n\r\nWhile `indicium` was made with web apps in mind, it is an in-memory search and\r\nit does not scale indefinitely or to cloud size (i.e. Facebook or Google size).\r\nEven in such an environment, it would still be a convenient way of searching\r\nlarge lists (such as currencies, languages, countries, etc.) It's also great for\r\napplications where there is an anticipated scale limit (i.e. searching a list of\r\ncompany assets, list of users in a corporate intranet, etc.)\r\n\r\nIndicium easily can handle millions of records without breaking a sweat thanks\r\nto Rust's [BTreeMap](https://cglab.ca/~abeinges/blah/rust-btree-case/). This\r\ncrate is primarily limited by available memory. However, depending on the nature\r\nyour data-set and if there are keywords that are repeated many times,\r\nperformance may begin to degrade at a point.\r\n\r\n# Installation\r\n\r\nConfigure the dependencies in your project's `Cargo.toml` file:\r\n\r\n```toml\r\n[dependencies]\r\nindicium = \"0.6\"\r\n```\r\n\r\n# Release Notes\r\n\r\n* Release notes are available on\r\n  [GitHub](https://github.com/leontoeides/indicium/releases).\r\n\r\n* The full [change\r\n  log](https://github.com/leontoeides/indicium/blob/master/CHANGELOG.md) is\r\n  available on GitHub.\r\n\r\n# Quick Start Guide\r\n\r\nFor our **Quick Start Guide** example, we will be searching inside of the\r\nfollowing `struct`:\r\n\r\n```rust\r\nstruct MyStruct {\r\n    title: String,\r\n    year: u16,\r\n    body: String,\r\n}\r\n```\r\n\r\n## 1. Implementing Indexable\r\n\r\nTo begin, we must make our record indexable. We'll do this by implementing the\r\n`Indexable` trait for our `struct`. The idea is to return a `String` for every\r\nfield that we would like to be indexed. Example:\r\n\r\n```rust\r\nuse indicium::simple::Indexable;\r\n\r\nimpl Indexable for MyStruct {\r\n    fn strings(\u0026self) -\u003e Vec\u003cString\u003e {\r\n        vec![\r\n            self.title.clone(),\r\n            self.year.to_string(),\r\n            self.body.clone(),\r\n        ]\r\n    }\r\n}\r\n```\r\n\r\nDon't forget that you may make numbers, numeric identifiers, enums, and other\r\ntypes in your `struct` (or other complex types) indexable by converting them to\r\na `String` and including them in the returned `Vec\u003cString\u003e`.\r\n\r\n## 2. Indexing a Collection\r\n\r\nTo index an existing collection, we can iterate over the collection. For each\r\nrecord, we will insert it into the search index. This should look something\r\nlike these two examples:\r\n\r\n#### Vec\r\n\r\n```rust\r\nuse indicium::simple::SearchIndex;\r\n\r\nlet my_vec: Vec\u003cMyStruct\u003e = Vec::new();\r\n\r\n// In the case of a `Vec` collection, we use the index as our key. A\r\n// `Vec` index is a `usize` type. Therefore we will instantiate\r\n// `SearchIndex` as `SearchIndex\u003cusize\u003e`.\r\n\r\nlet mut search_index: SearchIndex\u003cusize\u003e = SearchIndex::default();\r\n\r\nmy_vec\r\n    .iter()\r\n    .enumerate()\r\n    .for_each(|(index, element)|\r\n        search_index.insert(\u0026index, element)\r\n    );\r\n```\r\n\r\n#### HashMap\r\n\r\n```rust\r\nuse std::collections::HashMap;\r\nuse indicium::simple::SearchIndex;\r\n\r\nlet my_hash_map: HashMap\u003cString, MyStruct\u003e = HashMap::new();\r\n\r\n// In the case of a `HashMap` collection, we use the hash map's key as\r\n// the `SearchIndex` key. In our hypothetical example, we will use\r\n// MyStruct's `title` as a the key which is a `String` type. Therefore\r\n// we will instantiate `HashMap\u003cK, V\u003e` as HashMap\u003cString, MyStruct\u003e and\r\n// `SearchIndex\u003cK\u003e` as `SearchIndex\u003cString\u003e`.\r\n\r\nlet mut search_index: SearchIndex\u003cString\u003e = SearchIndex::default();\r\n\r\nmy_hash_map\r\n    .iter()\r\n    .for_each(|(key, value)|\r\n        search_index.insert(key, value)\r\n    );\r\n```\r\n\r\nAs long as the `Indexable` trait was implemented for your value type, the above\r\nexamples will index a previously populated `Vec` or `HashMap`. However, the\r\npreferred method for large collections is to `insert` into the `SearchIndex` as\r\nyou insert into your collection (Vec, HashMap, etc.)\r\n\r\nIt's recommended to wrap your target collection (your `Vec`, `HashMap`, etc.)\r\nand this `SearchIndex` together in a new `struct` type. Then, implement the\r\n`insert`, `replace`, `remove`, etc. methods for this new `struct` type that will\r\nupdate both the collection and search index. This will ensure that both your\r\ncollection and index are always synchronized.\r\n\r\nOnce the index has been populated, you can use the `search` and `autocomplete`\r\nmethods.\r\n\r\n## 3. Searching\r\n\r\nThe `search` method will return keys as the search results. Each resulting\r\nkey can then be used to retrieve the full record from its collection.\r\n\r\nBasic usage:\r\n\r\n```rust\r\nlet mut search_index: SearchIndex\u003cusize\u003e = SearchIndex::default();\r\n\r\nsearch_index.insert(\u00260, \u0026\"Harold Godwinson\");\r\nsearch_index.insert(\u00261, \u0026\"Edgar Ætheling\");\r\nsearch_index.insert(\u00262, \u0026\"William the Conqueror\");\r\nsearch_index.insert(\u00263, \u0026\"William Rufus\");\r\nsearch_index.insert(\u00264, \u0026\"Henry Beauclerc\");\r\n\r\nlet resulting_keys: Vec\u003c\u0026usize\u003e = search_index.search(\"William\");\r\n\r\nassert_eq!(resulting_keys, vec![\u00262, \u00263]);\r\n\r\n// Demonstrating fuzzy matching:\r\n\r\nlet resulting_keys: Vec\u003c\u0026usize\u003e = search_index.search(\"Harry\");\r\n\r\nassert_eq!(resulting_keys, vec![\u00260]);\r\n```\r\n\r\nSearch only supports exact keyword matches. For `Live` searches, fuzzy matching\r\nis only applied to the last keyword. Consider providing the `autocomplete`\r\nfeature to your users to help them build their search as they type.\r\n\r\n## 4. Autocompletion\r\n\r\nThe `autocomplete` method will provide several autocompletion options for the\r\nlast keyword in the supplied string.\r\n\r\nBasic usage:\r\n\r\n```rust\r\nlet mut search_index: SearchIndex\u003cusize\u003e =\r\n    SearchIndexBuilder::default()\r\n        .autocomplete_type(\u0026AutocompleteType::Global)\r\n        .build();\r\n\r\nsearch_index.insert(\u00260, \u0026\"apple\");\r\nsearch_index.insert(\u00261, \u0026\"ball\");\r\nsearch_index.insert(\u00263, \u0026\"bird\");\r\nsearch_index.insert(\u00264, \u0026\"birthday\");\r\nsearch_index.insert(\u00265, \u0026\"red\");\r\n\r\nlet autocomplete_options: Vec\u003cString\u003e =\r\n    search_index.autocomplete(\"a very big bi\");\r\n\r\nassert_eq!(\r\n    autocomplete_options,\r\n    vec![\"a very big bird\", \"a very big birthday\"]\r\n);\r\n\r\n// Demonstrating fuzzy matching:\r\n\r\nlet autocomplete_options: Vec\u003cString\u003e =\r\n    search_index.autocomplete(\"a very big birf\");\r\n\r\nassert_eq!(\r\n    autocomplete_options,\r\n    vec![\"a very big bird\", \"a very big birthday\"]\r\n);\r\n```\r\n\r\n# Crate Status\r\n\r\nThis crate is passively maintained. This crate does what it's expected to do and\r\ndoes it pretty well, in my opinion. Frequent updates are not expected.","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleontoeides%2Findicium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleontoeides%2Findicium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleontoeides%2Findicium/lists"}