9个版本
0.1.67 | 2023年8月28日 |
---|---|
0.1.66 | 2023年8月28日 |
0.1.65 | 2023年6月14日 |
0.1.63 |
|
#63 在 缓存
每月100次下载
160KB
3.5K SLoC
Flinch
Flinch是一个内存中的实时文档数据库,专为快速、高效的全文搜索和查询而设计。它提供了一个高性能的搜索解决方案,可以无缝集成到各种应用程序中。Flinch轻量级、易于使用且开源,使用户能够参与其开发并按需定制。
特性
- 内存数据库:Flinch将文档存储在内存中,从而实现超快的搜索性能。
- 实时更新:用户可以实时添加、更新和删除文档,确保数据库反映了最新的更改。
- 全文搜索:Flinch内置全文搜索引擎,提供强大的搜索功能,包括“边打字边搜索”和通配符搜索。
- 轻量级且易于使用:Flinch设计轻量级,具有简单的API,便于开发人员将其集成到应用程序中。
- 面向文档:Flinch是一个面向文档的数据库,允许用户将文档作为JSON对象存储和检索。
- 高度可扩展:Flinch旨在高效处理大量文档和查询,确保随着应用程序的增长而具有可扩展性。
- 查询:Flinch提供高速文档查询功能,查询性能快如闪电(⚡️)。
- 开源:Flinch是一个开源项目,使用户能够参与其开发并根据其要求进行定制。
注意:由于索引所需的时间,插入性能可能较慢,这是正常现象。
如何使用
运行 👇它
cargoadd flinch
或者在你的Cargo.toml中添加以下内容
flinch= "<YOUR_VERSION>"
以下示例演示了Flinch作为库和查询语言的功能,展示了其文档存储、检索、操作和搜索的特性。
示例1:将Flinch作为库使用
第一个示例演示了如何在Rust应用程序中将Flinch作为库使用。它展示了各种操作,如添加文档、更新值、检索数据、执行搜索等。
以下是代码中执行的关键操作
-
创建集合:创建具有特定选项的集合,包括索引选项、搜索选项、视图选项、范围选项和剪辑选项。
-
添加文档:使用
put
操作将文档添加到集合中。每个文档表示为一个 JSON 对象,并分配一个唯一的键。 -
实时更新:代码通过订阅一个频道并接收文档插入和删除的事件来演示实时更新。
-
检索文档:代码展示了如何使用
get
操作检索文档。包括获取单个文档、获取多个文档和基于索引获取文档的示例。 -
搜索:代码展示了 Flinch 的搜索功能。展示了如何执行搜索查询并检索匹配搜索查询的文档。还展示了使用通配符字符的“模糊”搜索。
-
视图:Flinch 支持视图,这是可以应用于集合的预定义过滤器。代码展示了如何根据视图配置获取文档。
-
删除集合:代码演示了如何删除集合,从而删除其所有文档。
-
订阅 Pub/Sub 事件:代码设置了一个订阅以接收文档插入和删除的 Pub/Sub 事件。它展示了监听有限数量事件的示例。
async fn library() {
// Initialize Flinch with collection options
let col_opts = CollectionOptions {
name: Some(COLLECTION.to_string()),
index_opts: vec![format!("name")],
search_opts: vec![format!("name")],
view_opts: vec![ViewConfig{
prop: "age".to_string(),
expected: "18".to_string(),
view_name: "ADULT".to_string(),
}],
range_opts: vec![format!("age")],
clips_opts: vec![format!("name")],
};
let database = Database::init();
database.add(col_opts).expect("created new collection");
// List available collections
println!("ls Collections {:?}", database.ls());
// Access the Flinch collection instance
let instance = database.using(COLLECTION);
assert!(instance.is_ok());
let instance = instance.unwrap();
let collection = instance.value();
// Subscribe to real-time updates
let (sx, mut rx) = tokio::sync::mpsc::channel(30000);
collection.sub(sx).await.expect("subscribe to channel");
// Insert documents into the collection
let insert = Instant::now();
let record_size = 10_000;
for i in 0..record_size {
collection.put(format!("P_{}",&i), QueryBased::from_str(
serde_json::to_string(
&User {
name: format!("julfikar{}",&i),
age: i,
}
).unwrap().as_str()
).unwrap()).await.unwrap();
}
assert_eq!(collection.len(), record_size as usize);
println!("insert:: {:?}", insert.elapsed());
// Replace a document in the collection
let x = collection.put(format!("P_{}",0), QueryBased::from_str(
serde_json::to_string(
&User {
name: format!("julfikar-replace"),
age: 10000,
}
).unwrap().as_str()
).unwrap()).await;
assert!(x.is_ok());
println!("replaced value in {}", x.unwrap());
// Get a single document from the collection
let single = collection.get(&format!("P_0"));
assert!(single.data.is_some());
println!("single:: {:?}", single.time_taken);
// Get multiple documents from the collection
let multi = collection.multi_get(vec![&format!("P_1"), &format!("P_0")]);
assert_ne!(multi.data.len(), 0);
println!("multi:: {:?}", multi.time_taken);
// Get an index from the collection
let gidx = collection.get_index("julfikar100");
assert!(gidx.data.is_some());
println!("index:: {:?} {:?}", gidx.time_taken, gidx.data.unwrap().1.data);
// Perform a search query in the collection
let search = collection.search("Julfikar0");
assert_ne!(search.data.len(), 0);
println!("search index:: {} res {}", search.time_taken, search.data.len());
// Perform a like search query in the collection
let like_search = collection.like_search("Julfikar 101");
assert_ne!(like_search.data.len(), 0);
println!("search:: {} res {}", like_search.time_taken, like_search.data.len());
// Fetch a view from the collection
let view = collection.fetch_view("ADULT");
assert_ne!(view.data.len(), 0);
println!("view:: {} res {}", view.time_taken, view.data.len());
// Drop the collection
collection.drop().await;
assert_eq!(collection.len(), 0, "after::drop");
// Listen to pub/sub events for demonstration (limited to 10 messages)
let mut i = 0;
loop {
let event = rx.recv().await.unwrap();
match event {
PubSubEvent::Data(d) => {
match d {
ActionType::Insert(k, _v) => {
println!("inserted :pub/sub: {}", k);
}
ActionType::Remove(k) => {
println!("removed :: {}", k);
}
};
}
PubSubEvent::Subscribed(_s) => {
}
};
i += 1;
if i == 10 {
break;
}
}
}
示例 2:使用 FLQL 查询
第二个示例介绍了 FLQL(Flinch 查询语言),它为与 Flinch 集合交互提供了强大且简洁的语法。它展示了各种 FLQL 查询及其功能。
以下是代码中展示的 FLQL 查询的分解
-
创建集合:此查询使用空选项创建一个新的集合。
-
删除集合:此查询从数据库中删除集合。
-
检查指针存在性:查询检查集合中是否存在特定的指针。
-
集合长度:此查询返回集合中的长度(文档数量)。
-
更新或插入文档:此查询将文档更新或插入到集合中。
-
条件更新或插入:此查询根据指定的条件执行条件更新或插入。
-
更新或插入到指针:此查询将文档更新或插入到集合中的指针。
-
获取文档:此查询从集合中检索文档。
-
条件获取:此查询根据指定的条件执行条件获取操作。
-
获取指针:查询从集合中检索指针的值。
-
获取视图:此查询根据视图配置获取文档。
-
获取剪辑:此查询根据剪辑配置获取文档。
-
获取索引:查询根据索引检索文档。
-
获取范围:此查询根据指定的起始和结束值检索文档范围。
-
搜索:此查询在集合中执行搜索查询。
-
条件搜索:此查询根据指定的条件执行条件搜索。
-
删除文档:此查询从集合中删除文档。
-
条件删除:此查询根据指定的条件执行条件删除。
-
删除指针:查询从集合中删除特定的指针。
-
删除视图:此查询从集合中删除视图。
-
删除剪辑:此查询从集合中删除剪辑。
async fn query_example() {
// Initialize Flinch with collection options
let col_opts = CollectionOptions {
name: Some(COLLECTION.to_string()),
index_opts: vec![format!("name")],
search_opts: vec![format!("name")],
view_opts: vec![ViewConfig{
prop: "age".to_string(),
expected: "18".to_string(),
view_name: "ADULT".to_string(),
}],
range_opts: vec![format!("age")],
clips_opts: vec![format!("name")],
};
let options = serde_json::to_string(&col_opts).unwrap();
let planner = Query::new();
// Create a new collection
let res = planner.exec(format!("new({});",options.as_str()).as_str()).await;
println!("new::collection::error {:?}",res.error);
// Insert documents into the collection
let record_size = 2;
for i in 0..record_size {
let v = serde_json::to_string(
&User {
name: format!("julfikar{}",&i),
age: i,
}
).unwrap();
let query = format!("put({}).into('{}');", v, &COLLECTION);
let x = planner.exec(query.as_str()).await;
assert_eq!(x.error, FlinchError::None);
}
// Get documents from the collection
let res = planner.exec(format!("get.when('map(\"name\") == \"julfikar1\"').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);
// Get an index from the collection
let res = planner.exec(format!("get.index('julfikar1').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);
// Perform a search query in the collection
let res = planner.exec(format!("search.query('julfikar 1').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);
}
更多查询示例可以在其他存储库中找到 https://github.com/mjm918/flinch-flql
new({});
drop('');
exists('').into('');
length('');
put({}).into('');
put({}).when('prop.name == \"acv\" OR prop.name STARTS_WITH \"ac\"').into('');
put({}).pointer('').into('');
get.from('');
get.when('prop.name == \"acv\" OR prop.name STARTS_WITH \"ac\"').from('');
get.pointer('').from('');
get.view('').from('');
get.clip('').from('');
delete.from('');
delete.when('prop.name == \"acv\" OR prop.name STARTS_WITH \"ac\"').from('');
delete.pointer('').from('');
delete.clip('').from('');
这些 FLQL 查询可以使用 Flinch 查询规划器执行,并提供了一种灵活且高效的方式来与 Flinch 集合交互。
依赖项
~14-26MB
~372K SLoC