mirror of https://github.com/nextgraph-org/rkv.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
4.8 KiB
131 lines
4.8 KiB
// Copyright 2018-2019 Mozilla
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
// this file except in compliance with the License. You may obtain a copy of the
|
|
// License at http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software distributed
|
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
#![cfg(feature = "db-dup-sort")]
|
|
|
|
use std::fs;
|
|
|
|
use tempfile::Builder;
|
|
|
|
use rkv::{
|
|
backend::{SafeMode, SafeModeDatabase, SafeModeRoCursor, SafeModeRwTransaction},
|
|
Readable, Rkv, StoreOptions, Value, Writer,
|
|
};
|
|
|
|
/// Consider a struct like this:
|
|
/// struct Sample {
|
|
/// id: u64,
|
|
/// value: String,
|
|
/// date: String,
|
|
/// }
|
|
/// We would like to index all of the fields so that we can search for the struct not only
|
|
/// by ID but also by value and date. When we index the fields individually in their own
|
|
/// tables, it is important that we run all operations within a single transaction to
|
|
/// ensure coherence of the indices.
|
|
/// This test features helper functions for reading and writing the parts of the struct.
|
|
/// Note that the reader functions take `Readable` because they might run within a Read
|
|
/// Transaction or a Write Transaction. The test demonstrates fetching values via both.
|
|
|
|
type SingleStore = rkv::SingleStore<SafeModeDatabase>;
|
|
type MultiStore = rkv::MultiStore<SafeModeDatabase>;
|
|
|
|
#[test]
|
|
fn read_many() {
|
|
let root = Builder::new()
|
|
.prefix("test_txns")
|
|
.tempdir()
|
|
.expect("tempdir");
|
|
fs::create_dir_all(root.path()).expect("dir created");
|
|
let k = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
|
|
let samplestore = k.open_single("s", StoreOptions::create()).expect("open");
|
|
let datestore = k.open_multi("m", StoreOptions::create()).expect("open");
|
|
let valuestore = k.open_multi("m", StoreOptions::create()).expect("open");
|
|
|
|
{
|
|
let mut writer = k.write().expect("env write lock");
|
|
|
|
for id in 0..30_u64 {
|
|
let value = format!("value{}", id);
|
|
let date = format!("2019-06-{}", id);
|
|
put_id_field(&mut writer, datestore, &date, id);
|
|
put_id_field(&mut writer, valuestore, &value, id);
|
|
put_sample(&mut writer, samplestore, id, &value);
|
|
}
|
|
|
|
// now we read in the same transaction
|
|
for id in 0..30_u64 {
|
|
let value = format!("value{}", id);
|
|
let date = format!("2019-06-{}", id);
|
|
let ids = get_ids_by_field(&writer, datestore, &date);
|
|
let ids2 = get_ids_by_field(&writer, valuestore, &value);
|
|
let samples = get_samples(&writer, samplestore, &ids);
|
|
let samples2 = get_samples(&writer, samplestore, &ids2);
|
|
println!("{:?}, {:?}", samples, samples2);
|
|
}
|
|
}
|
|
|
|
{
|
|
let reader = k.read().expect("env read lock");
|
|
for id in 0..30_u64 {
|
|
let value = format!("value{}", id);
|
|
let date = format!("2019-06-{}", id);
|
|
let ids = get_ids_by_field(&reader, datestore, &date);
|
|
let ids2 = get_ids_by_field(&reader, valuestore, &value);
|
|
let samples = get_samples(&reader, samplestore, &ids);
|
|
let samples2 = get_samples(&reader, samplestore, &ids2);
|
|
println!("{:?}, {:?}", samples, samples2);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_ids_by_field<'t, T>(txn: &'t T, store: MultiStore, field: &'t str) -> Vec<u64>
|
|
where
|
|
T: Readable<'t, Database = SafeModeDatabase, RoCursor = SafeModeRoCursor<'t>>,
|
|
{
|
|
store
|
|
.get(txn, field)
|
|
.expect("get iterator")
|
|
.map(|id| match id.expect("field") {
|
|
(_, Value::U64(id)) => id,
|
|
_ => panic!("getting value in iter"),
|
|
})
|
|
.collect::<Vec<u64>>()
|
|
}
|
|
|
|
fn get_samples<'t, T>(txn: &'t T, samplestore: SingleStore, ids: &[u64]) -> Vec<String>
|
|
where
|
|
T: Readable<'t, Database = SafeModeDatabase, RoCursor = SafeModeRoCursor<'t>>,
|
|
{
|
|
ids.iter()
|
|
.map(|id| {
|
|
let bytes = id.to_be_bytes();
|
|
match samplestore.get(txn, &bytes).expect("fetch sample") {
|
|
Some(Value::Str(sample)) => String::from(sample),
|
|
Some(_) => panic!("wrong type"),
|
|
None => panic!("no sample for this id!"),
|
|
}
|
|
})
|
|
.collect::<Vec<String>>()
|
|
}
|
|
|
|
fn put_sample(
|
|
txn: &mut Writer<SafeModeRwTransaction>,
|
|
samplestore: SingleStore,
|
|
id: u64,
|
|
value: &str,
|
|
) {
|
|
let idbytes = id.to_be_bytes();
|
|
samplestore
|
|
.put(txn, &idbytes, &Value::Str(value))
|
|
.expect("put id");
|
|
}
|
|
|
|
fn put_id_field(txn: &mut Writer<SafeModeRwTransaction>, store: MultiStore, field: &str, id: u64) {
|
|
store.put(txn, field, &Value::U64(id)).expect("put id");
|
|
}
|
|
|