Single-Table NoSQL In-Memory Database
This Ruby gem manages an in-memory database of facts. A fact is simply a map of properties and values. The values are either atomic literals or non-empty sets of literals. It is possible to delete a fact, but impossible to delete a property from a fact.
ATTENTION: The current implemention is naive and,
because of that, very slow. I will be very happy
if you suggest a better implementation without the change of the interface.
The Factbase::query()
method is what mostly needs performance optimization:
currently it simply iterates through all facts in the factbase in order
to find those that match the provided terms. Obviously,
even a simple indexing may significantly increase performance.
Here is how you use it (it's thread-safe, by the way):
fb = Factbase.new
f = fb.insert
f.kind = 'book'
f.title = 'Object Thinking'
fb.query('(eq kind "book")').each do |f|
f.seen = true
end
fb.insert
fb.query('(not (exists seen))').each do |f|
f.title = 'Elegant Objects'
end
You can save the factbase to the disc and then load it back:
file = '/tmp/simple.fb'
f1 = Factbase.new
f = f1.insert
f.foo = 42
File.save(file, f1.export)
f2 = Factbase.new
f2.import(File.read(file))
assert(f2.query('(eq foo 42)').each.to_a.size == 1)
There are some boolean terms available in a query (they return either TRUE or FALSE):
(always)
and(never)
are "true" and "false"(not t)
inverses thet
if it's boolean (exception otherwise)(or t1 t2 ...)
returns true if at least one argument is true(and t1 t2 ...)
returns true if all arguments are true(when t1 t2)
returns true ift1
is true andt2
is true ort1
is false(exists k)
returns true ifk
property exists in the fact(absent k)
returns true ifk
property is absent(eq a b)
returns true ifa
equals tob
(lt a b)
returns true ifa
is less thanb
(gt a b)
returns true ifa
is greater thanb
(many a)
return true if there are many values in thea
property(one a)
returns true if there is only one value in thea
property(matches a re)
returns true whena
matches regular expressionre
There are a few terms that return non-boolean values:
(at i a)
returns thei
-th value of thea
property(size k)
returns cardinality ofk
property (zero if property is absent)(type a)
returns type ofa
("String", "Integer", "Float", or "Time")(either a b)
returnsb
ifa
isnil
Also, some simple arithmetic:
(plus a b)
is a sum ofa
andb
(minus a b)
is a deducation ofb
froma
(times a b)
is a multiplication ofa
andb
(div a b)
is a division ofa
byb
One term is for meta-programming:
(defn foo "self.to_s")
defines a new term using Ruby syntax and returns true(undef foo)
undefines a term (nothing happens if it's not defined yet)
There are terms that are history of search aware:
(prev a)
returns the value ofa
in the previously seen fact(unique k)
returns true if the value ofk
property hasn't been seen yet
There are also terms that match the entire factbase
and must be used inside the (agg ..)
term:
(count)
returns the tally of facts(max k)
returns the maximum value of thek
property in all facts(min k)
returns the minimum(sum k)
returns the arithmetic sum of all values of thek
property
The agg
term enables sub-queries by evaluating the first argument (term)
over all available facts, passing the entire subset to the second argument,
and then returning the result as an atomic value:
(lt age (agg (eq gender 'F') (max age)))
selects all facts where theage
is smaller than the maximumage
of all women(eq id (agg (always) (max id)))
selects the fact with the largestid
How to contribute
Read these guidelines. Make sure you build is green before you contribute your pull request. You will need to have Ruby 3.2+ and Bundler installed. Then:
bundle update
bundle exec rake
If it's clean and you don't see any error messages, submit your pull request.