Rust

Flinch is an in-memory, real-time document database designed for fast, efficient full-text search and querying. It comes with a built-in full-text search engine that enables both "search-as-you-type" and like search capabilities. Flinch was created with the goal of providing a high-performance search solution that can be integrated into various applications.

Features

How to use

As library ``` async fn library() { let colopts = CollectionOptions { name: Some(COLLECTION.tostring()), indexopts: vec![format!("name")], searchopts: vec![format!("name")], viewopts: vec![ViewConfig{ prop: "age".tostring(), expected: "18".tostring(), viewname: "ADULT".tostring(), }], rangeopts: vec![format!("age")], clipsopts: vec![format!("name")], }; let database = Database::init(); database.add(colopts).expect("created new collection");

println!("ls Collections {:?}", database.ls());

let instance = database.using(COLLECTION);
assert!(instance.is_ok());

let instance = instance.unwrap();
let collection = instance.value();
assert_eq!(collection.len(),0);

let (sx, mut rx) = tokio::sync::mpsc::channel(30000);
collection.sub(sx).await.expect("subscribe to channel");

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());

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());

let single = collection.get(&format!("P_0"));
assert!(single.data.is_some());
println!("single:: {:?}", single.time_taken);

let multi = collection.multi_get(vec![&format!("P_1"),&format!("P_0")]);
assert_ne!(multi.data.len(),0);
println!("multi:: {:?}",multi.time_taken);

let gidx = collection.get_index("julfikar100");
assert!(gidx.data.is_some());
println!("index:: {:?} {:?}",gidx.time_taken,gidx.data.unwrap().1.data);

let search = collection.search("Julfikar0");
assert_ne!(search.data.len(),0);
println!("search index:: {} res {}",search.time_taken, search.data.len());

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());

let view = collection.fetch_view("ADULT");
assert_ne!(view.data.len(),0);
println!("view:: {} res {}",view.time_taken, view.data.len());

collection.drop().await;

assert_eq!(collection.len(),0,"after::drop");

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 { // for demo, listen till 10 message only
        break;
    }
}

} **Query example** async fn queryexample() { let colopts = CollectionOptions { name: Some(COLLECTION.tostring()), indexopts: vec![format!("name")], searchopts: vec![format!("name")], viewopts: vec![ViewConfig{ prop: "age".tostring(), expected: "18".tostring(), viewname: "ADULT".tostring(), }], rangeopts: vec![format!("age")], clipsopts: vec![format!("name")], }; let options = serdejson::tostring(&colopts).unwrap(); let planner = Query::new(); let res = planner.exec(format!("new({});",options.asstr()).as_str()).await; println!("new::collection::error {:?}",res.error);

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);
}

let res = planner.exec(format!("get.when(:map(\"name\") == \"julfikar1\":).from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);

let res = planner.exec(format!("get.index('julfikar1').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);

let res = planner.exec(format!("search.query('julfikar 1').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);

let res = planner.exec(format!("search.when(:map(\"age\") == 0:).query('julfikar').from('{}');",&COLLECTION).as_str()).await;
println!("{:?}",res);

} ```

Introducing FLQL

Create collection
new({});

Drop collection
drop('');

Check if pointer exists in collection
exists('').into('');

Length of collection
length('');

Update or Insert into collection
put({}).into('');

Conditional Update or Insert into collection
put({}).when(:includes(array_filter('e.f$.g'),2):).into('');

Update or Insert into collection to a Pointer
put({}).pointer('').into('');

Get from collection
get.from('');

Conditional Get from collection
get.when(:includes(array_filter('e.f$.g'),2):).from('');

Get Pointer from collection
get.pointer('').from('');

Get View from collection
get.view('').from('');

Get Clip from collection
get.clip('').from('');

Get index from collection
get.index('').from('');

Get range from collection
get.range(start:'', end:'', on:'').from('');

Search query
search.query('').from('');

Conditional Search query
search.when(#func(args)#).query('').from('');

Delete from collection
delete.from('');

Conditional Delete from collection
delete.when(:includes(array_filter('e.f$.g'),2):).from('');

Delete Pointer from collection
delete.pointer('').from('');

Delete View from collection
delete.view('').from('');

Delete Clip from collection
delete.clip('').from('');