factbase 0.15.0 → 0.15.1
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/Gemfile.lock +5 -5
- data/factbase.gemspec +4 -4
- data/lib/factbase/fact.rb +1 -1
- data/lib/factbase/fact_as_yaml.rb +10 -1
- data/lib/factbase/indexed/indexed_term.rb +6 -1
- data/lib/factbase/version.rb +1 -1
- metadata +7 -87
- data/.0pdd.yml +0 -8
- data/.gitattributes +0 -7
- data/.github/publish-benchmark.sh +0 -24
- data/.github/workflows/actionlint.yml +0 -25
- data/.github/workflows/benchmark.yml +0 -36
- data/.github/workflows/codecov.yml +0 -27
- data/.github/workflows/copyrights.yml +0 -19
- data/.github/workflows/markdown-lint.yml +0 -23
- data/.github/workflows/pdd.yml +0 -19
- data/.github/workflows/rake.yml +0 -28
- data/.github/workflows/reuse.yml +0 -19
- data/.github/workflows/typos.yml +0 -19
- data/.github/workflows/xcop.yml +0 -19
- data/.github/workflows/yamllint.yml +0 -19
- data/.gitignore +0 -11
- data/.pdd +0 -7
- data/.rubocop.yml +0 -51
- data/.rultor.yml +0 -27
- data/benchmark/bench_factbase.rb +0 -61
- data/benchmark/bench_large_query.rb +0 -128
- data/benchmark/bench_query.rb +0 -57
- data/benchmark/bench_taped.rb +0 -33
- data/fixtures/stories/agg.yml +0 -17
- data/fixtures/stories/always.yml +0 -16
- data/fixtures/stories/and.yml +0 -19
- data/fixtures/stories/as.yml +0 -16
- data/fixtures/stories/count.yml +0 -18
- data/fixtures/stories/empty.yml +0 -23
- data/fixtures/stories/eq.yml +0 -30
- data/fixtures/stories/gt.yml +0 -18
- data/fixtures/stories/join.yml +0 -22
- data/fixtures/stories/max.yml +0 -14
- data/fixtures/stories/min.yml +0 -14
- data/fixtures/stories/nth.yml +0 -14
- data/fixtures/stories/one.yml +0 -14
- data/fixtures/stories/or.yml +0 -18
- data/fixtures/stories/sprintf.yml +0 -12
- data/fixtures/stories/sum.yml +0 -14
- data/fixtures/stories/unique.yml +0 -30
- data/renovate.json +0 -6
- data/test/factbase/cached/test_cached_factbase.rb +0 -44
- data/test/factbase/cached/test_cached_query.rb +0 -100
- data/test/factbase/indexed/test_indexed_fact.rb +0 -23
- data/test/factbase/indexed/test_indexed_factbase.rb +0 -96
- data/test/factbase/indexed/test_indexed_query.rb +0 -208
- data/test/factbase/indexed/test_indexed_term.rb +0 -140
- data/test/factbase/sync/test_sync_factbase.rb +0 -22
- data/test/factbase/sync/test_sync_query.rb +0 -30
- data/test/factbase/terms/test_aggregates.rb +0 -63
- data/test/factbase/terms/test_aliases.rb +0 -58
- data/test/factbase/terms/test_casting.rb +0 -33
- data/test/factbase/terms/test_debug.rb +0 -111
- data/test/factbase/terms/test_defn.rb +0 -48
- data/test/factbase/terms/test_logical.rb +0 -57
- data/test/factbase/terms/test_math.rb +0 -122
- data/test/factbase/terms/test_meta.rb +0 -70
- data/test/factbase/terms/test_ordering.rb +0 -44
- data/test/factbase/terms/test_strings.rb +0 -36
- data/test/factbase/terms/test_system.rb +0 -31
- data/test/factbase/test_accum.rb +0 -74
- data/test/factbase/test_churn.rb +0 -41
- data/test/factbase/test_fact.rb +0 -108
- data/test/factbase/test_fact_as_yaml.rb +0 -77
- data/test/factbase/test_flatten.rb +0 -28
- data/test/factbase/test_impatient.rb +0 -176
- data/test/factbase/test_inv.rb +0 -44
- data/test/factbase/test_logged.rb +0 -155
- data/test/factbase/test_pre.rb +0 -35
- data/test/factbase/test_query.rb +0 -445
- data/test/factbase/test_rules.rb +0 -128
- data/test/factbase/test_syntax.rb +0 -166
- data/test/factbase/test_tallied.rb +0 -81
- data/test/factbase/test_taped.rb +0 -72
- data/test/factbase/test_tee.rb +0 -85
- data/test/factbase/test_term.rb +0 -87
- data/test/factbase/test_to_json.rb +0 -35
- data/test/factbase/test_to_xml.rb +0 -89
- data/test/factbase/test_to_yaml.rb +0 -39
- data/test/test__helper.rb +0 -41
- data/test/test_factbase.rb +0 -509
@@ -1,100 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require 'loog'
|
7
|
-
require_relative '../../test__helper'
|
8
|
-
require_relative '../../../lib/factbase'
|
9
|
-
require_relative '../../../lib/factbase/logged'
|
10
|
-
require_relative '../../../lib/factbase/cached/cached_factbase'
|
11
|
-
require_relative '../../../lib/factbase/indexed/indexed_factbase'
|
12
|
-
require_relative '../../../lib/factbase/sync/sync_factbase'
|
13
|
-
|
14
|
-
# Query test.
|
15
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
16
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
17
|
-
# License:: MIT
|
18
|
-
class TestCachedQuery < Factbase::Test
|
19
|
-
def test_queries_many_times
|
20
|
-
fb = Factbase::CachedFactbase.new(Factbase.new)
|
21
|
-
total = 5
|
22
|
-
total.times { fb.insert }
|
23
|
-
total.times do
|
24
|
-
assert_equal(5, fb.query('(always)').each.to_a.size)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_negates_correctly
|
29
|
-
fb = Factbase::CachedFactbase.new(Factbase.new)
|
30
|
-
fb.insert.foo = 42
|
31
|
-
3.times do
|
32
|
-
assert_equal(1, fb.query('(always)').each.to_a.size)
|
33
|
-
assert_equal(0, fb.query('(not (always))').each.to_a.size)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_aggregates_too
|
38
|
-
fb = Factbase::IndexedFactbase.new(Factbase::CachedFactbase.new(Factbase.new))
|
39
|
-
10_000.times do |i|
|
40
|
-
f = fb.insert
|
41
|
-
f.foo = i
|
42
|
-
f.hello = 1
|
43
|
-
end
|
44
|
-
3.times do
|
45
|
-
q = fb.query('(eq foo (agg (exists hello) (min foo)))')
|
46
|
-
assert_equal(1, q.each.to_a.size)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_joins_too
|
51
|
-
fb = Factbase::IndexedFactbase.new(Factbase::CachedFactbase.new(Factbase.new))
|
52
|
-
total = 10
|
53
|
-
total.times do |i|
|
54
|
-
f = fb.insert
|
55
|
-
f.foo = i
|
56
|
-
f.hello = 1
|
57
|
-
end
|
58
|
-
3.times do
|
59
|
-
assert_equal(total, fb.query('(join "bar<=foo" (eq foo (agg (exists hello) (min foo))))').each.to_a.size)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_works_with_logging
|
64
|
-
fb = Factbase::CachedFactbase.new(Factbase::Logged.new(Factbase.new, Loog::NULL))
|
65
|
-
total = 10
|
66
|
-
total.times do |i|
|
67
|
-
f = fb.insert
|
68
|
-
f.foo = i
|
69
|
-
f.hello = 1
|
70
|
-
end
|
71
|
-
3.times do
|
72
|
-
[
|
73
|
-
'(exists foo)',
|
74
|
-
'(and (gt foo -99) (exists hello))',
|
75
|
-
'(and (lt hello 1000.44) (exists foo) (not (exists bar)))'
|
76
|
-
].each do |q|
|
77
|
-
assert_equal(total, fb.query(q).each.to_a.size)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_caches_while_being_decorated
|
83
|
-
fb = Factbase::SyncFactbase.new(Factbase::CachedFactbase.new(Factbase.new))
|
84
|
-
10_000.times do |i|
|
85
|
-
f = fb.insert
|
86
|
-
f.foo = i
|
87
|
-
f.hello = 1
|
88
|
-
end
|
89
|
-
3.times do
|
90
|
-
assert_equal(1, fb.query('(eq foo (agg (exists hello) (min foo)))').each.to_a.size)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_deletes_too
|
95
|
-
fb = Factbase::CachedFactbase.new(Factbase.new)
|
96
|
-
fb.insert.foo = 1
|
97
|
-
fb.query('(eq foo 1)').delete!
|
98
|
-
assert_equal(0, fb.query('(always)').each.to_a.size)
|
99
|
-
end
|
100
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase'
|
8
|
-
require_relative '../../../lib/factbase/fact'
|
9
|
-
require_relative '../../../lib/factbase/indexed/indexed_fact'
|
10
|
-
|
11
|
-
# Fact test.
|
12
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
|
-
# License:: MIT
|
15
|
-
class TestIndexedFact < Factbase::Test
|
16
|
-
def test_updates_origin
|
17
|
-
origin = Factbase::Fact.new({})
|
18
|
-
fact = Factbase::IndexedFact.new(origin, {})
|
19
|
-
fact.foo = 42
|
20
|
-
refute_nil(origin['foo'])
|
21
|
-
assert_equal(42, origin.foo)
|
22
|
-
end
|
23
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require 'elapsed'
|
7
|
-
require 'loog'
|
8
|
-
require 'timeout'
|
9
|
-
require_relative '../../test__helper'
|
10
|
-
require_relative '../../../lib/factbase'
|
11
|
-
require_relative '../../../lib/factbase/indexed/indexed_factbase'
|
12
|
-
|
13
|
-
# Factbase test.
|
14
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
15
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
16
|
-
# License:: MIT
|
17
|
-
class TestIndexedFactbase < Factbase::Test
|
18
|
-
def test_queries_after_update
|
19
|
-
origin = Factbase.new
|
20
|
-
fb = Factbase::IndexedFactbase.new(origin)
|
21
|
-
fb.insert.foo = 42
|
22
|
-
fb.query('(exists foo)').each do |f|
|
23
|
-
f.bar = 33
|
24
|
-
end
|
25
|
-
refute_empty(origin.query('(exists bar)').each.to_a)
|
26
|
-
refute_empty(fb.query('(exists bar)').each.to_a)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_queries_after_update_in_txn
|
30
|
-
[
|
31
|
-
'(exists boom)',
|
32
|
-
'(one boom)',
|
33
|
-
'(and (exists boom) (exists boom))',
|
34
|
-
'(and (exists boom) (exists boom) (exists boom))',
|
35
|
-
'(and (one boom) (one boom))',
|
36
|
-
'(and (one boom) (one foo))',
|
37
|
-
'(and (one boom) (one boom) (one boom))',
|
38
|
-
'(and (one boom) (one boom) (one boom) (one foo))',
|
39
|
-
'(and (one boom) (exists boom))',
|
40
|
-
'(and (exists boom) (one boom) (one boom))',
|
41
|
-
'(and (exists boom) (exists boom) (one boom))',
|
42
|
-
'(and (eq foo 42) (exists boom) (one boom) (not (exists bar)))'
|
43
|
-
].each do |q|
|
44
|
-
origin = Factbase.new
|
45
|
-
fb = Factbase::IndexedFactbase.new(origin)
|
46
|
-
f = fb.insert
|
47
|
-
f.foo = 42
|
48
|
-
f.boom = 33
|
49
|
-
fb.txn do |fbt|
|
50
|
-
fbt.query(q).each do |n|
|
51
|
-
n.bar = n.foo + 1
|
52
|
-
end
|
53
|
-
end
|
54
|
-
refute_empty(origin.query('(exists bar)').each.to_a, q)
|
55
|
-
refute_empty(fb.query('(exists bar)').each.to_a, q)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_queries_after_insert_in_txn
|
60
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
61
|
-
fb.txn(&:insert)
|
62
|
-
refute_empty(fb.query('(always)').each.to_a)
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_works_with_huge_dataset
|
66
|
-
fb = Factbase.new
|
67
|
-
fb = Factbase::IndexedFactbase.new(fb)
|
68
|
-
10_000.times do |i|
|
69
|
-
fb.insert.then do |f|
|
70
|
-
f.id = i
|
71
|
-
f.foo = [42, 1, 256, 7, 99].sample
|
72
|
-
f.bar = [42, 13, 88, 19, 93].sample
|
73
|
-
f.rarely = rand if rand > 0.95
|
74
|
-
f.often = rand if rand > 0.05
|
75
|
-
end
|
76
|
-
end
|
77
|
-
[
|
78
|
-
'(and (eq foo 42) (exists bar))',
|
79
|
-
'(and (eq foo 42) (exists rarely))',
|
80
|
-
'(and (eq foo 42) (exists often))',
|
81
|
-
'(and (eq foo 42) (exists often) (exists bar) (absent rarely))',
|
82
|
-
'(and (eq foo 42) (empty (eq foo 888)))',
|
83
|
-
'(and (eq foo 42) (empty (eq foo $id)))',
|
84
|
-
'(and (eq foo 42) (empty (eq foo $often)))',
|
85
|
-
'(and (eq foo 42) (empty (and (eq foo $often) (gt foo 43))))',
|
86
|
-
'(and (eq foo 42) (empty (and (eq foo 42) (eq bar 42) (eq id -1))))',
|
87
|
-
'(and (eq foo 42) (empty (exists another)))'
|
88
|
-
].each do |q|
|
89
|
-
Timeout.timeout(4) do
|
90
|
-
elapsed(Loog::NULL, good: q) do
|
91
|
-
refute_empty(fb.query(q).each.to_a)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,208 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase'
|
8
|
-
require_relative '../../../lib/factbase/cached/cached_factbase'
|
9
|
-
require_relative '../../../lib/factbase/indexed/indexed_factbase'
|
10
|
-
|
11
|
-
# Query test.
|
12
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
13
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
14
|
-
# License:: MIT
|
15
|
-
class TestIndexedQuery < Factbase::Test
|
16
|
-
def test_converts_to_string
|
17
|
-
fb = Factbase.new
|
18
|
-
fb.insert.foo = 42
|
19
|
-
refute_nil(Factbase::IndexedQuery.new(fb.query('(exists foo)'), {}, fb).to_s)
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_queries_and_updates_origin
|
23
|
-
fb = Factbase.new
|
24
|
-
fb.insert.foo = 42
|
25
|
-
Factbase::IndexedQuery.new(fb.query('(exists foo)'), {}, fb).each do |f|
|
26
|
-
f.bar = 33
|
27
|
-
end
|
28
|
-
refute_empty(fb.query('(exists bar)').each.to_a)
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_queries_many_times
|
32
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
33
|
-
total = 5
|
34
|
-
total.times { fb.insert }
|
35
|
-
total.times do
|
36
|
-
assert_equal(5, fb.query('(always)').each.to_a.size)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_finds_by_eq
|
41
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
42
|
-
fb.insert.foo = 42
|
43
|
-
fb.insert
|
44
|
-
3.times do
|
45
|
-
assert_equal(1, fb.query('(eq foo 42)').each.to_a.size)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_fills_up_the_index
|
50
|
-
idx = {}
|
51
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new, idx)
|
52
|
-
fb.query('(eq x 1)').each.to_a
|
53
|
-
assert_equal(1, idx.size)
|
54
|
-
fb.insert
|
55
|
-
assert_empty(idx)
|
56
|
-
end
|
57
|
-
|
58
|
-
def test_finds_by_eq_with_symbol
|
59
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
60
|
-
f = fb.insert
|
61
|
-
f.num = 256
|
62
|
-
f.num = 42
|
63
|
-
fb.insert.num = 42
|
64
|
-
fb.insert.num = 55
|
65
|
-
fb.insert
|
66
|
-
fb.insert
|
67
|
-
3.times do
|
68
|
-
assert_equal(1, fb.query('(eq 1 (agg (eq num $num) (count)))').each.to_a.size)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_finds_by_eq_with_array
|
73
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
74
|
-
f = fb.insert
|
75
|
-
f.foo = 33
|
76
|
-
f.foo = 42
|
77
|
-
f.foo = 1
|
78
|
-
3.times do
|
79
|
-
assert_equal(1, fb.query('(eq foo 42)').each.to_a.size)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def test_finds_by_eq_with_agg
|
84
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
85
|
-
fb.insert.foo = 42
|
86
|
-
fb.insert.foo = 33
|
87
|
-
fb.insert
|
88
|
-
3.times do
|
89
|
-
assert_equal(1, fb.query('(eq foo (agg (exists foo) (max foo)))').each.to_a.size)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def test_finds_max_value
|
94
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
95
|
-
fb.insert.foo = 42
|
96
|
-
fb.insert.foo = 33
|
97
|
-
fb.insert
|
98
|
-
3.times do
|
99
|
-
assert_equal(42, fb.query('(agg (exists foo) (max foo))').one)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def test_finds_by_eq_with_formula
|
104
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
105
|
-
fb.insert.foo = 42
|
106
|
-
fb.insert
|
107
|
-
3.times do
|
108
|
-
assert_equal(1, fb.query('(eq foo (plus 40 2))').each.to_a.size)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def test_finds_by_eq_in_txn
|
113
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
114
|
-
fb.insert.foo = 42
|
115
|
-
fb.insert
|
116
|
-
3.times do
|
117
|
-
fb.txn { |fbt| assert_equal(1, fbt.query('(eq foo 42)').each.to_a.size) }
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def test_finds_with_conjunction
|
122
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
123
|
-
fb.insert.foo = 42
|
124
|
-
fb.insert.bar = 33
|
125
|
-
3.times do
|
126
|
-
assert_equal(2, fb.query('(or (exists foo) (exists bar))').each.to_a.size)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def test_finds_with_disjunction
|
131
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
132
|
-
fb.insert.foo = 42
|
133
|
-
fb.insert.bar = 33
|
134
|
-
f = fb.insert
|
135
|
-
f.foo = 42
|
136
|
-
f.bar = 33
|
137
|
-
3.times do
|
138
|
-
assert_equal(1, fb.query('(and (exists foo) (exists bar))').each.to_a.size)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def test_finds_with_inversion
|
143
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
144
|
-
fb.insert.foo = 42
|
145
|
-
fb.insert.bar = 33
|
146
|
-
3.times do
|
147
|
-
assert_equal(1, fb.query('(not (exists foo))').each.to_a.size)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_finds_with_disjunction_in_txn
|
152
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
153
|
-
fb.insert.foo = 42
|
154
|
-
fb.insert.bar = 33
|
155
|
-
f = fb.insert
|
156
|
-
f.foo = 42
|
157
|
-
f.bar = 33
|
158
|
-
3.times do
|
159
|
-
fb.txn { |fbt| assert_equal(1, fbt.query('(and (exists foo) (exists bar))').each.to_a.size) }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def test_attaches_alias
|
164
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
165
|
-
total = 10
|
166
|
-
total.times do |i|
|
167
|
-
f = fb.insert
|
168
|
-
f.foo = rand(0..10)
|
169
|
-
f.bar = rand(0..10)
|
170
|
-
f.xyz = i
|
171
|
-
end
|
172
|
-
3.times do
|
173
|
-
assert_equal(total, fb.query('(as boom (agg (eq foo $bar) (min xyz)))').each.to_a.size)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def test_joins_simple_one
|
178
|
-
idx = {}
|
179
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new, idx)
|
180
|
-
fb.insert.who = 4
|
181
|
-
fb.insert.friend = 4
|
182
|
-
assert_equal(1, fb.query('(and (exists who) (join "f<=friend" (eq friend $who)))').each.to_a.size)
|
183
|
-
assert_equal(2, idx.size)
|
184
|
-
end
|
185
|
-
|
186
|
-
def test_joins_too
|
187
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
188
|
-
total = 10_000
|
189
|
-
total.times do |i|
|
190
|
-
f = fb.insert
|
191
|
-
f.who = i
|
192
|
-
end
|
193
|
-
total.times do |i|
|
194
|
-
f = fb.insert
|
195
|
-
f.friend = i
|
196
|
-
end
|
197
|
-
3.times do
|
198
|
-
assert_equal(total, fb.query('(and (exists who) (join "f<=friend" (eq friend $who)))').each.to_a.size)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def test_deletes_too
|
203
|
-
fb = Factbase::IndexedFactbase.new(Factbase.new)
|
204
|
-
fb.insert.foo = 1
|
205
|
-
fb.query('(eq foo 1)').delete!
|
206
|
-
assert_equal(0, fb.query('(always)').each.to_a.size)
|
207
|
-
end
|
208
|
-
end
|
@@ -1,140 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase'
|
8
|
-
require_relative '../../../lib/factbase/term'
|
9
|
-
require_relative '../../../lib/factbase/taped'
|
10
|
-
require_relative '../../../lib/factbase/indexed/indexed_term'
|
11
|
-
|
12
|
-
# Term test.
|
13
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
14
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
15
|
-
# License:: MIT
|
16
|
-
class TestIndexedTerm < Factbase::Test
|
17
|
-
def test_predicts_on_eq
|
18
|
-
term = Factbase::Term.new(:eq, [:foo, 42])
|
19
|
-
idx = {}
|
20
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
21
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7] }, { 'foo' => [22, 42] }, { 'foo' => [] }])
|
22
|
-
n = term.predict(maps, {})
|
23
|
-
assert_equal(2, n.size)
|
24
|
-
assert_kind_of(Factbase::Taped, n)
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_predicts_on_exists
|
28
|
-
term = Factbase::Term.new(:exists, [:foo])
|
29
|
-
idx = {}
|
30
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
31
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7] }, { 'foo' => [22, 42] }, { 'foo' => [] }])
|
32
|
-
n = term.predict(maps, {})
|
33
|
-
assert_equal(3, n.size)
|
34
|
-
assert_kind_of(Factbase::Taped, n)
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_predicts_on_one
|
38
|
-
term = Factbase::Term.new(:one, [:foo])
|
39
|
-
idx = {}
|
40
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
41
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7] }, { 'foo' => [22, 42] }, { 'foo' => [] }])
|
42
|
-
n = term.predict(maps, {})
|
43
|
-
assert_equal(1, n.size)
|
44
|
-
assert_kind_of(Factbase::Taped, n)
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_predicts_on_not
|
48
|
-
term = Factbase::Term.new(:not, [Factbase::Term.new(:eq, [:foo, 42])])
|
49
|
-
idx = {}
|
50
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
51
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7], 'foo' => [22, 42] }, { 'foo' => [22] }])
|
52
|
-
n = term.predict(maps, {})
|
53
|
-
assert_equal(1, n.size)
|
54
|
-
assert_kind_of(Factbase::Taped, n)
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_predicts_on_and
|
58
|
-
term = Factbase::Term.new(
|
59
|
-
:and,
|
60
|
-
[
|
61
|
-
Factbase::Term.new(:eq, [:foo, 42]),
|
62
|
-
Factbase::Term.new(:eq, %i[bar $jeff])
|
63
|
-
]
|
64
|
-
)
|
65
|
-
idx = {}
|
66
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
67
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7], 'foo' => [22, 42] }, { 'foo' => [22, 42] }])
|
68
|
-
n = term.predict(maps, Factbase::Tee.new({}, { 'jeff' => [7] }))
|
69
|
-
assert_equal(1, n.size)
|
70
|
-
assert_kind_of(Factbase::Taped, n)
|
71
|
-
end
|
72
|
-
|
73
|
-
def test_predicts_on_single_and
|
74
|
-
term = Factbase::Term.new(:and, [Factbase::Term.new(:eq, [:foo, 42])])
|
75
|
-
idx = {}
|
76
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
77
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7], 'foo' => [4] }])
|
78
|
-
assert_equal(1, term.predict(maps, {}).size)
|
79
|
-
end
|
80
|
-
|
81
|
-
def test_predicts_on_or
|
82
|
-
term = Factbase::Term.new(
|
83
|
-
:or,
|
84
|
-
[
|
85
|
-
Factbase::Term.new(:exists, [:bar]),
|
86
|
-
Factbase::Term.new(:eq, [:foo, 42]),
|
87
|
-
Factbase::Term.new(:eq, [:bar, 7])
|
88
|
-
]
|
89
|
-
)
|
90
|
-
idx = {}
|
91
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
92
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'bar' => [7], 'foo' => [22, 42] }, { 'foo' => [22, 42] }])
|
93
|
-
n = term.predict(maps, {})
|
94
|
-
assert_equal(3, n.size)
|
95
|
-
assert_kind_of(Factbase::Taped, n)
|
96
|
-
end
|
97
|
-
|
98
|
-
def test_predicts_on_others
|
99
|
-
term = Factbase::Term.new(:boom, [])
|
100
|
-
idx = {}
|
101
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
102
|
-
maps = Factbase::Taped.new([{ 'foo' => [42] }, { 'alpha' => [] }, {}])
|
103
|
-
n = term.predict(maps, {})
|
104
|
-
assert_nil(n)
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_predicts_on_gt
|
108
|
-
term = Factbase::Term.new(:gt, [:foo, 42])
|
109
|
-
idx = {}
|
110
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
111
|
-
maps = Factbase::Taped.new([
|
112
|
-
{ 'foo' => [10] },
|
113
|
-
{ 'foo' => [43] },
|
114
|
-
{ 'foo' => [42] },
|
115
|
-
{ 'foo' => [100, 5] },
|
116
|
-
{ 'bar' => [50] },
|
117
|
-
{ 'foo' => [41, 42, 43] }
|
118
|
-
])
|
119
|
-
n = term.predict(maps, {})
|
120
|
-
assert_equal(3, n.size)
|
121
|
-
assert_kind_of(Factbase::Taped, n)
|
122
|
-
end
|
123
|
-
|
124
|
-
def test_predicts_on_lt
|
125
|
-
term = Factbase::Term.new(:lt, [:foo, 42])
|
126
|
-
idx = {}
|
127
|
-
term.redress!(Factbase::IndexedTerm, idx:)
|
128
|
-
maps = Factbase::Taped.new([
|
129
|
-
{ 'foo' => [10] },
|
130
|
-
{ 'foo' => [43] },
|
131
|
-
{ 'foo' => [42] },
|
132
|
-
{ 'foo' => [100, 5] },
|
133
|
-
{ 'bar' => [50] },
|
134
|
-
{ 'foo' => [41, 42, 43] }
|
135
|
-
])
|
136
|
-
n = term.predict(maps, {})
|
137
|
-
assert_equal(3, n.size)
|
138
|
-
assert_kind_of(Factbase::Taped, n)
|
139
|
-
end
|
140
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase'
|
8
|
-
require_relative '../../../lib/factbase/sync/sync_factbase'
|
9
|
-
|
10
|
-
# Sync factbase test.
|
11
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
|
-
# License:: MIT
|
14
|
-
class TestSyncFactbase < Factbase::Test
|
15
|
-
def test_queries_and_inserts
|
16
|
-
fb = Factbase::SyncFactbase.new(Factbase.new)
|
17
|
-
fb.insert.foo = 42
|
18
|
-
fb.query('(exists foo)').each do
|
19
|
-
fb.insert
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase'
|
8
|
-
require_relative '../../../lib/factbase/sync/sync_factbase'
|
9
|
-
|
10
|
-
# Query test.
|
11
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
|
-
# License:: MIT
|
14
|
-
class TestSyncQuery < Factbase::Test
|
15
|
-
def test_queries_many_times
|
16
|
-
fb = Factbase::SyncFactbase.new(Factbase.new)
|
17
|
-
total = 5
|
18
|
-
total.times { fb.insert }
|
19
|
-
total.times do
|
20
|
-
assert_equal(5, fb.query('(always)').each.to_a.size)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_deletes_too
|
25
|
-
fb = Factbase::SyncFactbase.new(Factbase.new)
|
26
|
-
fb.insert.foo = 1
|
27
|
-
fb.query('(eq foo 1)').delete!
|
28
|
-
assert_equal(0, fb.query('(always)').each.to_a.size)
|
29
|
-
end
|
30
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
4
|
-
# SPDX-License-Identifier: MIT
|
5
|
-
|
6
|
-
require_relative '../../test__helper'
|
7
|
-
require_relative '../../../lib/factbase/term'
|
8
|
-
require_relative '../../../lib/factbase/syntax'
|
9
|
-
|
10
|
-
# Aggregates test.
|
11
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
12
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
13
|
-
# License:: MIT
|
14
|
-
class TestAggregates < Factbase::Test
|
15
|
-
def test_aggregation
|
16
|
-
maps = [
|
17
|
-
{ 'x' => [1], 'y' => [0], 'z' => [4] },
|
18
|
-
{ 'x' => [2], 'y' => [42], 'z' => [3] },
|
19
|
-
{ 'x' => [3], 'y' => [42], 'z' => [5] },
|
20
|
-
{ 'x' => [4], 'y' => [42], 'z' => [2] },
|
21
|
-
{ 'x' => [5], 'y' => [42], 'z' => [1] },
|
22
|
-
{ 'x' => [8], 'y' => [0], 'z' => [6] }
|
23
|
-
]
|
24
|
-
{
|
25
|
-
'(eq x (agg (eq y 42) (min x)))' => '(eq x 2)',
|
26
|
-
'(eq z (agg (eq y 0) (max z)))' => '(eq x 8)',
|
27
|
-
'(eq x (agg (and (eq y 42) (gt z 1)) (max x)))' => '(eq x 4)',
|
28
|
-
'(and (eq x (agg (eq y 42) (min x))) (eq z 3))' => '(eq x 2)',
|
29
|
-
'(eq x (agg (eq y 0) (nth 0 x)))' => '(eq x 1)',
|
30
|
-
'(eq x (agg (eq y 0) (first x)))' => '(eq x 1)',
|
31
|
-
'(agg (eq foo 42) (always))' => '(eq x 1)'
|
32
|
-
}.each do |q, r|
|
33
|
-
t = Factbase::Syntax.new(q).to_term
|
34
|
-
f = maps.find { |m| t.evaluate(fact(m), maps, Factbase.new) }
|
35
|
-
refute_nil(f, "nothing found by: #{q}")
|
36
|
-
assert(Factbase::Syntax.new(r).to_term.evaluate(fact(f), [], Factbase.new))
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_empty
|
41
|
-
maps = [
|
42
|
-
{ 'x' => [1], 'y' => [0], 'z' => [4] },
|
43
|
-
{ 'x' => [8], 'y' => [0] }
|
44
|
-
]
|
45
|
-
{
|
46
|
-
'(empty (eq y 42))' => true,
|
47
|
-
'(empty (eq x 1))' => false
|
48
|
-
}.each do |q, r|
|
49
|
-
t = Factbase::Syntax.new(q).to_term
|
50
|
-
assert_equal(r, t.evaluate(Factbase::Fact.new({}), maps, Factbase.new), q)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_empty_with_params
|
55
|
-
maps = [
|
56
|
-
{ 'a' => [3], 'b' => [44] },
|
57
|
-
{ 'a' => [4], 'b' => [55] }
|
58
|
-
]
|
59
|
-
t = Factbase::Syntax.new('(empty (eq b $x))').to_term
|
60
|
-
assert(t.evaluate(Factbase::Fact.new({ 'x' => [42] }), maps, Factbase.new))
|
61
|
-
refute(t.evaluate(Factbase::Fact.new({ 'x' => [44] }), maps, Factbase.new))
|
62
|
-
end
|
63
|
-
end
|