2c872e6982 | 2 years ago | |
---|---|---|
.github/workflows | 2 years ago | |
cozo-core | 2 years ago | |
cozo-lib-c | 2 years ago | |
cozo-lib-java | 2 years ago | |
cozo-lib-nodejs | 2 years ago | |
cozo-lib-python | 2 years ago | |
cozo-lib-swift | 2 years ago | |
cozo-lib-wasm | 2 years ago | |
cozorocks | 2 years ago | |
cozoserver | 2 years ago | |
scripts | 2 years ago | |
static | 2 years ago | |
.gitignore | 2 years ago | |
.gitmodules | 2 years ago | |
CODE_OF_CONDUCT.md | 2 years ago | |
CONTRIBUTING.md | 2 years ago | |
Cargo.lock | 2 years ago | |
Cargo.toml | 2 years ago | |
LICENSE.txt | 2 years ago | |
README.md | 2 years ago | |
VERSION | 2 years ago |
README.md
cozo
A general-purpose, transactional, relational database that uses Datalog for query, is embeddable but can also handle huge amounts of data and concurrency, and focuses on graph data and algorithms.
What does embeddable mean here?
A database is almost surely embedded if you can use it on a phone which never connects to any network (this situation is not as unusual as you might think). SQLite is embedded. MySQL/Postgres/Oracle are client-server.
A database is embedded if it runs in the same process as your main program. This is in contradistinction to client-server databases, where your program connects to a database server (maybe running on a separate machine) via a client library. Embedded databases generally require no setup and can be used in far more environments.
We say Cozo is embeddable instead of embedded since you can also use it in client-server mode, which can make better use of server resources and allow much more concurrency than in embedded mode.
Why graphs?
Because data are inherently interconnected. Most insights about data can only be obtained if you take this interconnectedness into account.
Most existing graph databases start by requiring you to shoehorn your data into the labelled-property graph model. We don't go this route because we think the traditional relational model is much easier to work with for storing data, much more versatile, and can deal with graph data just fine. Even more importantly, the most piercing insights about data usually come from graph structures implicit several levels deep in your data. The relational model, being an algebra, can deal with it just fine. The property graph model, not so much, since that model is not very composable.
What is so cool about Datalog?
Datalog can express all relational queries. Recursion in Datalog is much easier to express, much more powerful, and usually runs faster than in SQL. Datalog is also extremely composable: you can build your queries piece by piece.
Recursion is especially important for graph queries. Cozo's dialect of Datalog supercharges it even further by allowing recursion through a safe subset of aggregations, and by providing extremely efficient canned algorithms (such as PageRank) for the kinds of recursions frequently required in graph analysis.
As you learn Datalog, you will discover that the rules of Datalog are like functions in a programming language. Rules are composable, and decomposing a query into rules can make it clearer and more maintainable, with no loss in efficiency. This is unlike the monolithic approach taken by the SQL
select-from-where
in nested forms, which can sometimes read like golfing.
Learning
Usually, to learn a database, you need to install it first. This is unnecessary for Cozo as a testimony to its extreme embeddability, since you can run a complete Cozo instance in your browser, at near-native speed for most operations!
So open up the Cozo in WASM page, and then:
After you have decided that Cozo is worth experimenting with for your next project, you can scroll down to learn how to use it embedded (or not) in your favourite environment.
Teasers
If you are in a hurry and just want a taste of what querying with Cozo is like, here it is.
In the following *route
is a relation with two columns fr
and to
,
representing a route between those airports,
and FRA
is the code for Frankfurt Airport.
How many airports are directly connected to FRA
?
?[count_unique(to)] := *route{fr: 'FRA', to}
count_unique(to) |
---|
310 |
How many airports are reachable from FRA
by one stop?
?[count_unique(to)] := *route{fr: 'FRA', to: 'stop},
*route{fr: stop, to}
count_unique(to) |
---|
2222 |
How many airports are reachable from FRA
by any number of stops?
count_unique(to) |
---|
3462 |
What are the two most difficult-to-reach airports
by the minimum number of hops required,
starting from FRA
?
reachable[to] := *route{fr: 'FRA', to}
reachable[to] := reachable[stop], *route{fr: stop, to}
?[count_unique(to)] := reachable[to]
shortest_paths[to, shortest(path)] := *route{fr: 'FRA', to},
path = ['FRA', to]
shortest_paths[to, shortest(path)] := shortest_paths[stop, prev_path],
*route{fr: stop, to},
path = append(prev_path, to)
?[to, path, p_len] := shortest_paths[to, path], p_len = length(path)
:order -p_len
:limit 2
to | path | p_len |
---|---|---|
YPO | ["FRA","YYZ","YTS","YMO","YFA","ZKE","YAT","YPO"] |
8 |
BVI | ["FRA","AUH","BNE","ISA","BQL","BEU","BVI"] |
7 |
What is the shortest path between FRA
and YPO
, by actual distance travelled?
start[] <- [['FRA']]
end[] <- [['YPO]]
?[src, dst, distance, path] <~ ShortestPathDijkstra(*route[], start[], end[])
src | dst | distance | path |
---|---|---|---|
FRA | YPO | 4544.0 | ["FRA","YUL","YVO","YKQ","YMO","YFA","ZKE","YAT","YPO"] |
Cozo attempts to provide nice error messages when you make mistakes:
?[x, Y] := x = 1, y = x + 1
eval::unbound_symb_in_head × Symbol 'Y' in rule head is unbound ╭──── 1 │ ?[x, Y] := x = 1, y = x + 1 · ─ ╰──── help: Note that symbols occurring only in negated positions are not considered bound
Install
How you install Cozo depends on which environment you want to use it in. Follow the links in the table below:
Language/Environment | Official platform support | Storage |
---|---|---|
Python | Linux (x86_64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
NodeJS | Linux (x86_64, ARM64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
Web browser | Modern browsers supporting web assembly | M |
Java (JVM) | Linux (x86_64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
Clojure (JVM) | Linux (x86_64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
Android | Android (ARM64, ARMv7, x86_64, x86) | MQ |
iOS/MacOS (Swift) | iOS (ARM64, simulators), Mac (ARM64, x86_64) | MQ |
Rust | Source only, usable on any platform with std support |
MQRST |
Golang | Linux (x86_64, ARM64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
C/C++/language with C FFI | Linux (x86_64, ARM64), Mac (ARM64, x86_64), Windows (x86_64) | MQR |
Standalone HTTP server | Linux (x86_64, ARM64), Mac (ARM64, x86_64), Windows (x86_64) | MQRST |
For the storage column:
- M: in-memory, non-persistent backend
- Q: SQLite storage backend
- R: RocksDB storage backend
- S: Sled storage backend
- T: TiKV distributed storage backend
The Rust doc has some tips on choosing storage, which is helpful even if you are not using Rust. Even if a storage/platform is not officially supported, you can still try to compile your version to use, maybe with some tweaks in the code.
Architecture
Status of the project
Cozo is very young and not production-ready yet, but we encourage you to try it out for your use case. Any feedback is welcome.
Versions before 1.0 do not promise syntax/API stability or storage compatibility.
Licensing
This project is licensed under MPL-2.0 or later. See here if you are interested in contributing to the project.