litestack 0.4.2 → 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 +40 -1
- data/FILESYSTEMS.md +55 -0
- data/Gemfile +2 -0
- data/README.md +8 -4
- data/ROADMAP.md +6 -6
- data/assets/litestack_advantage.png +0 -0
- data/bench/bench.rb +2 -0
- data/bench/bench_cache_rails.rb +33 -2
- data/bench/bench_cache_raw.rb +36 -12
- data/bench/bench_jobs_rails.rb +3 -3
- data/bench/bench_jobs_raw.rb +3 -3
- data/bin/liteboard +16 -13
- 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 +8 -0
- data/lib/active_support/cache/litecache.rb +40 -7
- data/lib/generators/litestack/install/install_generator.rb +2 -2
- data/lib/generators/litestack/install/templates/cable.yml +0 -3
- 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 +51 -19
- data/lib/litestack/litecache.sql.yml +7 -5
- 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/litemetric.sql.yml +1 -1
- data/lib/litestack/litemetric_collector.sql.yml +1 -1
- data/lib/litestack/litequeue.rb +17 -2
- data/lib/litestack/litequeue.sql.yml +38 -5
- data/lib/litestack/litescheduler.rb +9 -4
- 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 +9 -7
- data/lib/litestack/railtie.rb +4 -2
- data/lib/litestack/version.rb +1 -1
- data/lib/litestack.rb +15 -15
- data/lib/railties/rails/commands/dbconsole.rb +5 -5
- 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 +105 -56
- data/Gemfile.lock +0 -92
@@ -3,10 +3,10 @@ schema:
|
|
3
3
|
create_table_data: >
|
4
4
|
CREATE TABLE IF NOT EXISTS data(
|
5
5
|
id TEXT PRIMARY KEY,
|
6
|
-
value
|
6
|
+
value ANY,
|
7
7
|
expires_in INTEGER,
|
8
8
|
last_used INTEGER
|
9
|
-
);
|
9
|
+
) STRICT;
|
10
10
|
create_expiry_index: >
|
11
11
|
CREATE INDEX IF NOT EXISTS expiry_index ON data (expires_in);
|
12
12
|
create_last_used_index: >
|
@@ -14,7 +14,7 @@ schema:
|
|
14
14
|
|
15
15
|
stmts:
|
16
16
|
pruner: >
|
17
|
-
DELETE FROM data WHERE expires_in <=
|
17
|
+
DELETE FROM data WHERE expires_in <= unixepoch('now');
|
18
18
|
|
19
19
|
extra_pruner: >
|
20
20
|
DELETE FROM data WHERE id IN (
|
@@ -49,7 +49,7 @@ stmts:
|
|
49
49
|
value = EXCLUDED.value,
|
50
50
|
last_used = EXCLUDED.last_used,
|
51
51
|
expires_in = EXCLUDED.expires_in;
|
52
|
-
|
52
|
+
|
53
53
|
inserter: >
|
54
54
|
INSERT INTO data (id, value, expires_in, last_used)
|
55
55
|
VALUES ($1, $2, unixepoch('now') + $3, unixepoch('now'))
|
@@ -65,7 +65,7 @@ stmts:
|
|
65
65
|
SELECT id FROM data WHERE id = $1;
|
66
66
|
|
67
67
|
getter: >
|
68
|
-
SELECT id, value, expires_in FROM data WHERE id = $1;
|
68
|
+
SELECT id, value, expires_in FROM data WHERE id = $1 AND expires_in >= unixepoch('now');
|
69
69
|
|
70
70
|
deleter: >
|
71
71
|
delete FROM data WHERE id = $1 RETURNING value;
|
@@ -79,6 +79,8 @@ stmts:
|
|
79
79
|
last_used = EXCLUDED.last_used,
|
80
80
|
expires_in = EXCLUDED.expires_in;
|
81
81
|
|
82
|
+
|
83
|
+
|
82
84
|
counter: >
|
83
85
|
SELECT count(*) FROM data;
|
84
86
|
|
data/lib/litestack/litedb.rb
CHANGED
@@ -15,7 +15,7 @@ class Litedb < ::SQLite3::Database
|
|
15
15
|
# add litesearch support
|
16
16
|
include Litesearch
|
17
17
|
|
18
|
-
#
|
18
|
+
# override the original initilaizer to allow for connection configuration
|
19
19
|
def initialize(file, options = {}, zfs = nil)
|
20
20
|
if block_given?
|
21
21
|
super(file, options, zfs) do |db|
|
@@ -31,6 +31,10 @@ class Litedb < ::SQLite3::Database
|
|
31
31
|
collect_metrics if @collecting_metrics
|
32
32
|
end
|
33
33
|
|
34
|
+
def sqlite_version
|
35
|
+
SQLite3::SQLITE_VERSION_NUMBER
|
36
|
+
end
|
37
|
+
|
34
38
|
def collecting_metrics?
|
35
39
|
@collecting_metrics
|
36
40
|
end
|
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
|
@@ -19,7 +19,7 @@ schema:
|
|
19
19
|
created_at INTEGER DEFAULT((unixepoch()/300*300)) NOT NULL,
|
20
20
|
resolution TEXT DEFAULT('minute') NOT NULL,
|
21
21
|
PRIMARY KEY(resolution, created_at, topic, name, key)
|
22
|
-
)
|
22
|
+
);
|
23
23
|
|
24
24
|
create_topic_index_on_events: >
|
25
25
|
CREATE INDEX events_topic_index ON events (resolution, created_at, topic) WHERE name = '___';
|
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,
|
@@ -29,7 +29,7 @@ module Litescheduler
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.storage
|
32
|
-
if
|
32
|
+
if fiber_backed?
|
33
33
|
Fiber.current.storage
|
34
34
|
else
|
35
35
|
Thread.current
|
@@ -37,7 +37,7 @@ module Litescheduler
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.current
|
40
|
-
if
|
40
|
+
if fiber_backed?
|
41
41
|
Fiber.current
|
42
42
|
else
|
43
43
|
Thread.current
|
@@ -64,7 +64,7 @@ module Litescheduler
|
|
64
64
|
# they must send (true) as a parameter to this method
|
65
65
|
# else it is a no-op for fibers
|
66
66
|
def self.synchronize(fiber_sync = false, &block)
|
67
|
-
if
|
67
|
+
if fiber_backed?
|
68
68
|
yield # do nothing, just run the block as is
|
69
69
|
else
|
70
70
|
mutex.synchronize(&block)
|
@@ -72,7 +72,7 @@ module Litescheduler
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def self.max_contexts
|
75
|
-
return 50 if
|
75
|
+
return 50 if fiber_backed?
|
76
76
|
5
|
77
77
|
end
|
78
78
|
|
@@ -81,4 +81,9 @@ module Litescheduler
|
|
81
81
|
# a single mutex per process (is that ok?)
|
82
82
|
@@mutex ||= Mutex.new
|
83
83
|
end
|
84
|
+
|
85
|
+
def self.fiber_backed?
|
86
|
+
backend == :fiber || backend == :polyphony
|
87
|
+
end
|
88
|
+
private_class_method :fiber_backed?
|
84
89
|
end
|
@@ -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
|
|