Class: Factbase
- Inherits:
-
Object
- Object
- Factbase
- Defined in:
- lib/factbase.rb
Overview
A factbase, which essentially is a NoSQL one-table in-memory database with a Lisp-ish query interface.
This class is an entry point to a factbase. For example, this is how you add a new “fact” to a factbase, then put two properties into it, and then find this fact with a simple search.
fb = Factbase.new
f = fb.insert # new fact created
f.name = 'Jeff Lebowski'
f.age = 42
found = f.query('(gt 20 age)').each.to_a[0]
assert(found.age == 42)
Every fact is a key-value hash map. Every value is a non-empty set of values. Consider this example of creating a factbase with a single fact inside:
fb = Factbase.new
f = fb.insert
f.name = 'Jeff'
f.name = 'Walter'
f.age = 42
f.age = 'unknown'
f.place = 'LA'
puts f.to_json
This will print the following JSON:
{
'name': ['Jeff', 'Walter'],
'age': [42, 'unknown'],
'place: 'LA'
}
Value sets, as you can see, allow data of different types. However, there are only four types are allowed: Integer, Float, String, and Time.
A factbase may be exported to a file and then imported back:
fb1 = Factbase.new
File.binwrite(file, fb1.export)
fb2 = Factbase.new # it's empty
fb2.import(File.binread(file))
It’s impossible to delete properties of a fact. It is however possible to delete the entire fact, with the help of the query() and then delete!() methods.
It’s important to use binwrite
and binread
, because the content is a chain of bytes, not a text.
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2024 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: Fact, Inv, Looged, Pre, Query, Rollback, Rules, Syntax, Term, ToJSON, ToXML, ToYAML, Tuples
Constant Summary collapse
- VERSION =
Current version of the gem (changed by .rultor.yml on every release)
'0.0.0'
Instance Method Summary collapse
-
#dup ⇒ Factbase
Make a duplicate of this factbase.
-
#export ⇒ Bytes
Export it into a chain of bytes.
-
#import(bytes) ⇒ Object
Import from a chain of bytes.
-
#initialize(facts = []) ⇒ Factbase
constructor
Constructor.
-
#insert ⇒ Factbase::Fact
Insert a new fact and return it.
-
#query(query) ⇒ Object
Create a query capable of iterating.
-
#size ⇒ Integer
Size.
-
#txn(this = self) ⇒ Object
Run an ACID transaction, which will either modify the factbase or rollback in case of an error.
Constructor Details
#initialize(facts = []) ⇒ Factbase
Constructor.
89 90 91 92 |
# File 'lib/factbase.rb', line 89 def initialize(facts = []) @maps = facts @mutex = Mutex.new end |
Instance Method Details
#dup ⇒ Factbase
Make a duplicate of this factbase.
96 97 98 |
# File 'lib/factbase.rb', line 96 def dup Factbase.new(@maps.dup) end |
#export ⇒ Bytes
191 192 193 |
# File 'lib/factbase.rb', line 191 def export Marshal.dump(@maps) end |
#import(bytes) ⇒ Object
206 207 208 |
# File 'lib/factbase.rb', line 206 def import(bytes) @maps += Marshal.load(bytes) end |
#insert ⇒ Factbase::Fact
Insert a new fact and return it.
A fact, when inserted, is empty. It doesn’t contain any properties.
The operation is thread-safe, meaning that you different threads may insert facts parallel without breaking the consistency of the factbase.
114 115 116 117 118 119 120 121 |
# File 'lib/factbase.rb', line 114 def insert require_relative 'factbase/fact' map = {} @mutex.synchronize do @maps << map end Factbase::Fact.new(@mutex, map) end |
#query(query) ⇒ Object
Create a query capable of iterating.
There is a Lisp-like syntax, for example:
(eq title 'Object Thinking')
(gt time 2024-03-23T03:21:43Z)
(gt cost 42)
(exists seenBy)
(and
(eq foo 42.998)
(or
(gt bar 200)
(absent zzz)))
The full list of terms available in the query you can find in the README.md
file of the repository.
141 142 143 144 |
# File 'lib/factbase.rb', line 141 def query(query) require_relative 'factbase/query' Factbase::Query.new(@maps, @mutex, query) end |
#size ⇒ Integer
Size.
102 103 104 |
# File 'lib/factbase.rb', line 102 def size @maps.size end |
#txn(this = self) ⇒ Object
Run an ACID transaction, which will either modify the factbase or rollback in case of an error.
If necessary to terminate a transaction and roolback all changes, you should raise the Factbase::Rollback
exception:
fb = Factbase.new
fb.txn do |fbt|
fbt.insert. = 42
raise Factbase::Rollback
end
A the end of this script, the factbase will be empty. No facts will inserted and all changes that happened in the block will be rolled back.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/factbase.rb', line 162 def txn(this = self) copy = this.dup begin yield copy rescue Factbase::Rollback return end @mutex.synchronize do after = Marshal.load(copy.export) after.each_with_index do |m, i| @maps << {} if i >= @maps.size m.each do |k, v| @maps[i][k] = v end end end end |