factbase 0.17.0 → 0.17.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 +2 -1
- data/Gemfile.lock +7 -5
- data/Rakefile +16 -0
- data/lib/factbase/indexed/indexed_factbase.rb +6 -3
- data/lib/factbase/term.rb +21 -11
- data/lib/factbase/terms/agg.rb +35 -0
- data/lib/factbase/terms/and.rb +7 -0
- data/lib/factbase/terms/best.rb +25 -0
- data/lib/factbase/terms/empty.rb +36 -0
- data/lib/factbase/terms/max.rb +32 -0
- data/lib/factbase/terms/min.rb +32 -0
- data/lib/factbase/terms/nth.rb +33 -0
- data/lib/factbase/terms/or.rb +7 -0
- data/lib/factbase/terms/simplified.rb +31 -0
- data/lib/factbase/terms/sum.rb +38 -0
- data/lib/factbase/version.rb +1 -1
- metadata +9 -3
- data/lib/factbase/terms/aggregates.rb +0 -86
- data/lib/factbase/terms/logical.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91ef3ed499ea15abb77bc39cfed30eb9af704921e86c3c03c7bdd9b13cb44270
|
|
4
|
+
data.tar.gz: 3c2e80d00620e0de14ec4be35ff08b28393793c69742dda9bda55c4a5ae01c8f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c6e18a32a5ae773e78672e449cce031a2c7641b1291b9f637ccb28afb6afddc87091955ce06e75c0e35326f4db37b04fac5b827b1cdc203a8b0262c105ca2c3
|
|
7
|
+
data.tar.gz: 923207d8494313e1af4fae0ecf3068372a5dde28924840edbe1b6d625127afb7e4eb2cf44c9f86a28d9d7fc650336abce9c9b99cd492b3ff29bcbb4cf214c83e
|
data/Gemfile
CHANGED
|
@@ -12,12 +12,13 @@ gem 'minitest-reporters', '~>1.7', require: false
|
|
|
12
12
|
gem 'os', '~>1.1', require: false
|
|
13
13
|
gem 'qbash', '~>0.4', require: false
|
|
14
14
|
gem 'rake', '~>13.2', require: false
|
|
15
|
-
gem 'rdoc', '6.
|
|
15
|
+
gem 'rdoc', '6.16.1', require: false # GPL
|
|
16
16
|
gem 'rubocop', '~>1.74', require: false
|
|
17
17
|
gem 'rubocop-minitest', '~>0.38', require: false
|
|
18
18
|
gem 'rubocop-performance', '~>1.25', require: false
|
|
19
19
|
gem 'rubocop-rake', '~>0.7', require: false
|
|
20
20
|
gem 'simplecov', '~>0.22', require: false
|
|
21
21
|
gem 'simplecov-cobertura', '~>3.0', require: false
|
|
22
|
+
gem 'stackprof', '~>0.2', require: false, platforms: [:ruby]
|
|
22
23
|
gem 'threads', '~>0.4', require: false
|
|
23
24
|
gem 'yard', '~>0.9', require: false
|
data/Gemfile.lock
CHANGED
|
@@ -30,7 +30,7 @@ GEM
|
|
|
30
30
|
tago (~> 0.1)
|
|
31
31
|
ellipsized (0.3.0)
|
|
32
32
|
erb (6.0.0)
|
|
33
|
-
json (2.
|
|
33
|
+
json (2.17.1)
|
|
34
34
|
language_server-protocol (3.17.0.5)
|
|
35
35
|
lint_roller (1.1.0)
|
|
36
36
|
logger (1.7.0)
|
|
@@ -68,7 +68,7 @@ GEM
|
|
|
68
68
|
racc (1.8.1)
|
|
69
69
|
rainbow (3.1.1)
|
|
70
70
|
rake (13.3.1)
|
|
71
|
-
rdoc (6.
|
|
71
|
+
rdoc (6.16.1)
|
|
72
72
|
erb
|
|
73
73
|
psych (>= 4.0.0)
|
|
74
74
|
tsort
|
|
@@ -109,7 +109,8 @@ GEM
|
|
|
109
109
|
simplecov (~> 0.19)
|
|
110
110
|
simplecov-html (0.13.2)
|
|
111
111
|
simplecov_json_formatter (0.1.4)
|
|
112
|
-
|
|
112
|
+
stackprof (0.2.27)
|
|
113
|
+
stringio (3.1.9)
|
|
113
114
|
tago (0.4.0)
|
|
114
115
|
threads (0.5.0)
|
|
115
116
|
backtrace (~> 0)
|
|
@@ -119,7 +120,7 @@ GEM
|
|
|
119
120
|
unicode-emoji (~> 4.1)
|
|
120
121
|
unicode-emoji (4.1.0)
|
|
121
122
|
yaml (0.4.0)
|
|
122
|
-
yard (0.9.
|
|
123
|
+
yard (0.9.38)
|
|
123
124
|
|
|
124
125
|
PLATFORMS
|
|
125
126
|
arm64-darwin-22
|
|
@@ -139,13 +140,14 @@ DEPENDENCIES
|
|
|
139
140
|
os (~> 1.1)
|
|
140
141
|
qbash (~> 0.4)
|
|
141
142
|
rake (~> 13.2)
|
|
142
|
-
rdoc (= 6.
|
|
143
|
+
rdoc (= 6.16.1)
|
|
143
144
|
rubocop (~> 1.74)
|
|
144
145
|
rubocop-minitest (~> 0.38)
|
|
145
146
|
rubocop-performance (~> 1.25)
|
|
146
147
|
rubocop-rake (~> 0.7)
|
|
147
148
|
simplecov (~> 0.22)
|
|
148
149
|
simplecov-cobertura (~> 3.0)
|
|
150
|
+
stackprof (~> 0.2)
|
|
149
151
|
threads (~> 0.4)
|
|
150
152
|
yard (~> 0.9)
|
|
151
153
|
|
data/Rakefile
CHANGED
|
@@ -78,3 +78,19 @@ task :benchmark, [:name] do |_t, args|
|
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
|
+
|
|
82
|
+
# Run profiling on a benchmark and generate a flamegraph.
|
|
83
|
+
# To run this task, you need to have stackprof installed.
|
|
84
|
+
# https://github.com/tmm1/stackprof
|
|
85
|
+
# To run profiling for a specific benchmark you can run:
|
|
86
|
+
# bundle exec rake flamegraph\[bench_slow_query\]
|
|
87
|
+
desc 'Profile a benchmark (e.g., flamegraph[bench_slow_query])'
|
|
88
|
+
task :flamegraph, [:name] do |_t, args|
|
|
89
|
+
require 'stackprof'
|
|
90
|
+
bname = args[:name] || 'all'
|
|
91
|
+
puts "Starting profiling for '#{bname}'..."
|
|
92
|
+
StackProf.run(mode: :cpu, out: 'stackprof-cpu-myapp.dump', raw: true) do
|
|
93
|
+
Rake::Task['benchmark'].invoke(bname)
|
|
94
|
+
end
|
|
95
|
+
`stackprof --d3-flamegraph stackprof-cpu-myapp.dump > flamegraph.html`
|
|
96
|
+
end
|
|
@@ -57,9 +57,12 @@ class Factbase::IndexedFactbase
|
|
|
57
57
|
# Run an ACID transaction.
|
|
58
58
|
# @return [Factbase::Churn] How many facts have been changed (zero if rolled back)
|
|
59
59
|
def txn
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
result =
|
|
61
|
+
@origin.txn do |fbt|
|
|
62
|
+
yield Factbase::IndexedFactbase.new(fbt, @idx)
|
|
63
|
+
end
|
|
64
|
+
@idx.clear
|
|
65
|
+
result
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
# Export it into a chain of bytes, including both data and index.
|
data/lib/factbase/term.rb
CHANGED
|
@@ -52,6 +52,12 @@ require_relative 'terms/when'
|
|
|
52
52
|
require_relative 'terms/either'
|
|
53
53
|
require_relative 'terms/count'
|
|
54
54
|
require_relative 'terms/first'
|
|
55
|
+
require_relative 'terms/nth'
|
|
56
|
+
require_relative 'terms/sum'
|
|
57
|
+
require_relative 'terms/agg'
|
|
58
|
+
require_relative 'terms/empty'
|
|
59
|
+
require_relative 'terms/min'
|
|
60
|
+
require_relative 'terms/max'
|
|
55
61
|
|
|
56
62
|
# Term.
|
|
57
63
|
#
|
|
@@ -89,12 +95,6 @@ class Factbase::Term
|
|
|
89
95
|
# @return [Array] The operands
|
|
90
96
|
attr_reader :operands
|
|
91
97
|
|
|
92
|
-
require_relative 'terms/logical'
|
|
93
|
-
include Factbase::Logical
|
|
94
|
-
|
|
95
|
-
require_relative 'terms/aggregates'
|
|
96
|
-
include Factbase::Aggregates
|
|
97
|
-
|
|
98
98
|
require_relative 'terms/shared'
|
|
99
99
|
include Factbase::TermShared
|
|
100
100
|
|
|
@@ -149,7 +149,13 @@ class Factbase::Term
|
|
|
149
149
|
when: Factbase::When.new(operands),
|
|
150
150
|
either: Factbase::Either.new(operands),
|
|
151
151
|
count: Factbase::Count.new(operands),
|
|
152
|
-
first: Factbase::First.new(operands)
|
|
152
|
+
first: Factbase::First.new(operands),
|
|
153
|
+
nth: Factbase::Nth.new(operands),
|
|
154
|
+
sum: Factbase::Sum.new(operands),
|
|
155
|
+
agg: Factbase::Agg.new(operands),
|
|
156
|
+
empty: Factbase::Empty.new(operands),
|
|
157
|
+
min: Factbase::Min.new(operands),
|
|
158
|
+
max: Factbase::Max.new(operands)
|
|
153
159
|
}
|
|
154
160
|
end
|
|
155
161
|
|
|
@@ -211,11 +217,15 @@ class Factbase::Term
|
|
|
211
217
|
# Simplify it if possible.
|
|
212
218
|
# @return [Factbase::Term] New term or itself
|
|
213
219
|
def simplify
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
send(m)
|
|
220
|
+
if @terms.key?(@op) && @terms[@op].respond_to?(:simplify)
|
|
221
|
+
@terms[@op].simplify
|
|
217
222
|
else
|
|
218
|
-
|
|
223
|
+
m = "#{@op}_simplify"
|
|
224
|
+
if respond_to?(m, true)
|
|
225
|
+
send(m)
|
|
226
|
+
else
|
|
227
|
+
self
|
|
228
|
+
end
|
|
219
229
|
end
|
|
220
230
|
end
|
|
221
231
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative 'base'
|
|
7
|
+
# The term 'agg' that aggregates.
|
|
8
|
+
class Factbase::Agg < Factbase::TermBase
|
|
9
|
+
# Constructor.
|
|
10
|
+
# @param [Array] operands Operands
|
|
11
|
+
def initialize(operands = [])
|
|
12
|
+
super()
|
|
13
|
+
@operands = operands
|
|
14
|
+
@op = :agg
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Evaluate term on a fact.
|
|
18
|
+
# @param [Factbase::Fact] fact The fact
|
|
19
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
20
|
+
# @param [Factbase] fb Factbase to use for sub-queries
|
|
21
|
+
# @return [Object] The result of evaluation
|
|
22
|
+
def evaluate(fact, maps, fb)
|
|
23
|
+
assert_args(2)
|
|
24
|
+
selector = @operands[0]
|
|
25
|
+
unless selector.is_a?(Factbase::Term) || selector.is_a?(Factbase::TermBase)
|
|
26
|
+
raise "A term is expected, but '#{selector}' provided"
|
|
27
|
+
end
|
|
28
|
+
term = @operands[1]
|
|
29
|
+
unless term.is_a?(Factbase::Term) || selector.is_a?(Factbase::TermBase)
|
|
30
|
+
raise "A term is expected, but '#{term}' provided"
|
|
31
|
+
end
|
|
32
|
+
subset = fb.query(selector, maps).each(fb, fact).to_a
|
|
33
|
+
term.evaluate(nil, subset, fb)
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/factbase/terms/and.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
require_relative 'base'
|
|
7
7
|
require_relative 'boolean'
|
|
8
|
+
require_relative 'simplified'
|
|
8
9
|
# The 'and' term that represents a logical AND operation between multiple operands.
|
|
9
10
|
class Factbase::And < Factbase::TermBase
|
|
10
11
|
# Constructor.
|
|
@@ -25,4 +26,10 @@ class Factbase::And < Factbase::TermBase
|
|
|
25
26
|
end
|
|
26
27
|
true
|
|
27
28
|
end
|
|
29
|
+
|
|
30
|
+
def simplify
|
|
31
|
+
unique = Factbase::Simplified.new(@operands).unique
|
|
32
|
+
return unique[0] if unique.size == 1
|
|
33
|
+
Factbase::Term.new(@op, unique)
|
|
34
|
+
end
|
|
28
35
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
# The 'best' term evaluates the best value for a given key.
|
|
7
|
+
class Factbase::Best
|
|
8
|
+
def initialize(&block)
|
|
9
|
+
@criteria = block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def evaluate(key, maps)
|
|
13
|
+
raise "A symbol is expected, but #{key} provided" unless key.is_a?(Symbol)
|
|
14
|
+
best = nil
|
|
15
|
+
maps.each do |m|
|
|
16
|
+
vv = m[key.to_s]
|
|
17
|
+
next if vv.nil?
|
|
18
|
+
vv = [vv] unless vv.respond_to?(:to_a)
|
|
19
|
+
vv.each do |v|
|
|
20
|
+
best = v if best.nil? || @criteria.call(v, best)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
best
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative 'base'
|
|
7
|
+
|
|
8
|
+
# The 'empty' term checks for emptiness in the results of a query evaluation.
|
|
9
|
+
class Factbase::Empty < Factbase::TermBase
|
|
10
|
+
# Constructor.
|
|
11
|
+
# @param [Array] operands Operands
|
|
12
|
+
def initialize(operands)
|
|
13
|
+
super()
|
|
14
|
+
@operands = operands
|
|
15
|
+
@op = :empty
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Evaluate term on a fact.
|
|
19
|
+
# @param [Factbase::Fact] fact The fact
|
|
20
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
21
|
+
# @param [Factbase] fb Factbase to use for sub-queries
|
|
22
|
+
# @return [Boolean] The result of the emptiness check
|
|
23
|
+
def evaluate(fact, maps, fb)
|
|
24
|
+
assert_args(1)
|
|
25
|
+
term = @operands[0]
|
|
26
|
+
unless term.is_a?(Factbase::Term) || term.is_a?(Factbase::TermBase)
|
|
27
|
+
raise "A term is expected, but '#{term}' provided"
|
|
28
|
+
end
|
|
29
|
+
# rubocop:disable Lint/UnreachableLoop
|
|
30
|
+
fb.query(term, maps).each(fb, fact) do
|
|
31
|
+
return false
|
|
32
|
+
end
|
|
33
|
+
# rubocop:enable Lint/UnreachableLoop
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative '../../factbase'
|
|
7
|
+
require_relative 'best'
|
|
8
|
+
require_relative 'base'
|
|
9
|
+
|
|
10
|
+
# The 'max' term.
|
|
11
|
+
# This term calculates the max value among the evaluated operands.
|
|
12
|
+
class Factbase::Max < Factbase::TermBase
|
|
13
|
+
MAX = Factbase::Best.new { |v, b| v > b }
|
|
14
|
+
|
|
15
|
+
# Constructor.
|
|
16
|
+
# @param [Array] operands Operands
|
|
17
|
+
def initialize(operands)
|
|
18
|
+
super()
|
|
19
|
+
@operands = operands
|
|
20
|
+
@op = :max
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Evaluate term on a fact.
|
|
24
|
+
# @param [Factbase::Fact] _fact The fact
|
|
25
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
26
|
+
# @param [Factbase] _fb Factbase to use for sub-queries
|
|
27
|
+
# @return [Object] The max value among the evaluated operands
|
|
28
|
+
def evaluate(_fact, maps, _fb)
|
|
29
|
+
assert_args(1)
|
|
30
|
+
MAX.evaluate(@operands[0], maps)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative '../../factbase'
|
|
7
|
+
require_relative 'best'
|
|
8
|
+
require_relative 'base'
|
|
9
|
+
|
|
10
|
+
# The 'min' term.
|
|
11
|
+
# This term calculates the minimum value among the evaluated operands.
|
|
12
|
+
class Factbase::Min < Factbase::TermBase
|
|
13
|
+
MIN = Factbase::Best.new { |v, b| v < b }
|
|
14
|
+
|
|
15
|
+
# Constructor.
|
|
16
|
+
# @param [Array] operands Operands
|
|
17
|
+
def initialize(operands)
|
|
18
|
+
super()
|
|
19
|
+
@operands = operands
|
|
20
|
+
@op = :min
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Evaluate term on a fact.
|
|
24
|
+
# @param [Factbase::Fact] _fact The fact
|
|
25
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
26
|
+
# @param [Factbase] _fb Factbase to use for sub-queries
|
|
27
|
+
# @return [Object] The minimum value among the evaluated operands
|
|
28
|
+
def evaluate(_fact, maps, _fb)
|
|
29
|
+
assert_args(1)
|
|
30
|
+
MIN.evaluate(@operands[0], maps)
|
|
31
|
+
end
|
|
32
|
+
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 'base'
|
|
7
|
+
# Represents an 'nth' term in the Factbase.
|
|
8
|
+
# Retrieves the value of a specified key from the nth map.
|
|
9
|
+
class Factbase::Nth < Factbase::TermBase
|
|
10
|
+
# Constructor.
|
|
11
|
+
# @param [Array] operands Operands
|
|
12
|
+
def initialize(operands)
|
|
13
|
+
super()
|
|
14
|
+
@operands = operands
|
|
15
|
+
@op = :nth
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Evaluate term on a fact.
|
|
19
|
+
# @param [Factbase::Fact] _fact The fact
|
|
20
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
21
|
+
# @param [Factbase] _fb Factbase to use for sub-queries
|
|
22
|
+
# @return [Object] The value of the specified key from the nth map
|
|
23
|
+
def evaluate(_fact, maps, _fb)
|
|
24
|
+
assert_args(2)
|
|
25
|
+
pos = @operands[0]
|
|
26
|
+
raise "An integer is expected, but #{pos} provided" unless pos.is_a?(Integer)
|
|
27
|
+
k = @operands[1]
|
|
28
|
+
raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
|
|
29
|
+
m = maps[pos]
|
|
30
|
+
return nil if m.nil?
|
|
31
|
+
m[k.to_s]
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/factbase/terms/or.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
require_relative 'base'
|
|
7
7
|
require_relative 'boolean'
|
|
8
|
+
require_relative 'simplified'
|
|
8
9
|
# The 'or' term that represents a logical OR operation between multiple operands.
|
|
9
10
|
class Factbase::Or < Factbase::TermBase
|
|
10
11
|
# Constructor.
|
|
@@ -25,4 +26,10 @@ class Factbase::Or < Factbase::TermBase
|
|
|
25
26
|
end
|
|
26
27
|
false
|
|
27
28
|
end
|
|
29
|
+
|
|
30
|
+
def simplify
|
|
31
|
+
unique = Factbase::Simplified.new(@operands).unique
|
|
32
|
+
return unique[0] if unique.size == 1
|
|
33
|
+
Factbase::Term.new(@op, unique)
|
|
34
|
+
end
|
|
28
35
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative '../../factbase'
|
|
7
|
+
|
|
8
|
+
# Simplified operands.
|
|
9
|
+
#
|
|
10
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
11
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
12
|
+
# License:: MIT
|
|
13
|
+
class Factbase::Simplified
|
|
14
|
+
def initialize(operands)
|
|
15
|
+
@operands = operands
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Removes duplicate operands
|
|
19
|
+
def unique
|
|
20
|
+
strs = []
|
|
21
|
+
ops = []
|
|
22
|
+
@operands.each do |o|
|
|
23
|
+
o = o.simplify
|
|
24
|
+
s = o.to_s
|
|
25
|
+
next if strs.include?(s)
|
|
26
|
+
strs << s
|
|
27
|
+
ops << o
|
|
28
|
+
end
|
|
29
|
+
ops
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative 'base'
|
|
7
|
+
|
|
8
|
+
# This class represents a specialized 'sum' term.
|
|
9
|
+
# This term calculates the sum of values for a specified key.
|
|
10
|
+
class Factbase::Sum < Factbase::TermBase
|
|
11
|
+
# Constructor.
|
|
12
|
+
# @param [Array] operands Operands
|
|
13
|
+
def initialize(operands)
|
|
14
|
+
super()
|
|
15
|
+
@operands = operands
|
|
16
|
+
@op = :sum
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Evaluate term on a fact.
|
|
20
|
+
# @param [Factbase::Fact] _fact The fact
|
|
21
|
+
# @param [Array<Factbase::Fact>] maps All maps available
|
|
22
|
+
# @param [Factbase] _fb Factbase to use for sub-queries
|
|
23
|
+
# @return [Integer] The sum of values for the specified key across all maps
|
|
24
|
+
def evaluate(_fact, maps, _fb)
|
|
25
|
+
k = @operands[0]
|
|
26
|
+
raise "A symbol is expected, but '#{k}' provided" unless k.is_a?(Symbol)
|
|
27
|
+
sum = 0
|
|
28
|
+
maps.each do |m|
|
|
29
|
+
vv = m[k.to_s]
|
|
30
|
+
next if vv.nil?
|
|
31
|
+
vv = [vv] unless vv.respond_to?(:to_a)
|
|
32
|
+
vv.each do |v|
|
|
33
|
+
sum += v
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
sum
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/factbase/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: factbase
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.17.
|
|
4
|
+
version: 0.17.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yegor Bugayenko
|
|
@@ -208,13 +208,14 @@ files:
|
|
|
208
208
|
- lib/factbase/tee.rb
|
|
209
209
|
- lib/factbase/term.rb
|
|
210
210
|
- lib/factbase/terms/absent.rb
|
|
211
|
-
- lib/factbase/terms/
|
|
211
|
+
- lib/factbase/terms/agg.rb
|
|
212
212
|
- lib/factbase/terms/always.rb
|
|
213
213
|
- lib/factbase/terms/and.rb
|
|
214
214
|
- lib/factbase/terms/arithmetic.rb
|
|
215
215
|
- lib/factbase/terms/as.rb
|
|
216
216
|
- lib/factbase/terms/assert.rb
|
|
217
217
|
- lib/factbase/terms/base.rb
|
|
218
|
+
- lib/factbase/terms/best.rb
|
|
218
219
|
- lib/factbase/terms/boolean.rb
|
|
219
220
|
- lib/factbase/terms/compare.rb
|
|
220
221
|
- lib/factbase/terms/concat.rb
|
|
@@ -222,6 +223,7 @@ files:
|
|
|
222
223
|
- lib/factbase/terms/defn.rb
|
|
223
224
|
- lib/factbase/terms/div.rb
|
|
224
225
|
- lib/factbase/terms/either.rb
|
|
226
|
+
- lib/factbase/terms/empty.rb
|
|
225
227
|
- lib/factbase/terms/env.rb
|
|
226
228
|
- lib/factbase/terms/eq.rb
|
|
227
229
|
- lib/factbase/terms/exists.rb
|
|
@@ -231,23 +233,27 @@ files:
|
|
|
231
233
|
- lib/factbase/terms/head.rb
|
|
232
234
|
- lib/factbase/terms/inverted.rb
|
|
233
235
|
- lib/factbase/terms/join.rb
|
|
234
|
-
- lib/factbase/terms/logical.rb
|
|
235
236
|
- lib/factbase/terms/lt.rb
|
|
236
237
|
- lib/factbase/terms/lte.rb
|
|
237
238
|
- lib/factbase/terms/many.rb
|
|
238
239
|
- lib/factbase/terms/matches.rb
|
|
240
|
+
- lib/factbase/terms/max.rb
|
|
241
|
+
- lib/factbase/terms/min.rb
|
|
239
242
|
- lib/factbase/terms/minus.rb
|
|
240
243
|
- lib/factbase/terms/never.rb
|
|
241
244
|
- lib/factbase/terms/nil.rb
|
|
242
245
|
- lib/factbase/terms/not.rb
|
|
246
|
+
- lib/factbase/terms/nth.rb
|
|
243
247
|
- lib/factbase/terms/one.rb
|
|
244
248
|
- lib/factbase/terms/or.rb
|
|
245
249
|
- lib/factbase/terms/plus.rb
|
|
246
250
|
- lib/factbase/terms/prev.rb
|
|
247
251
|
- lib/factbase/terms/shared.rb
|
|
252
|
+
- lib/factbase/terms/simplified.rb
|
|
248
253
|
- lib/factbase/terms/size.rb
|
|
249
254
|
- lib/factbase/terms/sorted.rb
|
|
250
255
|
- lib/factbase/terms/sprintf.rb
|
|
256
|
+
- lib/factbase/terms/sum.rb
|
|
251
257
|
- lib/factbase/terms/times.rb
|
|
252
258
|
- lib/factbase/terms/to_float.rb
|
|
253
259
|
- lib/factbase/terms/to_integer.rb
|
|
@@ -1,86 +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 '../../factbase'
|
|
7
|
-
|
|
8
|
-
# Aggregating terms.
|
|
9
|
-
#
|
|
10
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
11
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
12
|
-
# License:: MIT
|
|
13
|
-
module Factbase::Aggregates
|
|
14
|
-
def min(_fact, maps, _fb)
|
|
15
|
-
assert_args(1)
|
|
16
|
-
_best(maps) { |v, b| v < b }
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def max(_fact, maps, _fb)
|
|
20
|
-
assert_args(1)
|
|
21
|
-
_best(maps) { |v, b| v > b }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def nth(_fact, maps, _fb)
|
|
25
|
-
assert_args(2)
|
|
26
|
-
pos = @operands[0]
|
|
27
|
-
raise "An integer is expected, but #{pos} provided" unless pos.is_a?(Integer)
|
|
28
|
-
k = @operands[1]
|
|
29
|
-
raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
|
|
30
|
-
m = maps[pos]
|
|
31
|
-
return nil if m.nil?
|
|
32
|
-
m[k.to_s]
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def sum(_fact, maps, _fb)
|
|
36
|
-
k = @operands[0]
|
|
37
|
-
raise "A symbol is expected, but '#{k}' provided" unless k.is_a?(Symbol)
|
|
38
|
-
sum = 0
|
|
39
|
-
maps.each do |m|
|
|
40
|
-
vv = m[k.to_s]
|
|
41
|
-
next if vv.nil?
|
|
42
|
-
vv = [vv] unless vv.respond_to?(:to_a)
|
|
43
|
-
vv.each do |v|
|
|
44
|
-
sum += v
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
sum
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def agg(fact, maps, fb)
|
|
51
|
-
assert_args(2)
|
|
52
|
-
selector = @operands[0]
|
|
53
|
-
raise "A term is expected, but '#{selector}' provided" unless selector.is_a?(Factbase::Term)
|
|
54
|
-
term = @operands[1]
|
|
55
|
-
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
|
56
|
-
subset = fb.query(selector, maps).each(fb, fact).to_a
|
|
57
|
-
term.evaluate(nil, subset, fb)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def empty(fact, maps, fb)
|
|
61
|
-
assert_args(1)
|
|
62
|
-
term = @operands[0]
|
|
63
|
-
raise "A term is expected, but '#{term}' provided" unless term.is_a?(Factbase::Term)
|
|
64
|
-
# rubocop:disable Lint/UnreachableLoop
|
|
65
|
-
fb.query(term, maps).each(fb, fact) do
|
|
66
|
-
return false
|
|
67
|
-
end
|
|
68
|
-
# rubocop:enable Lint/UnreachableLoop
|
|
69
|
-
true
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def _best(maps)
|
|
73
|
-
k = @operands[0]
|
|
74
|
-
raise "A symbol is expected, but #{k} provided" unless k.is_a?(Symbol)
|
|
75
|
-
best = nil
|
|
76
|
-
maps.each do |m|
|
|
77
|
-
vv = m[k.to_s]
|
|
78
|
-
next if vv.nil?
|
|
79
|
-
vv = [vv] unless vv.respond_to?(:to_a)
|
|
80
|
-
vv.each do |v|
|
|
81
|
-
best = v if best.nil? || yield(v, best)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
best
|
|
85
|
-
end
|
|
86
|
-
end
|
|
@@ -1,41 +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 '../../factbase'
|
|
7
|
-
|
|
8
|
-
# Logical terms.
|
|
9
|
-
#
|
|
10
|
-
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
11
|
-
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
12
|
-
# License:: MIT
|
|
13
|
-
module Factbase::Logical
|
|
14
|
-
# Simplifies AND or OR expressions by removing duplicates
|
|
15
|
-
# @return [Factbase::Term] Simplified term
|
|
16
|
-
def and_or_simplify
|
|
17
|
-
strs = []
|
|
18
|
-
ops = []
|
|
19
|
-
@operands.each do |o|
|
|
20
|
-
o = o.simplify
|
|
21
|
-
s = o.to_s
|
|
22
|
-
next if strs.include?(s)
|
|
23
|
-
strs << s
|
|
24
|
-
ops << o
|
|
25
|
-
end
|
|
26
|
-
return ops[0] if ops.size == 1
|
|
27
|
-
self.class.new(@op, ops)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Simplifies AND expressions by removing duplicates
|
|
31
|
-
# @return [Factbase::Term] Simplified term
|
|
32
|
-
def and_simplify
|
|
33
|
-
and_or_simplify
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Simplifies OR expressions by removing duplicates
|
|
37
|
-
# @return [Factbase::Term] Simplified term
|
|
38
|
-
def or_simplify
|
|
39
|
-
and_or_simplify
|
|
40
|
-
end
|
|
41
|
-
end
|