litestack 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.standard.yml +3 -1
- data/BENCHMARKS.md +3 -3
- data/CAVEATS.md +20 -0
- data/CHANGELOG.md +19 -4
- data/FILESYSTEMS.md +55 -0
- data/Gemfile +2 -0
- data/README.md +2 -2
- data/ROADMAP.md +6 -6
- data/assets/litestack_advantage.png +0 -0
- data/bench/bench.rb +2 -0
- data/bench/bench_cache_rails.rb +14 -17
- data/bench/bench_cache_raw.rb +18 -15
- data/bench/bench_jobs_rails.rb +3 -3
- data/bench/bench_jobs_raw.rb +3 -3
- data/bin/liteboard +16 -14
- data/gemfiles/rails70.gemfile +5 -0
- data/gemfiles/rails71.gemfile +5 -0
- data/gemfiles/rails71.gemfile.lock +264 -0
- data/lib/active_job/queue_adapters/litejob_adapter.rb +11 -3
- data/lib/active_record/connection_adapters/litedb_adapter.rb +17 -7
- data/lib/active_support/cache/litecache.rb +25 -15
- data/lib/generators/litestack/install/install_generator.rb +2 -2
- data/lib/litestack/liteboard/liteboard.rb +15 -19
- data/lib/litestack/liteboard/views/litecable.erb +1 -1
- data/lib/litestack/litecable.rb +1 -1
- data/lib/litestack/litecache.rb +23 -25
- data/lib/litestack/litedb.rb +5 -1
- data/lib/litestack/litejob.rb +1 -1
- data/lib/litestack/litejobqueue.rb +24 -14
- data/lib/litestack/litemetric.rb +7 -6
- data/lib/litestack/litequeue.rb +17 -2
- data/lib/litestack/litequeue.sql.yml +38 -5
- data/lib/litestack/litescheduler.rb +1 -2
- data/lib/litestack/litesearch/index.rb +11 -10
- data/lib/litestack/litesearch/model.rb +61 -3
- data/lib/litestack/litesearch/schema.rb +7 -2
- data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +69 -25
- data/lib/litestack/litesearch/schema_adapters.rb +4 -4
- data/lib/litestack/litesearch.rb +2 -2
- data/lib/litestack/litesupport.rb +7 -6
- data/lib/litestack/railtie.rb +4 -2
- data/lib/litestack/version.rb +1 -1
- data/lib/litestack.rb +15 -15
- data/lib/sequel/adapters/litedb.rb +9 -1
- data/lib/sequel/adapters/shared/litedb.rb +2 -2
- data/scripts/build_metrics.rb +2 -2
- data/scripts/test_cable.rb +1 -1
- metadata +95 -59
data/lib/litestack/litejob.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_stringe_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "litejobqueue"
|
4
4
|
|
5
5
|
##
|
6
6
|
# Litejob is a Ruby module that enables seamless integration of the Litejobqueue job queueing system into Ruby applications. By including the Litejob module in a class and implementing the #perform method, developers can easily enqueue and process jobs asynchronously.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_stringe_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
3
|
+
require_relative "litequeue"
|
4
|
+
require_relative "litemetric"
|
5
5
|
|
6
6
|
##
|
7
7
|
# Litejobqueue is a job queueing and processing system designed for Ruby applications. It is built on top of SQLite, which is an embedded relational database management system that is #lightweight and fast.
|
@@ -15,7 +15,7 @@ class Litejobqueue < Litequeue
|
|
15
15
|
include Litemetric::Measurable
|
16
16
|
|
17
17
|
# the default options for the job queue
|
18
|
-
# can be
|
18
|
+
# can be overridden by passing new options in a hash
|
19
19
|
# to Litejobqueue.new, it will also be then passed to the underlying Litequeue object
|
20
20
|
# config_path: "./litejob.yml" -> were to find the configuration file (if any)
|
21
21
|
# path: "./db/queue.db"
|
@@ -136,20 +136,28 @@ class Litejobqueue < Litequeue
|
|
136
136
|
# @@queue = nil
|
137
137
|
close
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
private
|
141
|
+
|
142
|
+
def prepare_search_options(opts)
|
143
|
+
sql_opts = super(opts)
|
144
|
+
sql_opts[:klass] = opts[:klass]
|
145
|
+
sql_opts[:params] = opts[:params]
|
146
|
+
sql_opts
|
147
|
+
end
|
141
148
|
|
142
149
|
def exit_callback
|
143
150
|
@running = false # stop all workers
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
+
if @jobs_in_flight > 0
|
152
|
+
puts "--- Litejob detected an exit, cleaning up"
|
153
|
+
index = 0
|
154
|
+
while @jobs_in_flight > 0 && index < 30 # 3 seconds grace period for jobs to finish
|
155
|
+
puts "--- Waiting for #{@jobs_in_flight} jobs to finish"
|
156
|
+
sleep 0.1
|
157
|
+
index += 1
|
158
|
+
end
|
159
|
+
puts " --- Exiting with #{@jobs_in_flight} jobs in flight"
|
151
160
|
end
|
152
|
-
puts " --- Exiting with #{@jobs_in_flight} jobs in flight"
|
153
161
|
end
|
154
162
|
|
155
163
|
def setup
|
@@ -179,6 +187,8 @@ class Litejobqueue < Litequeue
|
|
179
187
|
|
180
188
|
# create a worker according to environment
|
181
189
|
def create_worker
|
190
|
+
# temporarily stop this feature until a better solution is implemented
|
191
|
+
#return if defined?(Rails) && !defined?(Rails::Server)
|
182
192
|
Litescheduler.spawn do
|
183
193
|
worker_sleep_index = 0
|
184
194
|
while @running
|
@@ -186,7 +196,7 @@ class Litejobqueue < Litequeue
|
|
186
196
|
@queues.each do |priority, queues| # iterate through the levels
|
187
197
|
queues.each do |queue, spawns| # iterate through the queues in the level
|
188
198
|
batched = 0
|
189
|
-
|
199
|
+
|
190
200
|
while (batched < priority) && (payload = pop(queue, 1)) # fearlessly use the same queue object
|
191
201
|
capture(:dequeue, queue)
|
192
202
|
processed += 1
|
@@ -201,7 +211,7 @@ class Litejobqueue < Litequeue
|
|
201
211
|
end
|
202
212
|
if processed == 0
|
203
213
|
sleep @options[:sleep_intervals][worker_sleep_index]
|
204
|
-
worker_sleep_index += 1 if worker_sleep_index < @options[:sleep_intervals].length - 1
|
214
|
+
worker_sleep_index += 1 if worker_sleep_index < (@options[:sleep_intervals].length - 1)
|
205
215
|
else
|
206
216
|
worker_sleep_index = 0 # reset the index
|
207
217
|
end
|
data/lib/litestack/litemetric.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "singleton"
|
4
4
|
|
5
|
-
require_relative "
|
5
|
+
require_relative "litesupport"
|
6
6
|
|
7
7
|
# this class is a singleton
|
8
8
|
# and should remain so
|
@@ -148,10 +148,11 @@ class Litemetric
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def exit_callback
|
151
|
-
return unless @collector.count > 0
|
152
|
-
warn "--- Litemetric detected an exit, flushing metrics"
|
153
151
|
@running = false
|
154
|
-
@collector.
|
152
|
+
if @collector.count > 0
|
153
|
+
warn "--- Litemetric detected an exit, flushing metrics"
|
154
|
+
@collector.flush
|
155
|
+
end
|
155
156
|
end
|
156
157
|
|
157
158
|
def setup
|
@@ -203,8 +204,8 @@ class Litemetric
|
|
203
204
|
def create_snapshotter
|
204
205
|
Litescheduler.spawn do
|
205
206
|
while @running
|
206
|
-
sleep @litemetric.options[:snapshot_interval]
|
207
207
|
capture_snapshot
|
208
|
+
sleep @litemetric.options[:snapshot_interval]
|
208
209
|
end
|
209
210
|
end
|
210
211
|
end
|
@@ -275,7 +276,7 @@ class Litemetric
|
|
275
276
|
def capture_single_key(topic, event, key, value, time = nil)
|
276
277
|
run_stmt(:capture_event, topic.to_s, event.to_s, key.to_s, time, 1, value)
|
277
278
|
end
|
278
|
-
|
279
|
+
|
279
280
|
def count
|
280
281
|
run_stmt(:event_count)[0][0]
|
281
282
|
end
|
data/lib/litestack/litequeue.rb
CHANGED
@@ -13,7 +13,7 @@ require_relative "litesupport"
|
|
13
13
|
|
14
14
|
class Litequeue
|
15
15
|
# the default options for the queue
|
16
|
-
# can be
|
16
|
+
# can be overridden by passing new options in a hash
|
17
17
|
# to Litequeue.new
|
18
18
|
# path: "./queue.db"
|
19
19
|
# mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory
|
@@ -80,7 +80,7 @@ class Litequeue
|
|
80
80
|
run_sql("DELETE FROM queue WHERE iif(?1 IS NOT NULL, name = ?1, TRUE)", queue)
|
81
81
|
end
|
82
82
|
|
83
|
-
# returns a count of entries in all queues, or if a queue name is given,
|
83
|
+
# returns a count of entries in all queues, or if a queue name is given, returns the count of entries in that queue
|
84
84
|
def count(queue = nil)
|
85
85
|
run_sql("SELECT count(*) FROM queue WHERE iif(?1 IS NOT NULL, name = ?1, TRUE)", queue)[0][0]
|
86
86
|
end
|
@@ -111,9 +111,24 @@ class Litequeue
|
|
111
111
|
queues: queues
|
112
112
|
}
|
113
113
|
end
|
114
|
+
|
115
|
+
def find(opts = {})
|
116
|
+
run_stmt(:search, prepare_search_options(opts))
|
117
|
+
end
|
114
118
|
|
115
119
|
private
|
116
120
|
|
121
|
+
def prepare_search_options(opts)
|
122
|
+
sql_opts = {}
|
123
|
+
sql_opts[:fire_at_from] = opts[:fire_at][0] rescue nil
|
124
|
+
sql_opts[:fire_at_to] = opts[:fire_at][1] rescue nil
|
125
|
+
sql_opts[:created_at_from] = opts[:created_at][0] rescue nil
|
126
|
+
sql_opts[:created_at_to] = opts[:created_at][1] rescue nil
|
127
|
+
sql_opts[:name] = opts[:queue]
|
128
|
+
sql_opts[:dir] = opts[:dir] == :desc ? -1 : 1
|
129
|
+
sql_opts
|
130
|
+
end
|
131
|
+
|
117
132
|
def create_connection
|
118
133
|
super("#{__dir__}/litequeue.sql.yml") do |conn|
|
119
134
|
conn.wal_autocheckpoint = 10000
|
@@ -4,7 +4,7 @@ schema:
|
|
4
4
|
CREATE TABLE IF NOT EXISTS queue(
|
5
5
|
id TEXT PRIMARY KEY DEFAULT(hex(randomblob(32))) NOT NULL ON CONFLICT REPLACE,
|
6
6
|
name TEXT DEFAULT('default') NOT NULL ON CONFLICT REPLACE,
|
7
|
-
fire_at
|
7
|
+
fire_at REAL DEFAULT(unixepoch('subsec')) NOT NULL ON CONFLICT REPLACE,
|
8
8
|
value TEXT,
|
9
9
|
created_at INTEGER DEFAULT(unixepoch()) NOT NULL ON CONFLICT REPLACE
|
10
10
|
) WITHOUT ROWID;
|
@@ -16,21 +16,36 @@ stmts:
|
|
16
16
|
|
17
17
|
push: >
|
18
18
|
INSERT INTO queue(id, name, fire_at, value)
|
19
|
-
VALUES (hex(randomblob(32)), $1, (unixepoch() + $2), $3)
|
19
|
+
VALUES (hex(randomblob(32)), $1, (unixepoch('subsec') + $2), $3)
|
20
20
|
RETURNING id, name;
|
21
21
|
|
22
22
|
repush: >
|
23
23
|
INSERT INTO queue(id, name, fire_at, value)
|
24
|
-
VALUES (?, ?, (unixepoch() + ?), ?)
|
24
|
+
VALUES (?, ?, (unixepoch('subsec') + ?), ?)
|
25
25
|
RETURNING name;
|
26
26
|
|
27
|
+
pop_in_place: >
|
28
|
+
UPDATE queue
|
29
|
+
SET
|
30
|
+
name = '->:' || name,
|
31
|
+
fire_at = unixepoch('subsec')
|
32
|
+
WHERE (name, fire_at, id)
|
33
|
+
IN (
|
34
|
+
SELECT name, fire_at, id FROM queue
|
35
|
+
WHERE name = ifnull($1, 'default')
|
36
|
+
AND fire_at <= (unixepoch('subsec'))
|
37
|
+
ORDER BY fire_at ASC
|
38
|
+
LIMIT ifnull($2, 1)
|
39
|
+
)
|
40
|
+
RETURNING id, value;
|
41
|
+
|
27
42
|
pop: >
|
28
43
|
DELETE FROM queue
|
29
44
|
WHERE (name, fire_at, id)
|
30
45
|
IN (
|
31
46
|
SELECT name, fire_at, id FROM queue
|
32
47
|
WHERE name = ifnull($1, 'default')
|
33
|
-
AND fire_at <= (unixepoch())
|
48
|
+
AND fire_at <= (unixepoch('subsec'))
|
34
49
|
ORDER BY fire_at ASC
|
35
50
|
LIMIT ifnull($2, 1)
|
36
51
|
)
|
@@ -40,7 +55,25 @@ stmts:
|
|
40
55
|
DELETE FROM queue
|
41
56
|
WHERE id = $1
|
42
57
|
RETURNING value;
|
43
|
-
|
58
|
+
|
59
|
+
search: >
|
60
|
+
SELECT *, :params, value ->> '$.params', value ->> '$.params' LIKE '%'||'three_second'||'%' FROM queue
|
61
|
+
WHERE
|
62
|
+
name = ifnull(:name, name)
|
63
|
+
AND
|
64
|
+
iif(:fire_at_from, fire_at >= :fire_at_from, true)
|
65
|
+
AND
|
66
|
+
iif(:fire_at_to, fire_at <= :fire_at_to, true)
|
67
|
+
AND
|
68
|
+
iif(:created_at_from, created_at >= :created_at_from, true)
|
69
|
+
AND
|
70
|
+
iif(:created_at_to, created_at <= :created_at_to, true)
|
71
|
+
AND
|
72
|
+
iif(:klass IS NOT NULL, value ->> '$.klass' LIKE '%'||:klass||'%', true)
|
73
|
+
AND
|
74
|
+
iif(:params IS NOT NULL, value ->> '$.params' LIKE '%'||:params||'%', true)
|
75
|
+
ORDER BY created_at * :dir;
|
76
|
+
|
44
77
|
info: >
|
45
78
|
SELECT
|
46
79
|
name,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require "oj"
|
2
|
-
require_relative "
|
2
|
+
require_relative "schema"
|
3
3
|
|
4
4
|
class Litesearch::Index
|
5
5
|
DEFAULT_SEARCH_OPTIONS = {limit: 25, offset: 0}
|
@@ -71,7 +71,7 @@ class Litesearch::Index
|
|
71
71
|
|
72
72
|
def rebuild!
|
73
73
|
if @db.transaction_active?
|
74
|
-
|
74
|
+
do_rebuild
|
75
75
|
else
|
76
76
|
@db.transaction(:immediate) { do_rebuild }
|
77
77
|
end
|
@@ -102,19 +102,20 @@ class Litesearch::Index
|
|
102
102
|
rs = @stmts[:search].execute(term, options[:limit], options[:offset])
|
103
103
|
generate_results(rs)
|
104
104
|
end
|
105
|
-
|
106
|
-
def similar(id, limit=10)
|
105
|
+
|
106
|
+
def similar(id, limit = 10)
|
107
107
|
# pp term = @db.execute(@schema.sql_for(:similarity_query), id)
|
108
|
-
if @schema.schema[:tokenizer] == :trigram
|
108
|
+
rs = if @schema.schema[:tokenizer] == :trigram
|
109
109
|
# just use the normal similarity approach for now
|
110
110
|
# need to recondisder that for trigram indexes later
|
111
|
-
|
111
|
+
@stmts[:similar].execute(id, limit) # standard:disable Style/IdenticalConditionalBranches
|
112
112
|
else
|
113
|
-
|
113
|
+
@stmts[:similar].execute(id, limit) # standard:disable Style/IdenticalConditionalBranches
|
114
114
|
end
|
115
|
+
|
115
116
|
generate_results(rs)
|
116
117
|
end
|
117
|
-
|
118
|
+
|
118
119
|
def clear!
|
119
120
|
@stmts[:delete_all].execute!(id)
|
120
121
|
end
|
@@ -140,7 +141,7 @@ class Litesearch::Index
|
|
140
141
|
else
|
141
142
|
result = rs.to_a
|
142
143
|
end
|
143
|
-
result
|
144
|
+
result
|
144
145
|
end
|
145
146
|
|
146
147
|
def exists?(name)
|
@@ -175,7 +176,7 @@ class Litesearch::Index
|
|
175
176
|
|
176
177
|
def do_modify(new_schema)
|
177
178
|
changes = @schema.compare(new_schema)
|
178
|
-
# ensure the new schema maintains
|
179
|
+
# ensure the new schema maintains field order
|
179
180
|
new_schema.order_fields(@schema)
|
180
181
|
# with the changes object decide what needs to be done to the schema
|
181
182
|
requires_schema_change = false
|
@@ -11,6 +11,7 @@ module Litesearch::Model
|
|
11
11
|
klass.include Litesearch::Model::ActiveRecordInstanceMethods
|
12
12
|
klass.extend Litesearch::Model::ActiveRecordClassMethods
|
13
13
|
ActiveRecord::Base.extend Litesearch::Model::BaseClassMethods
|
14
|
+
Litesearch::Schema.prepend Litesearch::Model::ActiveRecordSchemaMethods
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
@@ -21,7 +22,7 @@ module Litesearch::Model
|
|
21
22
|
end
|
22
23
|
|
23
24
|
module InstanceMethods
|
24
|
-
def similar(limit=10)
|
25
|
+
def similar(limit = 10)
|
25
26
|
conn = self.class.get_connection
|
26
27
|
idx = conn.search_index(self.class.send(:index_name))
|
27
28
|
r_a_h = conn.results_as_hash
|
@@ -36,14 +37,38 @@ module Litesearch::Model
|
|
36
37
|
end
|
37
38
|
result
|
38
39
|
end
|
39
|
-
|
40
40
|
end
|
41
41
|
|
42
42
|
module ClassMethods
|
43
|
+
|
43
44
|
def litesearch
|
45
|
+
# it is possible that this code is running when there is no table created yet
|
46
|
+
if !defined?(ActiveRecord::Base).nil? && ancestors.include?(ActiveRecord::Base)
|
47
|
+
unless table_exists?
|
48
|
+
# capture the schema block
|
49
|
+
@schema = ::Litesearch::Schema.new
|
50
|
+
@schema.model_class = self if @schema.respond_to? :model_class
|
51
|
+
@schema.type :backed
|
52
|
+
@schema.table table_name.to_sym
|
53
|
+
yield @schema
|
54
|
+
@schema.post_init
|
55
|
+
@schema_not_created = true
|
56
|
+
after_initialize do
|
57
|
+
if self.class.instance_variable_get(:@schema_not_created)
|
58
|
+
self.class.get_connection.search_index(self.class.index_name) do |schema|
|
59
|
+
@schema.model_class = self.class if @schema.respond_to? :model_class
|
60
|
+
schema.merge(self.class.instance_variable_get(:@schema))
|
61
|
+
end
|
62
|
+
self.class.instance_variable_set(:@schema_not_created, false)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return nil
|
66
|
+
end
|
67
|
+
end
|
44
68
|
idx = get_connection.search_index(index_name) do |schema|
|
45
69
|
schema.type :backed
|
46
70
|
schema.table table_name.to_sym
|
71
|
+
schema.model_class = self if schema.respond_to? :model_class
|
47
72
|
yield schema
|
48
73
|
schema.post_init
|
49
74
|
@schema = schema # save the schema
|
@@ -77,7 +102,7 @@ module Litesearch::Model
|
|
77
102
|
else
|
78
103
|
models_hash = search_models
|
79
104
|
end
|
80
|
-
# remove the models from the options hash before passing it
|
105
|
+
# remove the models from the options hash before passing it to the query
|
81
106
|
options.delete(:models)
|
82
107
|
models_hash.each do |name, klass|
|
83
108
|
selects << "SELECT '#{name}' AS model, rowid, -rank AS search_rank FROM #{index_name_for_table(klass.table_name)}(:term)"
|
@@ -109,6 +134,32 @@ module Litesearch::Model
|
|
109
134
|
end
|
110
135
|
end
|
111
136
|
|
137
|
+
module ActiveRecordSchemaMethods
|
138
|
+
|
139
|
+
attr_accessor :model_class
|
140
|
+
|
141
|
+
def field(name, attributes = {})
|
142
|
+
keys = attributes.keys
|
143
|
+
if keys.include?(:action_text) || keys.include?(:rich_text)
|
144
|
+
attributes[:source] = "#{ActionText::RichText.table_name}.body" rescue "action_text_rich_texts.body"
|
145
|
+
attributes[:reference] = :record_id
|
146
|
+
attributes[:conditions] = { record_type: model_class.name }
|
147
|
+
attributes[:target] = nil
|
148
|
+
elsif keys.include? :as
|
149
|
+
attributes[:source] = attributes[:target] unless attributes[:source]
|
150
|
+
attributes[:reference] = "#{attributes[:as]}_id"
|
151
|
+
attributes[:conditions] = {"#{attributes[:as]}_type".to_sym => model_class.name }
|
152
|
+
attributes[:target] = nil
|
153
|
+
end
|
154
|
+
super(name, attributes)
|
155
|
+
end
|
156
|
+
|
157
|
+
def allowed_attributes
|
158
|
+
super + [:polymorphic, :as, :action_text]
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
112
163
|
module ActiveRecordInstanceMethods; end
|
113
164
|
|
114
165
|
module ActiveRecordClassMethods
|
@@ -121,6 +172,13 @@ module Litesearch::Model
|
|
121
172
|
end
|
122
173
|
|
123
174
|
def search(term)
|
175
|
+
if @schema_not_created
|
176
|
+
get_connection.search_index(index_name) do |schema|
|
177
|
+
schema.merge(@schema)
|
178
|
+
schema.model_class = self if schema.respond_to? :model_class
|
179
|
+
end
|
180
|
+
@schema_not_created = false
|
181
|
+
end
|
124
182
|
self.select(
|
125
183
|
"#{table_name}.*"
|
126
184
|
).joins(
|
@@ -1,6 +1,7 @@
|
|
1
|
-
require_relative "
|
1
|
+
require_relative "schema_adapters"
|
2
2
|
|
3
3
|
class Litesearch::Schema
|
4
|
+
|
4
5
|
TOKENIZERS = {
|
5
6
|
porter: "porter unicode61 remove_diacritics 2",
|
6
7
|
unicode: "unicode61 remove_diacritics 2",
|
@@ -34,6 +35,10 @@ class Litesearch::Schema
|
|
34
35
|
@schema[:fields] = {} unless @schema[:fields]
|
35
36
|
end
|
36
37
|
|
38
|
+
def merge(other_schema)
|
39
|
+
@schema.merge!(other_schema.schema)
|
40
|
+
end
|
41
|
+
|
37
42
|
# schema definition API
|
38
43
|
def name(new_name)
|
39
44
|
@schema[:name] = new_name
|
@@ -181,7 +186,7 @@ class Litesearch::Schema
|
|
181
186
|
end
|
182
187
|
|
183
188
|
def allowed_attributes
|
184
|
-
[:weight, :col, :target]
|
189
|
+
[:weight, :col, :target, :source, :conditions, :reference]
|
185
190
|
end
|
186
191
|
end
|
187
192
|
|
@@ -25,20 +25,24 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
25
25
|
|
26
26
|
def create_primary_triggers_sql(active = false)
|
27
27
|
when_stmt = "TRUE"
|
28
|
-
cols = active_cols_names
|
28
|
+
cols = active_cols_names.select{|n| !n.nil?}
|
29
29
|
if (filter = @schema[:filter_column])
|
30
30
|
when_stmt = "NEW.#{filter} = TRUE"
|
31
31
|
cols << filter
|
32
32
|
end
|
33
|
+
update_filter = ""
|
34
|
+
if cols.length > 0
|
35
|
+
" OF #{cols.join(', ')} "
|
36
|
+
end
|
33
37
|
|
34
38
|
<<-SQL
|
35
39
|
CREATE TRIGGER #{name}_insert AFTER INSERT ON #{table} WHEN #{when_stmt} BEGIN
|
36
40
|
INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) VALUES (NEW.rowid, #{trigger_cols_sql});
|
37
41
|
END;
|
38
|
-
CREATE TRIGGER #{name}_update AFTER UPDATE
|
42
|
+
CREATE TRIGGER #{name}_update AFTER UPDATE #{update_filter} ON #{table} WHEN #{when_stmt} BEGIN
|
39
43
|
INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) VALUES (NEW.rowid, #{trigger_cols_sql});
|
40
44
|
END;
|
41
|
-
CREATE TRIGGER #{name}_update_not AFTER UPDATE
|
45
|
+
CREATE TRIGGER #{name}_update_not AFTER UPDATE #{update_filter} ON #{table} WHEN NOT #{when_stmt} BEGIN
|
42
46
|
DELETE FROM #{name} WHERE rowid = NEW.rowid;
|
43
47
|
END;
|
44
48
|
CREATE TRIGGER #{name}_delete AFTER DELETE ON #{table} BEGIN
|
@@ -51,19 +55,41 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
51
55
|
"DROP TRIGGER IF EXISTS #{target_table}_#{target_col}_#{col}_#{name}_update;"
|
52
56
|
end
|
53
57
|
|
58
|
+
def drop_secondary_trigger_poly_sql(target_table, target_col, col)
|
59
|
+
"DROP TRIGGER IF EXISTS #{target_table}_#{target_col}_#{name}_update;"
|
60
|
+
end
|
61
|
+
|
54
62
|
def create_secondary_trigger_sql(target_table, target_col, col)
|
55
63
|
<<~SQL
|
56
|
-
CREATE TRIGGER #{target_table}_#{target_col}_#{col}_#{name}_update AFTER UPDATE OF #{target_col} ON #{target_table} BEGIN
|
64
|
+
CREATE TRIGGER IF NOT EXISTS #{target_table}_#{target_col}_#{col}_#{name}_update AFTER UPDATE OF #{target_col} ON #{target_table} BEGIN
|
57
65
|
#{rebuild_sql} AND #{table}.#{col} = NEW.id;
|
58
66
|
END;
|
59
67
|
SQL
|
60
68
|
end
|
61
69
|
|
70
|
+
def create_secondary_trigger_poly_sql(target_table, target_col, col, conditions)
|
71
|
+
conditions_sql = conditions.collect{|k, v| "NEW.#{k} = '#{v}'"}.join(" AND ")
|
72
|
+
<<~SQL
|
73
|
+
CREATE TRIGGER IF NOT EXISTS #{target_table}_#{target_col}_#{name}_insert AFTER INSERT ON #{target_table} WHEN #{conditions_sql} BEGIN
|
74
|
+
#{rebuild_sql};
|
75
|
+
END;
|
76
|
+
CREATE TRIGGER IF NOT EXISTS #{target_table}_#{target_col}_#{name}_update AFTER UPDATE ON #{target_table} WHEN #{conditions_sql} BEGIN
|
77
|
+
#{rebuild_sql};
|
78
|
+
END;
|
79
|
+
SQL
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
62
84
|
def drop_secondary_triggers_sql
|
63
85
|
sql = ""
|
64
86
|
@schema[:fields].each do |name, field|
|
65
87
|
if field[:trigger_sql]
|
66
|
-
|
88
|
+
if field[:col]
|
89
|
+
sql << drop_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
|
90
|
+
elsif field[:source]
|
91
|
+
sql << drop_secondary_trigger_poly_sql(field[:target_table], field[:target_col], name)
|
92
|
+
end
|
67
93
|
end
|
68
94
|
end
|
69
95
|
sql.empty? ? nil : sql
|
@@ -73,18 +99,18 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
73
99
|
sql = ""
|
74
100
|
@schema[:fields].each do |name, field|
|
75
101
|
if field[:trigger_sql]
|
76
|
-
|
102
|
+
if field[:col]
|
103
|
+
sql << create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
|
104
|
+
elsif field[:source]
|
105
|
+
sql << create_secondary_trigger_poly_sql(field[:target_table], field[:target_col], name, field[:conditions])
|
106
|
+
end
|
77
107
|
end
|
78
108
|
end
|
79
109
|
sql.empty? ? nil : sql
|
80
110
|
end
|
81
111
|
|
82
112
|
def rebuild_sql
|
83
|
-
|
84
|
-
jcs = join_conditions_sql
|
85
|
-
fs = filter_sql
|
86
|
-
conditions = " ON #{jcs} #{fs}" unless jcs.empty? && fs.empty?
|
87
|
-
"INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) SELECT #{table}.id, #{select_cols_sql} FROM #{join_tables_sql} #{conditions}"
|
113
|
+
"INSERT OR REPLACE INTO #{name}(rowid, #{active_field_names.join(", ")}) SELECT #{table}.id, #{select_cols_sql} FROM #{joins_sql} #{filter_sql}"
|
88
114
|
end
|
89
115
|
|
90
116
|
def enrich_schema
|
@@ -92,12 +118,23 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
92
118
|
if field[:target] && !field[:target].start_with?("#{table}.")
|
93
119
|
field[:target] = field[:target].downcase
|
94
120
|
target_table, target_col = field[:target].split(".")
|
95
|
-
field[:col] = "#{name}_id"
|
121
|
+
field[:col] = :"#{name}_id" unless field[:col]
|
96
122
|
field[:target_table] = target_table.to_sym
|
97
123
|
field[:target_col] = target_col.to_sym
|
98
124
|
field[:sql] = "(SELECT #{field[:target_col]} FROM #{field[:target_table]} WHERE id = NEW.#{field[:col]})"
|
99
125
|
field[:trigger_sql] = true # create_secondary_trigger_sql(field[:target_table], field[:target_col], field[:col])
|
100
126
|
field[:target_table_alias] = "#{field[:target_table]}_#{name}"
|
127
|
+
elsif field[:source]
|
128
|
+
field[:source] = field[:source].downcase
|
129
|
+
target_table, target_col = field[:source].split(".")
|
130
|
+
field[:target_table] = target_table.to_sym
|
131
|
+
field[:target_col] = target_col.to_sym
|
132
|
+
field[:conditions_sql] = field[:conditions].collect{|k, v| "#{k} = '#{v}'"}.join(" AND ") if field[:conditions]
|
133
|
+
field[:sql] = "SELECT #{field[:target_col]} FROM #{field[:target_table]} WHERE #{field[:reference]} = NEW.id"
|
134
|
+
field[:sql] += " AND #{field[:conditions_sql]}" if field[:conditions_sql]
|
135
|
+
field[:sql] = "(#{field[:sql]})"
|
136
|
+
field[:trigger_sql] = true
|
137
|
+
field[:target_table_alias] = "#{field[:target_table]}_#{name}"
|
101
138
|
else
|
102
139
|
field[:col] = name unless field[:col]
|
103
140
|
field[:sql] = field[:col]
|
@@ -109,7 +146,7 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
109
146
|
|
110
147
|
def filter_sql
|
111
148
|
sql = ""
|
112
|
-
sql << "
|
149
|
+
sql << " WHERE #{@schema[:filter_column]} = TRUE " if @schema[:filter_column]
|
113
150
|
sql
|
114
151
|
end
|
115
152
|
|
@@ -124,20 +161,27 @@ class Litesearch::Schema::BackedAdapter < Litesearch::Schema::ContentlessAdapter
|
|
124
161
|
(!field[:trigger_sql].nil?) ? "#{field[:target_table_alias]}.#{field[:target_col]}" : field[:target]
|
125
162
|
end.join(", ")
|
126
163
|
end
|
127
|
-
|
128
|
-
def
|
129
|
-
|
164
|
+
|
165
|
+
def joins_sql
|
166
|
+
joins = [@schema[:table]]
|
130
167
|
active_fields.each do |name, field|
|
131
|
-
|
168
|
+
if field[:trigger_sql]
|
169
|
+
join_table = ""
|
170
|
+
join_table << "#{field[:target_table]} AS #{field[:target_table_alias]} ON "
|
171
|
+
if field[:col]
|
172
|
+
join_table << "#{field[:target_table_alias]}.id = #{@schema[:table]}.#{field[:col]}" if field[:col]
|
173
|
+
elsif field[:source]
|
174
|
+
join_table << "#{field[:target_table_alias]}.#{field[:reference]} = #{@schema[:table]}.id"
|
175
|
+
if field[:conditions]
|
176
|
+
join_table << " AND "
|
177
|
+
join_table << field[:conditions].collect{|k, v| "#{field[:target_table_alias]}.#{k} = '#{v}'"}.join(" AND ")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
joins << join_table
|
181
|
+
end
|
132
182
|
end
|
133
|
-
|
183
|
+
joins.join(" LEFT JOIN ")
|
134
184
|
end
|
135
185
|
|
136
|
-
|
137
|
-
conditions = []
|
138
|
-
active_fields.each do |name, field|
|
139
|
-
conditions << "#{field[:target_table_alias]}.id = #{@schema[:table]}.#{field[:col]}" if field[:trigger_sql]
|
140
|
-
end
|
141
|
-
conditions.join(" AND ")
|
142
|
-
end
|
186
|
+
|
143
187
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative "
|
2
|
-
require_relative "
|
3
|
-
require_relative "
|
4
|
-
require_relative "
|
1
|
+
require_relative "schema_adapters/basic_adapter"
|
2
|
+
require_relative "schema_adapters/standalone_adapter"
|
3
|
+
require_relative "schema_adapters/contentless_adapter"
|
4
|
+
require_relative "schema_adapters/backed_adapter"
|
data/lib/litestack/litesearch.rb
CHANGED