Skip to main content

indexed_json/
lib.rs

1/* Copyright 2023 Architect Financial Technologies LLC. This is free
2 * software released under the MIT license */
3
4//! With this module you can store serde serializable types in simple
5//! newline delimited json formatted text files and index fields in
6//! those records for quick querying and retreival of matching records
7//! not unlike in a relational database.
8//!
9//! The main intended use case is logging of important books and
10//! records that need to be stored in a simple and accessible format
11//! which can be processed by external tools and backed up online by
12//! simple tools. Writing such records as json text to simple text
13//! files is about as interoperable and resiliant as you can get. At
14//! the same time this library will build an index of an arbitrary set
15//! of fields in your records so that queries can be run on the data
16//! set as if it was in a database. The index can be freely deleted,
17//! and if it becomes corrupted, it can simply be rebuilt, the core
18//! data is never touched.
19//!
20//! This is not exactly a full database, since it doesn't support
21//! modification of records efficiently. If you want to change an
22//! existing record, you can just do that, you can even open the file
23//! in emacs and just edit it. However in that case the entire index
24//! will be invalidated and rebuilt, which can take some
25//! time. Therefore this should be considered an append only database,
26//! since only append is implemented efficiently (which for our use
27//! case is perfectly fine).
28
29use anyhow::{bail, Result};
30use bytes::Buf;
31use chrono::{DateTime, NaiveDate, Utc};
32use compact_str::CompactString;
33use futures::future;
34use fxhash::FxHashMap;
35use immutable_chunkmap::set::SetS as Set;
36use log::{error, warn};
37use netidx_core::pack::Pack;
38use netidx_derive::Pack;
39use parser::LeafTbl;
40use serde::{Deserialize, Serialize};
41use sled::IVec;
42use smallvec::SmallVec;
43use std::{
44    any::Any,
45    cmp::{self, Ordering},
46    collections::{BTreeMap, HashMap},
47    fmt::{Debug, Display},
48    io::SeekFrom,
49    marker::PhantomData,
50    path::{Path, PathBuf},
51    sync::Arc,
52    time::UNIX_EPOCH,
53};
54use tokio::{
55    fs::{self, File, OpenOptions},
56    io::{AsyncBufReadExt, AsyncSeekExt, AsyncWriteExt, BufStream},
57    task,
58};
59
60pub mod parser;
61#[cfg(test)]
62mod test;
63
64/// A query against the index
65#[derive(Debug, Clone)]
66pub enum Query {
67    Eq(Arc<dyn IndexableField + Send + Sync>),
68    Gt(Arc<dyn IndexableField + Send + Sync>),
69    Gte(Arc<dyn IndexableField + Send + Sync>),
70    Lt(Arc<dyn IndexableField + Send + Sync>),
71    Lte(Arc<dyn IndexableField + Send + Sync>),
72    And(Vec<Query>),
73    Or(Vec<Query>),
74    Not(Box<Query>),
75}
76
77impl Display for Query {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        use Query::*;
80        match self {
81            Eq(i) => write!(f, "{} == {}", i.key(), i),
82            Gt(i) => write!(f, "{} > {}", i.key(), i),
83            Gte(i) => write!(f, "{} >= {}", i.key(), i),
84            Lt(i) => write!(f, "{} < {}", i.key(), i),
85            Lte(i) => write!(f, "{} <= {}", i.key(), i),
86            Not(i) => match &**i {
87                Eq(i) => write!(f, "{} != {}", i.key(), i),
88                q => write!(f, "!{}", q),
89            },
90            And(qs) => {
91                let len = qs.len();
92                if len == 0 {
93                    Ok(())
94                } else if len == 1 {
95                    write!(f, "{}", &qs[0])
96                } else {
97                    write!(f, "(")?;
98                    for (i, q) in qs.iter().enumerate() {
99                        if i < len - 1 {
100                            write!(f, "{} && ", q)?
101                        } else {
102                            write!(f, "{}", q)?
103                        }
104                    }
105                    write!(f, ")")
106                }
107            }
108            Or(qs) => {
109                let len = qs.len();
110                if len == 0 {
111                    Ok(())
112                } else if len == 1 {
113                    write!(f, "{}", &qs[0])
114                } else {
115                    write!(f, "(")?;
116                    for (i, q) in qs.iter().enumerate() {
117                        if i < len - 1 {
118                            write!(f, "{} || ", q)?
119                        } else {
120                            write!(f, "{}", q)?
121                        }
122                    }
123                    write!(f, ")")
124                }
125            }
126        }
127    }
128}
129
130impl Query {
131    /// Given a table of key -> constructor functions for leaf nodes,
132    /// parse the specified string as a query and return it.
133    pub fn parse(leaf: &LeafTbl, s: &str) -> Result<Query> {
134        parser::parse_query(leaf, s)
135    }
136
137    /// return true if T matches
138    pub fn matches<T: Indexable>(&self, t: &T) -> bool {
139        match self {
140            Self::Eq(i) => match t.dyn_partial_cmp(&**i) {
141                Some(Ordering::Equal) => true,
142                None | Some(Ordering::Greater | Ordering::Less) => false,
143            },
144            Self::Gt(i) => match t.dyn_partial_cmp(&**i) {
145                Some(Ordering::Greater) => true,
146                None | Some(Ordering::Equal | Ordering::Less) => false,
147            },
148            Self::Gte(i) => match t.dyn_partial_cmp(&**i) {
149                Some(Ordering::Greater | Ordering::Equal) => true,
150                None | Some(Ordering::Less) => false,
151            },
152            Self::Lt(i) => match t.dyn_partial_cmp(&**i) {
153                Some(Ordering::Less) => true,
154                None | Some(Ordering::Equal | Ordering::Greater) => false,
155            },
156            Self::Lte(i) => match t.dyn_partial_cmp(&**i) {
157                Some(Ordering::Less | Ordering::Equal) => true,
158                None | Some(Ordering::Greater) => false,
159            },
160            Self::And(qs) => qs.iter().all(|q| q.matches(t)),
161            Self::Or(qs) => qs.iter().any(|q| q.matches(t)),
162            Self::Not(q) => !q.matches(t),
163        }
164    }
165}
166
167/// An indexable field. Indexable fields must be byte equal, meaning
168/// the decoded representation will be equal if the encoded bytes are
169/// equal. They may or may not be byte comparable.
170pub trait IndexableField: Display + Debug + Send + Sync + Any {
171    /// return the database key that should be used for this field
172    fn key(&self) -> &'static str;
173
174    /// return true if this object can be compared by comparing
175    /// their encoded bytes. This is true for e.g. big endian encoded
176    /// integers, strings, and a lot of other types, but not generally
177    /// for, chrono::DateTime, Decimal, etc..
178    fn byte_compareable(&self) -> bool;
179
180    /// Place the database representation of the value into the
181    /// specified buffer
182    fn encode(&self, buf: &mut SmallVec<[u8; 128]>) -> Result<()>;
183
184    /// Compare the value with the specified encoded value. This will
185    /// only be called if byte_comparable is false. The default
186    /// implementation just does byte comparison.
187    fn decode_cmp(&self, b: &[u8]) -> Result<Ordering> {
188        let mut buf: SmallVec<[u8; 128]> = SmallVec::new();
189        self.encode(&mut buf)?;
190        Ok(b.cmp(&buf[..]))
191    }
192
193    /// return a downcastable object to recover the original type of
194    /// the indexable value in a query. This is a work around until
195    /// trait upcasting is stabilized. (this is currenly only used by
196    /// the unit tests, but who knows, it could be useful)
197    fn as_any(&self) -> &dyn Any;
198}
199
200/// This must be implemented for a type to be indexable
201pub trait Indexable {
202    type Iter: IntoIterator<Item = Box<dyn IndexableField>>;
203
204    /// Return an iterator of indexable values in this record.
205    fn index(&self) -> Self::Iter;
206
207    /// return the timestamp of this record. This will only be used to
208    /// figure out what file to put the record in. The only real
209    /// reason why there are multiple files is to ensure logs are
210    /// rotated periodically so they can be backed up, and potentially
211    /// archived permanently. If you just return the same timestamp
212    /// for every record then there will just be one file and
213    /// everything else will work fine.
214    fn timestamp(&self) -> DateTime<Utc>;
215
216    /// given an indexable field object return an ordering if the
217    /// field is relevant to this record
218    fn dyn_partial_cmp(&self, i: &dyn IndexableField) -> Option<Ordering>;
219}
220
221fn min_key_with_prefix(index: &sled::Tree, k: &[u8]) -> Result<Option<IVec>> {
222    let mut iter = index.scan_prefix(k);
223    Ok(iter.next().transpose()?.map(|(k, _)| k))
224}
225
226fn max_key_with_prefix(index: &sled::Tree, k: &[u8]) -> Result<Option<IVec>> {
227    let mut iter = index.scan_prefix(k);
228    Ok(iter.next_back().transpose()?.map(|(k, _)| k))
229}
230
231/// This is the location of a record in the archive.
232#[derive(Debug, Clone, Copy, Pack, PartialEq, Eq, PartialOrd, Ord)]
233#[pack(unwrapped)]
234pub struct IndexEntry {
235    /// which file is the record in
236    pub file: NaiveDate,
237    /// offset from the beginning of the file where the record begins
238    pub offset: u64,
239}
240
241#[derive(Debug)]
242struct JsonFile<T: Indexable + Serialize + for<'a> Deserialize<'a>> {
243    phantom: PhantomData<T>,
244    last_used: DateTime<Utc>,
245    file: Option<BufStream<File>>,
246    path: PathBuf,
247    name: NaiveDate,
248    len: u64,
249    pos: u64,
250    rbuf: String,
251    wbuf: Vec<u8>,
252}
253
254macro_rules! open_file {
255    ($self:expr) => {
256        if $self.file.is_some() {
257            $self.file.as_mut().unwrap()
258        } else {
259            let f = OpenOptions::new()
260                .read(true)
261                .append(true)
262                .write(true)
263                .create(true)
264                .open(&$self.path)
265                .await?;
266            let mut f = BufStream::new(f);
267            let len = f.seek(SeekFrom::End(0)).await?;
268            $self.len = len;
269            $self.pos = len;
270            $self.file = Some(f);
271            $self.file.as_mut().unwrap()
272        }
273    };
274}
275
276impl<T: Indexable + Serialize + for<'a> Deserialize<'a>> JsonFile<T> {
277    fn new(base: impl AsRef<Path>, name: NaiveDate) -> Self {
278        Self {
279            phantom: PhantomData,
280            last_used: DateTime::<Utc>::MIN_UTC,
281            file: None,
282            path: base.as_ref().join(&format!("{name}")),
283            name,
284            pos: 0,
285            len: 0,
286            rbuf: String::new(),
287            wbuf: Vec::new(),
288        }
289    }
290
291    async fn mtime(&self) -> Result<u128> {
292        Ok(fs::metadata(&self.path)
293            .await?
294            .modified()?
295            .duration_since(UNIX_EPOCH)?
296            .as_nanos())
297    }
298
299    fn close_if_idle(&mut self, now: DateTime<Utc>) {
300        if now - self.last_used > chrono::Duration::minutes(5) {
301            self.file = None
302        }
303    }
304
305    async fn get(&mut self, pos: u64) -> Result<Option<(u64, T)>> {
306        self.last_used = Utc::now();
307        let file = open_file!(self);
308        if pos != self.pos {
309            let new_pos = file.seek(SeekFrom::Start(pos)).await?;
310            if new_pos != pos {
311                bail!("{pos} doesn't exist in {:?}", &self.name)
312            }
313            self.pos = new_pos;
314        }
315        self.rbuf.clear();
316        let read = file.read_line(&mut self.rbuf).await?;
317        if self.rbuf.len() == 0 {
318            Ok(None)
319        } else {
320            self.pos = self.pos + read as u64;
321            Ok(Some((self.pos, serde_json::from_str(&self.rbuf.trim())?)))
322        }
323    }
324
325    async fn flush(&mut self) -> Result<()> {
326        if let Some(file) = &mut self.file {
327            file.flush().await?
328        }
329        Ok(())
330    }
331
332    fn db_name(&self) -> CompactString {
333        use std::fmt::Write;
334        let mut buf = CompactString::new("");
335        write!(buf, "{}", self.name).unwrap();
336        buf
337    }
338
339    async fn update_mtime(&mut self, db: &sled::Tree) -> Result<()> {
340        let mtime = self.mtime().await?;
341        db.insert(self.db_name().as_bytes(), &u128::to_be_bytes(mtime))?;
342        Ok(())
343    }
344
345    /// write record to the file, return the position of the beginning
346    /// of the newly written record.
347    async fn append(&mut self, record: &T) -> Result<u64> {
348        self.last_used = Utc::now();
349        let file = open_file!(self);
350        let pos = self.pos;
351        if self.pos != self.len {
352            file.flush().await?;
353            self.pos = file.seek(SeekFrom::End(0)).await?;
354            self.len = self.pos;
355        }
356        self.wbuf.clear();
357        serde_json::to_writer(&mut self.wbuf, record)?;
358        self.wbuf.push(b'\n');
359        file.write_all(&self.wbuf).await?;
360        self.pos += self.wbuf.len() as u64;
361        self.len += self.wbuf.len() as u64;
362        Ok(pos)
363    }
364}
365
366/// An indexed json archive
367pub struct IndexedJson<T: Indexable + Serialize + for<'a> Deserialize<'a>> {
368    phantom: PhantomData<T>,
369    base: PathBuf,
370    db: sled::Db,
371    index_status: sled::Tree,
372    trees: FxHashMap<CompactString, sled::Tree>,
373    files: BTreeMap<NaiveDate, JsonFile<T>>,
374    gc: DateTime<Utc>,
375    dirty: bool,
376}
377
378impl<T: Debug + Indexable + Serialize + for<'a> Deserialize<'a>> IndexedJson<T> {
379    /// Open an existing indexed json archive, or create a new one. If
380    /// the index is missing or outdated then it will be rebuilt.
381    pub async fn open(base: impl AsRef<Path>) -> Result<Self> {
382        if !base.as_ref().exists() {
383            fs::create_dir_all(&base).await?;
384        }
385        if !fs::metadata(&base).await?.is_dir() {
386            bail!("{:?} is not a directory", base.as_ref())
387        }
388        let db = task::spawn_blocking({
389            let path = base.as_ref().join("db");
390            move || sled::Config::new().flush_every_ms(None).path(path).open()
391        })
392        .await??;
393        let index_status = task::spawn_blocking({
394            let db = db.clone();
395            move || db.open_tree("status")
396        })
397        .await??;
398        let files = BTreeMap::new();
399        let mut t = Self {
400            phantom: PhantomData,
401            base: PathBuf::from(base.as_ref()),
402            db,
403            index_status,
404            trees: HashMap::default(),
405            files,
406            gc: Utc::now(),
407            dirty: false,
408        };
409        t.maybe_reindex().await?;
410        Ok(t)
411    }
412
413    /// the base path of the archive
414    pub fn path(&self) -> &Path {
415        &self.base
416    }
417
418    // note this will close all open files as well as add any new
419    // files that have appeared on disk to the database.
420    async fn rescan_files(&mut self) -> Result<()> {
421        self.trees.clear();
422        for name in self.db.tree_names() {
423            if name.starts_with(b"index_") {
424                let tree = self.db.open_tree(&name)?;
425                self.trees
426                    .insert(CompactString::from_utf8_lossy(&*name), tree);
427            }
428        }
429        self.files.clear();
430        let mut dir = fs::read_dir(&self.base).await?;
431        loop {
432            match dir.next_entry().await? {
433                None => break,
434                Some(e) => {
435                    if e.file_type().await?.is_file() {
436                        let name = e.file_name();
437                        let name = name.to_string_lossy();
438                        if let Ok(d) = name.parse::<NaiveDate>() {
439                            self.files.insert(d, JsonFile::new(&self.base, d));
440                        }
441                    }
442                }
443            }
444        }
445        Ok(())
446    }
447
448    async fn needs_reindex(&mut self) -> Result<bool> {
449        self.rescan_files().await?;
450        for file in self.files.values() {
451            match self.index_status.get(file.db_name().as_bytes())? {
452                None => return Ok(true),
453                Some(dbmtime) => {
454                    let dbmtime = {
455                        if dbmtime.len() != 16 {
456                            return Ok(true);
457                        }
458                        (&mut &*dbmtime).get_u128()
459                    };
460                    let fsmtime = file.mtime().await?;
461                    if fsmtime != dbmtime {
462                        return Ok(true);
463                    }
464                }
465            }
466        }
467        for r in self.index_status.iter() {
468            let (file, _) = r?;
469            let file = match String::from_utf8_lossy(&*file).parse::<NaiveDate>() {
470                Err(_) => return Ok(true),
471                Ok(file) => file,
472            };
473            if !self.files.contains_key(&file) {
474                return Ok(true);
475            }
476        }
477        Ok(false)
478    }
479
480    /// Rebuild the index only if necessary. This is called
481    /// automatically by `open`. However if you know, or suspect, the
482    /// files have been modified out of band since then, you can call
483    /// again and it will check and rebuild the index if they have.
484    ///
485    /// There is no need to do this if you just called `append`.
486    ///
487    /// Before checking it will also rescan the filesystem. As a
488    /// result, all open files will be closed. Any new files will be
489    /// added to the index, and missing files will be removed from
490    /// it. So if any new files appear or old files disappear the
491    /// index will be rebuilt.
492    ///
493    /// If the index is damaged you can call `reindex`, which will
494    /// force it to rebuild.
495    pub async fn maybe_reindex(&mut self) -> Result<()> {
496        if self.needs_reindex().await? {
497            self.reindex().await?
498        }
499        Ok(())
500    }
501
502    /// force rebuild the index
503    pub async fn reindex(&mut self) -> Result<()> {
504        for (_, tree) in self.trees.drain() {
505            tree.clear()?
506        }
507        self.index_status.clear()?;
508        self.rescan_files().await?;
509        for (file, f) in self.files.iter_mut() {
510            let mut pos = 0;
511            loop {
512                match f.get(pos).await {
513                    Ok(Some((next_pos, t))) => {
514                        let entry = IndexEntry {
515                            file: *file,
516                            offset: pos,
517                        };
518                        Self::index_record(&self.db, &mut self.trees, entry, &t)?;
519                        pos = next_pos;
520                    }
521                    Ok(None) => break,
522                    Err(e) => {
523                        error!("error reindexing file {file} pos {pos} error {:?}", e);
524                        break;
525                    }
526                }
527            }
528            f.update_mtime(&self.index_status).await?
529        }
530        self.dirty = true;
531        self.flush().await?;
532        Ok(())
533    }
534
535    // index format in btree {value}/{i} => {IndexEntry}
536    // where i is the number of times the same value
537    // appears in the index. Each key is stored in a separate
538    // sled tree according to it's name
539    fn index_record<'a>(
540        db: &sled::Db,
541        trees: &mut FxHashMap<CompactString, sled::Tree>,
542        pos: IndexEntry,
543        record: &'a T,
544    ) -> Result<()> {
545        let mut kbuf: SmallVec<[u8; 128]> = SmallVec::new();
546        let mut vbuf: SmallVec<[u8; 16]> = SmallVec::new();
547        vbuf.resize(pos.encoded_len(), 0u8);
548        pos.encode(&mut &mut *vbuf)?;
549        for field in record.index() {
550            let tree = match trees.get(field.key()) {
551                Some(t) => t,
552                None => {
553                    let tree = db.open_tree(format!("index_{}", field.key()))?;
554                    trees
555                        .entry(CompactString::from(field.key()))
556                        .or_insert(tree)
557                }
558            };
559            kbuf.clear();
560            field.encode(&mut kbuf)?;
561            kbuf.push(b'/');
562            let count = match max_key_with_prefix(tree, &kbuf)? {
563                None => 0,
564                Some(k) if k.len() < 8 => 0,
565                Some(k) => (&mut &k[k.len() - 8..]).get_u64() + 1,
566            };
567            kbuf.extend_from_slice(&u64::to_be_bytes(count));
568            tree.insert(&kbuf[..], &vbuf[..])?;
569        }
570        Ok::<_, anyhow::Error>(())
571    }
572
573    fn maybe_gc(&mut self) {
574        let now = Utc::now();
575        if now - self.gc > chrono::Duration::minutes(5) {
576            self.gc = now;
577            for f in self.files.values_mut() {
578                f.close_if_idle(now)
579            }
580        }
581    }
582
583    /// flush writes to disk. If you call append in batches, you
584    /// should call this at the end of each batch to make sure your
585    /// changes are flushed to disk.
586    pub async fn flush(&mut self) -> Result<()> {
587        if self.dirty {
588            future::join_all(self.files.values_mut().map(|f| async {
589                f.flush().await?;
590                f.update_mtime(&self.index_status).await
591            }))
592            .await
593            .into_iter()
594            .collect::<Result<Vec<_>>>()?;
595            task::spawn_blocking({
596                let db = self.db.clone();
597                move || db.flush()
598            })
599            .await??;
600            task::spawn_blocking({
601                let db = self.index_status.clone();
602                move || db.flush()
603            })
604            .await??;
605            for tree in self.trees.values() {
606                task::spawn_blocking({
607                    let db = tree.clone();
608                    move || db.flush()
609                })
610                .await??;
611            }
612            self.dirty = false;
613        }
614        Ok(())
615    }
616
617    /// append the record to the end of the file corresponding to it's
618    /// timestamp and index it.
619    pub async fn append(&mut self, record: &T) -> Result<()> {
620        let name = record.timestamp().date_naive();
621        let file = self
622            .files
623            .entry(name)
624            .or_insert_with(|| JsonFile::new(&self.base, name));
625        let pos = file.append(record).await?;
626        Self::index_record(
627            &self.db,
628            &mut self.trees,
629            IndexEntry {
630                file: name,
631                offset: pos,
632            },
633            record,
634        )?;
635        self.dirty = true;
636        Ok(self.maybe_gc())
637    }
638
639    /// return the index of the first record in the first file, or
640    /// none if there are no records
641    pub fn first(&self) -> Option<IndexEntry> {
642        self.files.first_key_value().map(|(k, _)| IndexEntry {
643            file: *k,
644            offset: 0,
645        })
646    }
647
648    /// retreive the specified record from the json files. Returns a
649    /// pair of the record and the next entry index if there is
650    /// one. If None is returned then there were no more entries in
651    /// the archive. You can iterate through all the records in the
652    /// archive by starting with `first` and calling get with each
653    /// successive record until it returns `None`
654    pub async fn get(&mut self, mut entry: IndexEntry) -> Result<Option<(IndexEntry, T)>> {
655        loop {
656            let file = self
657                .files
658                .entry(entry.file)
659                .or_insert_with(|| JsonFile::new(&self.base, entry.file));
660            match file.get(entry.offset).await? {
661                Some((offset, t)) => break Ok(Some((IndexEntry { offset, ..entry }, t))),
662                None => {
663                    let mut r = self.files.range(entry.file..);
664                    r.next(); // will be the current entry
665                    match r.next() {
666                        Some((e, _)) => {
667                            entry = IndexEntry {
668                                file: *e,
669                                offset: 0,
670                            };
671                        }
672                        None => break Ok(None),
673                    }
674                }
675            }
676        }
677    }
678
679    /// execute the specified query against the index and return the
680    /// set of matching entries. You can then retrieve the matching
681    /// entries using `get`
682    pub fn query(&self, query: &Query) -> Result<Set<IndexEntry>> {
683        fn field_k(field: &dyn IndexableField) -> Result<SmallVec<[u8; 128]>> {
684            let mut buf = SmallVec::new();
685            field.encode(&mut buf)?;
686            buf.push(b'/');
687            Ok(buf)
688        }
689        fn insert(set: &mut Set<IndexEntry>, k: &[u8], mut v: &[u8]) {
690            match IndexEntry::decode(&mut v) {
691                Ok(ent) => {
692                    set.insert_cow(ent);
693                }
694                Err(e) => {
695                    warn!("could not decode entry with key {:?}, {:?}", k, e)
696                }
697            }
698        }
699        fn gt_gte(
700            trees: &FxHashMap<CompactString, sled::Tree>,
701            field: &dyn IndexableField,
702            gte: bool,
703        ) -> Result<Set<IndexEntry>> {
704            match trees.get(field.key()) {
705                None => Ok(Set::new()),
706                Some(tree) => {
707                    let mut set = Set::new();
708                    let key = field_k(field)?;
709                    if field.byte_compareable() {
710                        let min = match min_key_with_prefix(tree, &key[..])? {
711                            Some(k) => k,
712                            None => match tree.first()? {
713                                Some((k, _)) => k,
714                                None => return Ok(set),
715                            },
716                        };
717                        for r in tree.range(&min[..]..) {
718                            let (k, v) = r?;
719                            let k = &k[0..cmp::min(key.len(), k.len() - 8)];
720                            if (gte && k >= &key[..]) || k > &key[..] {
721                                insert(&mut set, &*k, &*v)
722                            }
723                        }
724                    } else {
725                        for r in tree.iter() {
726                            let (k, v) = r?;
727                            match field.decode_cmp(&k[..cmp::min(key.len(), k.len() - 8) - 1])? {
728                                Ordering::Less => insert(&mut set, &*k, &*v),
729                                Ordering::Equal if gte => insert(&mut set, &*k, &*v),
730                                Ordering::Equal | Ordering::Greater => (),
731                            }
732                        }
733                    }
734                    Ok(set)
735                }
736            }
737        }
738        fn lt_lte(
739            trees: &FxHashMap<CompactString, sled::Tree>,
740            field: &dyn IndexableField,
741            lte: bool,
742        ) -> Result<Set<IndexEntry>> {
743            match trees.get(field.key()) {
744                None => Ok(Set::new()),
745                Some(tree) => {
746                    let key = field_k(field)?;
747                    let mut set = Set::new();
748                    if field.byte_compareable() {
749                        let max = match max_key_with_prefix(tree, &key)? {
750                            Some(k) => k,
751                            None => match tree.last()? {
752                                Some((k, _)) => k,
753                                None => return Ok(set),
754                            },
755                        };
756                        for r in tree.range(..=&max[..]) {
757                            let (k, v) = r?;
758                            let k = &k[0..cmp::min(key.len(), k.len() - 8)];
759                            if (lte && k <= &key[..]) || k < &key[..] {
760                                insert(&mut set, &*k, &*v)
761                            }
762                        }
763                    } else {
764                        for r in tree.iter() {
765                            let (k, v) = r?;
766                            match field.decode_cmp(&k[..cmp::min(key.len(), k.len() - 8) - 1])? {
767                                Ordering::Greater => insert(&mut set, &*k, &*v),
768                                Ordering::Equal if lte => insert(&mut set, &*k, &*v),
769                                Ordering::Equal | Ordering::Less => (),
770                            }
771                        }
772                    }
773                    Ok(set)
774                }
775            }
776        }
777        fn eq(
778            trees: &FxHashMap<CompactString, sled::Tree>,
779            field: &dyn IndexableField,
780        ) -> Result<Set<IndexEntry>> {
781            let mut set = Set::new();
782            match trees.get(field.key()) {
783                None => Ok(Set::new()),
784                Some(tree) => {
785                    for r in tree.scan_prefix(&field_k(field)?) {
786                        let (k, v) = r?;
787                        insert(&mut set, &*k, &*v)
788                    }
789                    Ok(set)
790                }
791            }
792        }
793        fn all_for_key(
794            trees: &FxHashMap<CompactString, sled::Tree>,
795            field: &dyn IndexableField,
796        ) -> Result<Set<IndexEntry>> {
797            match trees.get(field.key()) {
798                None => Ok(Set::new()),
799                Some(tree) => {
800                    let mut set = Set::new();
801                    for r in tree.iter() {
802                        let (k, v) = r?;
803                        insert(&mut set, &*k, &*v)
804                    }
805                    Ok(set)
806                }
807            }
808        }
809        fn all(trees: &FxHashMap<CompactString, sled::Tree>) -> Result<Set<IndexEntry>> {
810            let mut set = Set::new();
811            for tree in trees.values() {
812                for r in tree.iter() {
813                    let (k, v) = r?;
814                    insert(&mut set, &*k, &*v)
815                }
816            }
817            Ok(set)
818        }
819        match query {
820            Query::Eq(field) => eq(&self.trees, &**field),
821            Query::Gt(field) => gt_gte(&self.trees, &**field, false),
822            Query::Gte(field) => gt_gte(&self.trees, &**field, true),
823            Query::Lt(field) => lt_lte(&self.trees, &**field, false),
824            Query::Lte(field) => lt_lte(&self.trees, &**field, true),
825            Query::And(qs) => Ok(qs
826                .iter()
827                .map(|q| self.query(q))
828                .collect::<Result<Vec<Set<_>>>>()?
829                .into_iter()
830                .fold(None, |acc: Option<Set<_>>, s| match acc {
831                    Some(acc) => Some(acc.intersect(&s)),
832                    None => Some(s),
833                })
834                .unwrap_or_else(Set::new)),
835            Query::Or(qs) => Ok(qs
836                .iter()
837                .map(|q| self.query(q))
838                .collect::<Result<Vec<_>>>()?
839                .into_iter()
840                .fold(Set::new(), |acc, s| acc.union(&s))),
841            Query::Not(q) => match &**q {
842                Query::Eq(field) => {
843                    let matches = eq(&self.trees, &**field)?;
844                    let all = all_for_key(&self.trees, &**field)?;
845                    Ok(all.diff(&matches))
846                }
847                Query::Gt(field) => lt_lte(&self.trees, &**field, true),
848                Query::Gte(field) => lt_lte(&self.trees, &**field, false),
849                Query::Lt(field) => gt_gte(&self.trees, &**field, true),
850                Query::Lte(field) => gt_gte(&self.trees, &**field, false),
851                q @ Query::And(_) | q @ Query::Or(_) | q @ Query::Not(_) => {
852                    let matches = self.query(q)?;
853                    Ok(all(&self.trees)?.diff(&matches))
854                }
855            },
856        }
857    }
858}