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.
It is NOT thread-safe!
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2024 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: Accum, Fact, Flatten, Inv, Looged, Pre, Query, Rollback, Rules, Syntax, Tee, Term, ToJSON, ToXML, ToYAML
Constant Summary collapse
- VERSION =
Current version of the gem (changed by .rultor.yml on every release)
'0.4.0'
Instance Method Summary collapse
-
#dup ⇒ Factbase
Make a deep 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, the total number of facts in the factbase.
-
#txn(this = self) ⇒ Boolean
Run an ACID transaction, which will either modify the factbase or rollback in case of an error.
Constructor Details
#initialize(facts = []) ⇒ Factbase
Constructor.
91 92 93 94 |
# File 'lib/factbase.rb', line 91 def initialize(facts = []) @maps = facts @mutex = Mutex.new end |
Instance Method Details
#dup ⇒ Factbase
Make a deep duplicate of this factbase.
98 99 100 |
# File 'lib/factbase.rb', line 98 def dup Factbase.new(@maps.map { |m| m.transform_values(&:dup) }) end |
#export ⇒ Bytes
201 202 203 |
# File 'lib/factbase.rb', line 201 def export Marshal.dump(@maps) end |
#import(bytes) ⇒ Object
216 217 218 219 |
# File 'lib/factbase.rb', line 216 def import(bytes) raise 'Empty input, cannot load a factbase' if bytes.empty? @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.
116 117 118 119 120 121 122 123 |
# File 'lib/factbase.rb', line 116 def insert map = {} @mutex.synchronize do @maps << map end require_relative 'factbase/fact' 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.
143 144 145 146 |
# File 'lib/factbase.rb', line 143 def query(query) require_relative 'factbase/query' Factbase::Query.new(@maps, @mutex, query) end |
#size ⇒ Integer
Size, the total number of facts in the factbase.
104 105 106 |
# File 'lib/factbase.rb', line 104 def size @maps.size end |
#txn(this = self) ⇒ Boolean
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.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/factbase.rb', line 165 def txn(this = self) copy = this.dup begin yield copy rescue Factbase::Rollback return false end modified = false @mutex.synchronize do after = Marshal.load(copy.export) after.each_with_index do |m, i| if i >= @maps.size @maps << {} modified = true end m.each do |k, vv| next if @maps[i][k] == vv @maps[i][k] = vv modified = true end end end modified end |