2 stable releases

2.0.0 Aug 14, 2024
1.0.0 Aug 10, 2024

#277 in Database interfaces

Download history 107/week @ 2024-08-07 139/week @ 2024-08-14 1/week @ 2024-08-21 4/week @ 2024-08-28

147 downloads per month
Used in 3 crates

LGPL-3.0-only

77KB
1.5K SLoC

Criterium

Criterium is a mini-framework to make implementing dynamic conditions and database queries easier.

It does so by supplying the needed datastructures that can be compiled to SQL and a list of values or directly matched against a given value.

Multiple Criteria can also be combined using Boolean logic (chains).

Dynamic also means configurable, Criterium integrates itself with Serde.

It currently integrates with rusqlite.

How does it work?

Also see the concepts document.

Usually when building database query one starts out with a relatively simple function:

fn fetch_bookmark_information(
	is_favorite: Option<bool>,
	name_contains: Option<String>,
	tags: Vec<String>,
	but_not_tags: Vec<String>,
	//
) -> Vec<Bookmark>;

But as you can see even from this simple example: This won't scale very well and writing the SQL generation for that by hand is a tedious nightmare (even when switching over to structs).

And most of the time one doesn't even need all the options available. This is where Criterium comes in.

With Criterium you have an enum that describes all the possible queries for a datatype.

enum BookmarkCriterium {
	IsFavorite(BooleanCriterium),
	Name(StringCriterium),
	HasTag(StringCriterium),
	//
}

impl ToRusqliteQuery for BookmarkCriterium {
	fn get_sql_where(&self) -> String {
		match self {
			Self::IsFavorite(c) => c.get_sql_where("favorite".to_string()),
			Self::Name(c) => c.get_sql_where("name".to_string()),
		}
	}

	fn get_where_values(&self) -> Vec<Value> {
		match self {
			Self::IsFavorite(c) => c.get_where_values(),
			Self::Name(c) => c.get_where_values(),
		}
	}
}

Note: Not all of the datatypes of the example are implemented yet, but it should be pretty straigtforward to implement them outside of Criterium.

Multiple of these are then combined using a CriteriumChain:

let chain_builder: CriteriumChainBuilder<BookmarkCriterium> =
	CriteriumChainBuilder::and(true);
	
chain_builder.add_criterium(
	BookmarkCriterium::IsFavorite(true.into()));
chain_builder.add_criterium(
	BookmarkCriterium::HasTag("tag".into()));
chain_builder.add_criterium(
	BookmarkCriterium::HasTag(StringCriterium::HasPrefix("useful:")));
chain_builder.add_criterium(
	BookmarkCriterium::Name(StringCriterium::Contains("foo")));

let bookmarks = database.query_bookmarks(chain_builder.to_chain());

While at first glance this looks more verbose (it is) this can be assembled on the fly and is more flexible (Noticed how I snug a prefix match on a tag in there?).

The SQL in the background simply has a WHERE with the output of the chain.get_sql_where() concatenated after and the values taken from chain.get_where_values(), you only have to provide the values your Criterium relies on in the SQL query.

As you can tell this is useful when there are a lot of things to query for, and also pretty opinioated, it may oe may not fit your project.

Isn't that an Object Relational Model (ORM) like in django?

Yes, kind of. But very lightweight and barebones … and the compiler can verify most of it for you.

Feature Flags

By default all integrations that require external crates are disabled. More integrations to come soonish™.

  • rusqlite - Enables rusqlite integration (compiling criteria to SQL)
  • chrono - Enables chrono integration (passing a DateTime in place of a number)
  • serde - Enables serializing and deserializing criteium-chains and default criteria using serde.
  • full - Enable all features, useful for building documentation, for dependencies you should explicitly enable all features you need.

What is in here?

Note: If you are reding this from the rust domunetation, the links are broken.

  • boolean - A Criterium for matching boolean values.
  • number - A Criterium for matching numbers.
  • string - A Criterium for matching text.
  • search - A Criterium for integrating full text search capabilities.
  • chain - A Criterium for connecting other criteria with boolean operators
    • builder.rs - A builder struct to make it easier to construct chains.
    • rusqlite.rs - Rusqlite integration.
  • rusqlite.rs - Traits for integrating with rusqlite.

Dependencies

~0–4MB
~72K SLoC