factbase 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/benchmark.yml +3 -1
- data/.gitignore +6 -5
- data/.rultor.yml +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +9 -9
- data/README.md +37 -25
- data/REUSE.toml +4 -0
- data/Rakefile +10 -3
- data/benchmark/bench_factbase.rb +61 -0
- data/benchmark/bench_large_query.rb +128 -0
- data/benchmark/bench_query.rb +41 -0
- data/benchmark/bench_taped.rb +33 -0
- data/factbase.gemspec +5 -5
- data/lib/factbase/accum.rb +1 -1
- data/lib/factbase/{looged.rb → logged.rb} +8 -8
- data/lib/factbase/taped.rb +25 -25
- data/lib/factbase/term.rb +9 -1
- data/lib/factbase/terms/aggregates.rb +2 -2
- data/lib/factbase/terms/meta.rb +1 -1
- data/lib/factbase/terms/ordering.rb +1 -1
- data/lib/factbase.rb +3 -5
- data/test/factbase/terms/test_aggregates.rb +2 -2
- data/test/factbase/terms/test_aliases.rb +4 -4
- data/test/factbase/terms/test_casting.rb +1 -2
- data/test/factbase/terms/test_debug.rb +2 -2
- data/test/factbase/terms/test_defn.rb +2 -2
- data/test/factbase/terms/test_logical.rb +2 -2
- data/test/factbase/terms/test_math.rb +1 -2
- data/test/factbase/terms/test_meta.rb +2 -2
- data/test/factbase/terms/test_ordering.rb +2 -2
- data/test/factbase/terms/test_strings.rb +2 -2
- data/test/factbase/terms/test_system.rb +1 -2
- data/test/factbase/test_accum.rb +2 -2
- data/test/factbase/test_churn.rb +2 -2
- data/test/factbase/test_fact.rb +2 -2
- data/test/factbase/test_flatten.rb +2 -2
- data/test/factbase/test_inv.rb +2 -2
- data/test/factbase/{test_looged.rb → test_logged.rb} +13 -13
- data/test/factbase/test_pre.rb +2 -2
- data/test/factbase/test_query.rb +2 -2
- data/test/factbase/test_rules.rb +2 -2
- data/test/factbase/test_syntax.rb +2 -2
- data/test/factbase/test_tallied.rb +2 -2
- data/test/factbase/test_taped.rb +2 -2
- data/test/factbase/test_tee.rb +2 -2
- data/test/factbase/test_term.rb +2 -2
- data/test/factbase/test_to_json.rb +2 -2
- data/test/factbase/test_to_xml.rb +2 -2
- data/test/factbase/test_to_yaml.rb +2 -2
- data/test/test__helper.rb +3 -1
- data/test/test_factbase.rb +5 -5
- metadata +29 -29
- data/benchmarks/simple.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a3adc3cc2aec38609bd1d98b4266e18db6d3fc9e0e9eb3714f9772bf6bfee60
|
4
|
+
data.tar.gz: 7eae0cf3bbd8b1d432971f051ad08be1f39df640245d2b0f565c14a95a436aa1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee55ea6c2b5c2bd780c5b241bc16c14a2151adcb44829ec15b71c6e6fa614f9c9776e11d7c0bf76e2693a9141c745a0a233946e7b09b9961dc042f2ab22d412a
|
7
|
+
data.tar.gz: 59c44e45b9a6f2bbfe70a7fe1e649fba6c3af8b9d04e9d225289cd32d7736db1e37a138f53ed300b5639fdadbe9cef448750c9033c08aad10f4d5e13c0587e91
|
@@ -23,11 +23,13 @@ jobs:
|
|
23
23
|
bundler-cache: true
|
24
24
|
- run: bundle config set --global path "$(pwd)/vendor/bundle"
|
25
25
|
- run: bundle install --no-color
|
26
|
-
- run: bundle exec
|
26
|
+
- run: bundle exec rake benchmark > stdout.txt
|
27
27
|
- run: |
|
28
28
|
set -x
|
29
29
|
sum=$(
|
30
|
+
echo '```text'
|
30
31
|
cat stdout.txt
|
32
|
+
echo '```'
|
31
33
|
echo
|
32
34
|
echo "The results were calculated in [this GHA job][benchmark-gha]"
|
33
35
|
echo "on $(date +'%Y-%m-%d') at $(date +'%H:%M'),"
|
data/.gitignore
CHANGED
data/.rultor.yml
CHANGED
data/Gemfile
CHANGED
@@ -10,7 +10,7 @@ gem 'minitest', '5.25.4', require: false
|
|
10
10
|
gem 'minitest-reporters', '1.7.1', require: false
|
11
11
|
gem 'rake', '13.2.1', require: false
|
12
12
|
gem 'rspec-rails', '7.1.1', require: false
|
13
|
-
gem 'rubocop', '1.
|
13
|
+
gem 'rubocop', '1.73.1', require: false
|
14
14
|
gem 'rubocop-minitest', '>0', require: false
|
15
15
|
gem 'rubocop-performance', '>0', require: false
|
16
16
|
gem 'rubocop-rake', '>0', require: false
|
data/Gemfile.lock
CHANGED
@@ -2,13 +2,13 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
factbase (0.0.0)
|
5
|
-
backtrace (
|
6
|
-
decoor (
|
5
|
+
backtrace (>= 0.4.0)
|
6
|
+
decoor (>= 0.0.1)
|
7
7
|
json (~> 2.7)
|
8
|
-
loog (
|
8
|
+
loog (>= 0.6.0)
|
9
9
|
nokogiri (~> 1.10)
|
10
|
-
others (
|
11
|
-
tago (
|
10
|
+
others (>= 0.0.3)
|
11
|
+
tago (>= 0.0.2)
|
12
12
|
yaml (~> 0.3)
|
13
13
|
|
14
14
|
GEM
|
@@ -148,7 +148,7 @@ GEM
|
|
148
148
|
rspec-mocks (~> 3.13)
|
149
149
|
rspec-support (~> 3.13)
|
150
150
|
rspec-support (3.13.2)
|
151
|
-
rubocop (1.
|
151
|
+
rubocop (1.73.1)
|
152
152
|
json (~> 2.3)
|
153
153
|
language_server-protocol (~> 3.17.0.2)
|
154
154
|
lint_roller (~> 1.1.0)
|
@@ -159,7 +159,7 @@ GEM
|
|
159
159
|
rubocop-ast (>= 1.38.0, < 2.0)
|
160
160
|
ruby-progressbar (~> 1.7)
|
161
161
|
unicode-display_width (>= 2.4.0, < 4.0)
|
162
|
-
rubocop-ast (1.38.
|
162
|
+
rubocop-ast (1.38.1)
|
163
163
|
parser (>= 3.3.1.0)
|
164
164
|
rubocop-minitest (0.37.1)
|
165
165
|
lint_roller (~> 1.1)
|
@@ -197,7 +197,7 @@ GEM
|
|
197
197
|
unicode-display_width (3.1.4)
|
198
198
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
199
199
|
unicode-emoji (4.0.4)
|
200
|
-
uri (1.0.
|
200
|
+
uri (1.0.3)
|
201
201
|
useragent (0.16.11)
|
202
202
|
yaml (0.4.0)
|
203
203
|
yard (0.9.37)
|
@@ -218,7 +218,7 @@ DEPENDENCIES
|
|
218
218
|
minitest-reporters (= 1.7.1)
|
219
219
|
rake (= 13.2.1)
|
220
220
|
rspec-rails (= 7.1.1)
|
221
|
-
rubocop (= 1.
|
221
|
+
rubocop (= 1.73.1)
|
222
222
|
rubocop-minitest (> 0)
|
223
223
|
rubocop-performance (> 0)
|
224
224
|
rubocop-rake (> 0)
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ The values are either atomic literals or non-empty sets of literals.
|
|
17
17
|
It is possible to delete a fact, but impossible to delete a property
|
18
18
|
from a fact.
|
19
19
|
|
20
|
-
**ATTENTION**: The current
|
20
|
+
**ATTENTION**: The current implementation is naive and,
|
21
21
|
because of that, **very slow**. I will be very happy
|
22
22
|
if you suggest a better implementation without the change of the interface.
|
23
23
|
The `Factbase::query()` method is what mostly needs performance optimization:
|
@@ -68,9 +68,9 @@ You can make a factbase log all operations:
|
|
68
68
|
|
69
69
|
```ruby
|
70
70
|
require 'loog'
|
71
|
-
require 'factbase/
|
71
|
+
require 'factbase/logged'
|
72
72
|
log = Loog::VERBOSE
|
73
|
-
fb = Factbase::
|
73
|
+
fb = Factbase::Logged.new(Factbase.new, log)
|
74
74
|
f = fb.insert
|
75
75
|
```
|
76
76
|
|
@@ -78,9 +78,9 @@ You can also count the amount of changes made to a factbase:
|
|
78
78
|
|
79
79
|
```ruby
|
80
80
|
require 'loog'
|
81
|
-
require 'factbase/
|
81
|
+
require 'factbase/tallied'
|
82
82
|
log = Loog::VERBOSE
|
83
|
-
fb = Factbase::
|
83
|
+
fb = Factbase::Tallied.new(Factbase.new, log)
|
84
84
|
f = fb.insert
|
85
85
|
churn = fb.churn
|
86
86
|
assert churn.inserted == 1
|
@@ -137,7 +137,7 @@ Also, some simple arithmetic:
|
|
137
137
|
* `(div v1 v2)` is a division of `∏v1` by `∏v2`
|
138
138
|
|
139
139
|
It's possible to add and deduct string values to time values, like
|
140
|
-
`(plus t '2 days')` or
|
140
|
+
`(plus t '2 days')` or `(minus t '14 hours')`.
|
141
141
|
|
142
142
|
Types may be converted:
|
143
143
|
|
@@ -190,7 +190,7 @@ There are some system-level terms:
|
|
190
190
|
|
191
191
|
Read
|
192
192
|
[these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
|
193
|
-
Make sure
|
193
|
+
Make sure your build is green before you contribute
|
194
194
|
your pull request. You will need to have
|
195
195
|
[Ruby](https://www.ruby-lang.org/en/) 3.2+ and
|
196
196
|
[Bundler](https://bundler.io/) installed. Then:
|
@@ -207,26 +207,38 @@ If it's clean and you don't see any error messages, submit your pull request.
|
|
207
207
|
This is the result of the benchmark:
|
208
208
|
|
209
209
|
<!-- benchmark_begin -->
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
210
|
+
```text
|
211
|
+
|
212
|
+
|
213
|
+
Benchmarking, please wait a few seconds...
|
214
|
+
user system total real
|
215
|
+
insert 500 facts 0.017487 0.000000 0.017487 ( 0.017560)
|
216
|
+
export 500 facts 0.000517 0.000024 0.000541 ( 0.000542)
|
217
|
+
import 10456 bytes (500 facts) 0.000404 0.000000 0.000404 ( 0.000404)
|
218
|
+
insert 10 facts 0.001825 0.000000 0.001825 ( 0.001826)
|
219
|
+
query 10 times w/txn 0.049619 0.003999 0.053618 ( 0.053621)
|
220
|
+
query 10 times w/o txn 0.043671 0.000000 0.043671 ( 0.043673)
|
221
|
+
modify 10 attrs w/txn 0.030897 0.000975 0.031872 ( 0.031875)
|
222
|
+
delete 10 facts w/txn 0.012024 0.000013 0.012037 ( 0.012038)
|
223
|
+
(and (eq what 'issue-was-closed') (exists... -> 200 7.836740 0.018246 7.854986 ( 7.855478)
|
224
|
+
(and (eq what 'issue-was-closed') (exists... -> 200/txn 8.817858 0.014003 8.831861 ( 8.832345)
|
225
|
+
(and (eq what 'issue-was-closed') (exists... -> zero 10.051354 0.017001 10.068355 ( 10.069083)
|
226
|
+
(and (eq what 'issue-was-closed') (exists... -> zero/txn 10.667497 0.016002 10.683499 ( 10.684435)
|
227
|
+
(gt time '2024-03-23T03:21:43Z') 0.008827 0.000000 0.008827 ( 0.008827)
|
228
|
+
(gt cost 50) 0.009784 0.000002 0.009786 ( 0.009789)
|
229
|
+
(eq title 'Object Thinking 5000') 0.008519 0.000000 0.008519 ( 0.008523)
|
230
|
+
(and (eq foo 42.998) (or (gt bar 200) (absent zzz))) 0.013591 0.000000 0.013591 ( 0.013595)
|
231
|
+
(eq id (agg (always) (max id))) 0.021419 0.000000 0.021419 ( 0.021423)
|
232
|
+
(join "c<=cost,b<=bar" (eq id (agg (always) (max id)))) 0.141201 0.000000 0.141201 ( 0.141216)
|
233
|
+
delete! 0.004025 0.000000 0.004025 ( 0.004027)
|
234
|
+
Taped.append() x50000 0.038128 0.001997 0.040125 ( 0.040130)
|
235
|
+
Taped.each() x125 1.510154 0.001001 1.511155 ( 1.511313)
|
236
|
+
Taped.delete_if() x375 0.883123 0.000000 0.883123 ( 0.883229)
|
237
|
+
```
|
226
238
|
|
227
239
|
The results were calculated in [this GHA job][benchmark-gha]
|
228
|
-
on 2025-
|
240
|
+
on 2025-03-03 at 15:04,
|
229
241
|
on Linux with 4 CPUs.
|
230
242
|
<!-- benchmark_end -->
|
231
243
|
|
232
|
-
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/
|
244
|
+
[benchmark-gha]: https://github.com/yegor256/factbase/actions/runs/13633891920
|
data/REUSE.toml
CHANGED
@@ -4,13 +4,17 @@
|
|
4
4
|
version = 1
|
5
5
|
[[annotations]]
|
6
6
|
path = [
|
7
|
+
"**/*.csv",
|
7
8
|
"**/*.jpg",
|
8
9
|
"**/*.json",
|
9
10
|
"**/*.md",
|
10
11
|
"**/*.pdf",
|
11
12
|
"**/*.png",
|
12
13
|
"**/*.svg",
|
14
|
+
"**/*.txt",
|
15
|
+
"**/*.vm",
|
13
16
|
"**/.gitignore",
|
17
|
+
"**/CNAME",
|
14
18
|
".gitattributes",
|
15
19
|
".gitignore",
|
16
20
|
".gitleaksignore",
|
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ def version
|
|
15
15
|
Gem::Specification.load(Dir['*.gemspec'].first).version
|
16
16
|
end
|
17
17
|
|
18
|
-
task default: %i[clean test rubocop yard]
|
18
|
+
task default: %i[clean test rubocop yard benchmark]
|
19
19
|
|
20
20
|
require 'rake/testtask'
|
21
21
|
desc 'Run all unit tests'
|
@@ -40,7 +40,14 @@ RuboCop::RakeTask.new(:rubocop) do |task|
|
|
40
40
|
task.requires << 'rubocop-rspec'
|
41
41
|
end
|
42
42
|
|
43
|
-
desc '
|
43
|
+
desc 'Benchmark them all'
|
44
44
|
task :benchmark do
|
45
|
-
|
45
|
+
fb = Factbase.new
|
46
|
+
require 'benchmark'
|
47
|
+
Benchmark.bm(60) do |b|
|
48
|
+
Dir['benchmark/bench_*.rb'].each do |f|
|
49
|
+
require_relative f
|
50
|
+
Kernel.send(File.basename(f).gsub(/\.rb$/, '').to_sym, b, fb)
|
51
|
+
end
|
52
|
+
end
|
46
53
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../lib/factbase'
|
7
|
+
|
8
|
+
def bench_factbase(bmk, fb)
|
9
|
+
total = 500
|
10
|
+
bmk.report("insert #{total} facts") do
|
11
|
+
total.times do
|
12
|
+
fact = fb.insert
|
13
|
+
fact.foo = rand(0.0..100.0).round(3)
|
14
|
+
fact.bar = rand(100..300)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
bin = nil
|
19
|
+
bmk.report("export #{total} facts") do
|
20
|
+
bin = fb.export
|
21
|
+
end
|
22
|
+
bmk.report("import #{bin.size} bytes (#{total} facts)") do
|
23
|
+
fb2 = Factbase.new
|
24
|
+
fb2.import(bin)
|
25
|
+
end
|
26
|
+
|
27
|
+
actions = 10
|
28
|
+
bmk.report("insert #{actions} facts") do
|
29
|
+
fb.txn do |fbt|
|
30
|
+
actions.times do
|
31
|
+
fbt.insert.z = rand(0..100)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
bmk.report("query #{actions} times w/txn") do
|
36
|
+
fb.txn do |fbt|
|
37
|
+
actions.times do |i|
|
38
|
+
fbt.query("(gt foo #{i})").each.to_a.each
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
bmk.report("query #{actions} times w/o txn") do
|
43
|
+
actions.times do |i|
|
44
|
+
fb.query("(gt foo #{i})").each.to_a.each
|
45
|
+
end
|
46
|
+
end
|
47
|
+
bmk.report("modify #{actions} attrs w/txn") do
|
48
|
+
fb.txn do |fbt|
|
49
|
+
actions.times do |i|
|
50
|
+
fbt.query("(gt foo #{i})").each.to_a.first.bar = 55
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
bmk.report("delete #{actions} facts w/txn") do
|
55
|
+
fb.txn do |fbt|
|
56
|
+
actions.times do |i|
|
57
|
+
fbt.query("(gt foo #{100 - i})").delete!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../lib/factbase'
|
7
|
+
require_relative '../lib/factbase/logged'
|
8
|
+
|
9
|
+
def bench_large_query(bmk, fb)
|
10
|
+
total = 200
|
11
|
+
repo = 'foo'
|
12
|
+
total.times do |i|
|
13
|
+
f = fb.insert
|
14
|
+
f.id = i
|
15
|
+
f.where = 'github'
|
16
|
+
f.what = 'issue-was-closed'
|
17
|
+
f.who = 444
|
18
|
+
f.when = Time.now - (i * rand(1_000..100_000))
|
19
|
+
f.issue = i
|
20
|
+
f.repository = repo
|
21
|
+
end
|
22
|
+
total.times do |i|
|
23
|
+
f = fb.insert
|
24
|
+
f.id = i + total
|
25
|
+
f.where = 'github'
|
26
|
+
f.what = 'label-was-attached'
|
27
|
+
f.when = Time.now - (i * rand(1_000..100_000))
|
28
|
+
f.issue = i
|
29
|
+
f.repository = repo
|
30
|
+
f.label = 'bug'
|
31
|
+
end
|
32
|
+
total.times do |i|
|
33
|
+
f = fb.insert
|
34
|
+
f.id = i + (total * 2)
|
35
|
+
f.where = 'github'
|
36
|
+
f.what = 'issue-was-opened'
|
37
|
+
f.who = 555
|
38
|
+
f.when = Time.now - (i * rand(1_000..100_000))
|
39
|
+
f.issue = i
|
40
|
+
f.repository = repo
|
41
|
+
end
|
42
|
+
total.times do |i|
|
43
|
+
f = fb.insert
|
44
|
+
f.id = i + (total * 3)
|
45
|
+
f.where = 'github'
|
46
|
+
f.what = 'issue-was-assigned'
|
47
|
+
f.who = 666
|
48
|
+
f.when = Time.now - (i * rand(1_000..100_000))
|
49
|
+
f.issue = i
|
50
|
+
f.repository = repo
|
51
|
+
end
|
52
|
+
|
53
|
+
q = "(and
|
54
|
+
(eq what 'issue-was-closed')
|
55
|
+
(exists where)
|
56
|
+
(exists who)
|
57
|
+
(exists when)
|
58
|
+
(exists issue)
|
59
|
+
(exists repository)
|
60
|
+
(join 'label' (and
|
61
|
+
(eq what 'label-was-attached')
|
62
|
+
(eq issue $issue)
|
63
|
+
(eq where $where)
|
64
|
+
(eq repository $repository)
|
65
|
+
(or (eq label 'bug') (eq label 'enhancement') (eq label 'question'))))
|
66
|
+
(exists label)
|
67
|
+
(join 'opened_when<=when,opener<=who' (and
|
68
|
+
(eq what 'issue-was-opened')
|
69
|
+
(eq where $where)
|
70
|
+
(eq issue $issue)
|
71
|
+
(eq repository $repository)))
|
72
|
+
(exists opener)
|
73
|
+
(join 'assigned_when<=when,assigner<=who' (and
|
74
|
+
(eq what 'issue-was-assigned')
|
75
|
+
(eq where $where)
|
76
|
+
(eq issue $issue)
|
77
|
+
(eq repository $repository)))
|
78
|
+
(exists assigner)
|
79
|
+
(as seconds (to_integer (minus when assigned_when)))
|
80
|
+
(as closer who)
|
81
|
+
(as who assigner)
|
82
|
+
(empty (and
|
83
|
+
(eq what 'bug-was-resolved')
|
84
|
+
(eq where $where)
|
85
|
+
(eq issue $issue)
|
86
|
+
(eq repository $repository))))".gsub(/\s+/, ' ')
|
87
|
+
|
88
|
+
cycles = 1
|
89
|
+
bmk.report("#{q[0..40]}... -> #{total}") do
|
90
|
+
cycles.times do
|
91
|
+
t = fb.query(q).each.to_a.size
|
92
|
+
raise "Found #{t} facts, expected to find #{total}" unless t == total
|
93
|
+
end
|
94
|
+
end
|
95
|
+
bmk.report("#{q[0..40]}... -> #{total}/txn") do
|
96
|
+
cycles.times do
|
97
|
+
fb.txn do |fbt|
|
98
|
+
t = fbt.query(q).each.to_a.size
|
99
|
+
raise "Found #{t} facts, expected to find #{total}" unless t == total
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
total.times do |i|
|
105
|
+
f = fb.insert
|
106
|
+
f.id = i
|
107
|
+
f.where = 'github'
|
108
|
+
f.what = 'bug-was-resolved'
|
109
|
+
f.who = 444
|
110
|
+
f.when = Time.now - (i * rand(1_000..100_000))
|
111
|
+
f.issue = i
|
112
|
+
f.repository = repo
|
113
|
+
end
|
114
|
+
bmk.report("#{q[0..40]}... -> zero") do
|
115
|
+
cycles.times do
|
116
|
+
t = fb.query(q).each.to_a.size
|
117
|
+
raise "Found #{t} facts, expected to find nothing" unless t.zero?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
bmk.report("#{q[0..40]}... -> zero/txn") do
|
121
|
+
cycles.times do
|
122
|
+
fb.txn do |fbt|
|
123
|
+
t = fbt.query(q).each.to_a.size
|
124
|
+
raise "Found #{t} facts, expected to find nothing" unless t.zero?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../lib/factbase'
|
7
|
+
|
8
|
+
def bench_query(bmk, fb)
|
9
|
+
total = 500
|
10
|
+
total.times do |i|
|
11
|
+
f = fb.insert
|
12
|
+
f.id = i
|
13
|
+
f.title = "Object Thinking #{i}"
|
14
|
+
f.time = Time.now.iso8601
|
15
|
+
f.cost = rand(1..100)
|
16
|
+
f.foo = rand(0.0..100.0).round(3)
|
17
|
+
f.bar = rand(100..300)
|
18
|
+
f.seenBy = "User#{i}" if i.even?
|
19
|
+
f.zzz = "Extra#{i}" if (i % 10).zero?
|
20
|
+
end
|
21
|
+
|
22
|
+
runs = 10
|
23
|
+
[
|
24
|
+
'(gt time \'2024-03-23T03:21:43Z\')',
|
25
|
+
'(gt cost 50)',
|
26
|
+
'(eq title \'Object Thinking 5000\')',
|
27
|
+
'(and (eq foo 42.998) (or (gt bar 200) (absent zzz)))',
|
28
|
+
'(eq id (agg (always) (max id)))',
|
29
|
+
'(join "c<=cost,b<=bar" (eq id (agg (always) (max id))))'
|
30
|
+
].each do |q|
|
31
|
+
bmk.report(q) do
|
32
|
+
runs.times do
|
33
|
+
fb.query(q).each.to_a
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
bmk.report('delete!') do
|
39
|
+
fb.query('(gt foo 50)').delete!
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
5
|
+
|
6
|
+
require_relative '../lib/factbase'
|
7
|
+
require_relative '../lib/factbase/taped'
|
8
|
+
|
9
|
+
def bench_taped(bmk, _fb)
|
10
|
+
maps = []
|
11
|
+
taped = Factbase::Taped.new(maps)
|
12
|
+
|
13
|
+
cycles = 50_000
|
14
|
+
bmk.report("Taped.append() x#{cycles}") do
|
15
|
+
cycles.times do
|
16
|
+
taped << { foo: rand(0..100) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
cycles /= 400
|
21
|
+
bmk.report("Taped.each() x#{cycles}") do
|
22
|
+
cycles.times do
|
23
|
+
taped.each.to_a
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
cycles *= 3
|
28
|
+
bmk.report("Taped.delete_if() x#{cycles}") do
|
29
|
+
cycles.times do
|
30
|
+
taped.delete_if { |m| m[:foo] < 50 }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/factbase.gemspec
CHANGED
@@ -25,13 +25,13 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.files = `git ls-files`.split($RS)
|
26
26
|
s.rdoc_options = ['--charset=UTF-8']
|
27
27
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
28
|
-
s.add_dependency 'backtrace', '
|
29
|
-
s.add_dependency 'decoor', '
|
28
|
+
s.add_dependency 'backtrace', '>=0.4.0'
|
29
|
+
s.add_dependency 'decoor', '>=0.0.1'
|
30
30
|
s.add_dependency 'json', '~>2.7'
|
31
|
-
s.add_dependency 'loog', '
|
31
|
+
s.add_dependency 'loog', '>=0.6.0'
|
32
32
|
s.add_dependency 'nokogiri', '~>1.10'
|
33
|
-
s.add_dependency 'others', '
|
34
|
-
s.add_dependency 'tago', '
|
33
|
+
s.add_dependency 'others', '>=0.0.3'
|
34
|
+
s.add_dependency 'tago', '>=0.0.2'
|
35
35
|
s.add_dependency 'yaml', '~>0.3'
|
36
36
|
s.metadata['rubygems_mfa_required'] = 'true'
|
37
37
|
end
|
data/lib/factbase/accum.rb
CHANGED
@@ -41,7 +41,7 @@ class Factbase::Accum
|
|
41
41
|
kk = args[1].to_s
|
42
42
|
vv = @props[kk].nil? ? [] : @props[kk]
|
43
43
|
vvv = @fact.method_missing(*args)
|
44
|
-
vvv = [vvv] unless vvv.nil? || vvv.respond_to?(:
|
44
|
+
vvv = [vvv] unless vvv.nil? || vvv.respond_to?(:to_a)
|
45
45
|
vv += vvv.to_a unless vvv.nil?
|
46
46
|
vv.uniq!
|
47
47
|
vv.empty? ? nil : vv
|
@@ -14,7 +14,7 @@ require_relative 'syntax'
|
|
14
14
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
15
15
|
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
16
16
|
# License:: MIT
|
17
|
-
class Factbase::
|
17
|
+
class Factbase::Logged
|
18
18
|
def initialize(fb, loog)
|
19
19
|
raise 'The "fb" is nil' if fb.nil?
|
20
20
|
@fb = fb
|
@@ -42,7 +42,7 @@ class Factbase::Looged
|
|
42
42
|
r =
|
43
43
|
@fb.txn do |fbt|
|
44
44
|
id = fbt.object_id
|
45
|
-
yield Factbase::
|
45
|
+
yield Factbase::Logged.new(fbt, @loog)
|
46
46
|
rescue Factbase::Rollback => e
|
47
47
|
rollback = true
|
48
48
|
raise e
|
@@ -102,7 +102,7 @@ class Factbase::Looged
|
|
102
102
|
q = Factbase::Syntax.new(@fb, @expr).to_term.to_s
|
103
103
|
r = nil
|
104
104
|
tail =
|
105
|
-
Factbase::
|
105
|
+
Factbase::Logged.elapsed do
|
106
106
|
r = @fb.query(@expr).one(params)
|
107
107
|
end
|
108
108
|
if r.nil?
|
@@ -118,10 +118,10 @@ class Factbase::Looged
|
|
118
118
|
if block_given?
|
119
119
|
r = nil
|
120
120
|
tail =
|
121
|
-
Factbase::
|
121
|
+
Factbase::Logged.elapsed do
|
122
122
|
r = @fb.query(@expr).each(params, &)
|
123
123
|
end
|
124
|
-
raise ".each of #{@
|
124
|
+
raise ".each of #{@expr.class} returned #{r.class}" unless r.is_a?(Integer)
|
125
125
|
if r.zero?
|
126
126
|
@loog.debug("Nothing found by '#{q}' #{tail}")
|
127
127
|
else
|
@@ -131,7 +131,7 @@ class Factbase::Looged
|
|
131
131
|
else
|
132
132
|
array = []
|
133
133
|
tail =
|
134
|
-
Factbase::
|
134
|
+
Factbase::Logged.elapsed do
|
135
135
|
@fb.query(@expr).each(params) do |f|
|
136
136
|
array << f
|
137
137
|
end
|
@@ -149,10 +149,10 @@ class Factbase::Looged
|
|
149
149
|
r = nil
|
150
150
|
before = @fb.size
|
151
151
|
tail =
|
152
|
-
Factbase::
|
152
|
+
Factbase::Logged.elapsed do
|
153
153
|
r = @fb.query(@expr).delete!
|
154
154
|
end
|
155
|
-
raise ".delete! of #{@
|
155
|
+
raise ".delete! of #{@expr.class} returned #{r.class}" unless r.is_a?(Integer)
|
156
156
|
if before.zero?
|
157
157
|
@loog.debug("There were no facts, nothing deleted by '#{@expr}' #{tail}")
|
158
158
|
elsif r.zero?
|