runestone 1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +101 -1
- data/db/migrate/{20181101150207_create_ts_tables.rb → 20181101150207_create_runestone_tables.rb} +3 -2
- data/lib/runestone.rb +47 -5
- data/lib/runestone/active_record/relation_methods.rb +8 -5
- data/lib/runestone/corpus.rb +4 -3
- data/lib/runestone/version.rb +1 -1
- data/lib/runestone/web_search.rb +1 -5
- data/runestone.gemspec +1 -1
- data/test/corpus_test.rb +20 -1
- data/test/database.rb +2 -2
- data/test/delayed_index_test.rb +2 -2
- data/test/highlight_test.rb +23 -1
- data/test/indexing_test.rb +2 -2
- data/test/multi_index_test.rb +4 -4
- data/test/ordering_test.rb +28 -0
- data/test/query_test.rb +26 -26
- data/test/synonym_test.rb +18 -18
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c069ec88ae02b52e3cb0a155efe7d17b0cb3d0f9b248e74deec97cb1ed752e0
|
4
|
+
data.tar.gz: 4e627b4879f88ae65b8b56090604e7e8fe0379f93f5a72b4142d76d5eabf08f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f3f103de6f864ce9d8047232023f1c6db98867be4a7015d899c088875b830d9ee34e84c31d23b18bbdb88fc9058efe7a455891958d5adbb6b3600aed0ff4552
|
7
|
+
data.tar.gz: 014ab3c81cb2c6df0473cd5ec0c65a35a6aa46a41c7f351962b26a97abc32eac74820738bcccddfa0d67b5a3b8c715d8ed5a6d1810d1099be1f134d85ac54679
|
data/README.md
CHANGED
@@ -4,7 +4,107 @@ Runestone provides full text search PostgreSQL's full text search capabilities.
|
|
4
4
|
It was inspired by [Postgres full-text search is Good Enough!][1] and
|
5
5
|
[Super Fuzzy Searching on PostgreSQL][2]
|
6
6
|
|
7
|
+
## Installation
|
7
8
|
|
9
|
+
Install Runestone from RubyGems:
|
10
|
+
|
11
|
+
``` sh
|
12
|
+
$ gem install runestone
|
13
|
+
```
|
14
|
+
|
15
|
+
Or include it in your project's `Gemfile` with Bundler:
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
gem 'runestone'
|
19
|
+
```
|
20
|
+
|
21
|
+
After installation, run the Runestone's migration to to enable the necessary database extensions and create the runestones and runestone corpus tables.
|
22
|
+
|
23
|
+
```sh
|
24
|
+
$ rails db:migrate
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Indexing
|
30
|
+
|
31
|
+
To index your ActiveRecord Models:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class Building < ApplicationRecord
|
35
|
+
|
36
|
+
runestone do
|
37
|
+
index 'name', 'addresses.global', 'addresses.national'
|
38
|
+
|
39
|
+
attributes(:name)
|
40
|
+
attributes(:size, :floors)
|
41
|
+
attribute(:addresses) {
|
42
|
+
addresses&.map{|address| {
|
43
|
+
local: address.local,
|
44
|
+
regional: address.regional,
|
45
|
+
national: address.national,
|
46
|
+
global: address.global
|
47
|
+
} }
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
When searching the attribute(s) will be available in `data` on the result(s), but only the attributes specified by `index` will indexed and used for searching.
|
55
|
+
|
56
|
+
### Searching
|
57
|
+
|
58
|
+
To search for the Building:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Building.search("Empire")
|
62
|
+
```
|
63
|
+
|
64
|
+
You can also search through all indexed models with:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
Runestone::Model.search("needle")
|
68
|
+
```
|
69
|
+
|
70
|
+
Additionally you can highlight the results. When this is done each result will have a `highlights` attribute which is the same as data, but with matches wrapped in a `<b>` tag:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Runestone::Model.highlight(@results, "needle")
|
74
|
+
```
|
75
|
+
|
76
|
+
## Configuration
|
77
|
+
|
78
|
+
### Synonym
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Runestone.add_synonym('ten', '10')
|
82
|
+
|
83
|
+
Runestone.add_synonym('one hundred', '100')
|
84
|
+
Runestone.add_synonym('100', 'one hundred')
|
85
|
+
```
|
86
|
+
|
87
|
+
### Defaults
|
88
|
+
|
89
|
+
#### dictionary
|
90
|
+
|
91
|
+
The default dictionary that Runestone uses is the `runestone` dictionary. Which
|
92
|
+
is the `simple` dictionary in PostgreSQL with `unaccent` to tranliterate some
|
93
|
+
characters to the ASCII equivlent.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Runestone.dictionary = :runesonte
|
97
|
+
```
|
98
|
+
|
99
|
+
#### normalization for ranking
|
100
|
+
|
101
|
+
Ranking can be configured to use the `normalization` paramater as described
|
102
|
+
in the [PostgreSQL documentation][3]. The default is `16`
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Runestone.dictionary = 16
|
106
|
+
```
|
8
107
|
|
9
108
|
[1]: http://rachbelaid.com/postgres-full-text-search-is-good-enough/
|
10
|
-
[2]: http://www.www-old.bartlettpublishing.com/site/bartpub/blog/3/entry/350
|
109
|
+
[2]: http://www.www-old.bartlettpublishing.com/site/bartpub/blog/3/entry/350
|
110
|
+
[3]: https://www.postgresql.org/docs/13/textsearch-controls.html#TEXTSEARCH-RANKING
|
data/db/migrate/{20181101150207_create_ts_tables.rb → 20181101150207_create_runestone_tables.rb}
RENAMED
@@ -4,6 +4,7 @@ class CreateRunestoneTables < ActiveRecord::Migration[6.0]
|
|
4
4
|
enable_extension 'pgcrypto'
|
5
5
|
enable_extension 'pg_trgm'
|
6
6
|
enable_extension 'fuzzystrmatch'
|
7
|
+
enable_extension 'unaccent'
|
7
8
|
|
8
9
|
create_table :runestones, id: :uuid do |t|
|
9
10
|
t.belongs_to :record, type: :uuid, polymorphic: true, null: false
|
@@ -21,8 +22,8 @@ class CreateRunestoneTables < ActiveRecord::Migration[6.0]
|
|
21
22
|
|
22
23
|
CREATE INDEX runestone_corpus_trgm_idx ON runestone_corpus USING GIN (word gin_trgm_ops);
|
23
24
|
|
24
|
-
CREATE TEXT SEARCH CONFIGURATION
|
25
|
-
ALTER TEXT SEARCH CONFIGURATION
|
25
|
+
CREATE TEXT SEARCH CONFIGURATION runestone (COPY = simple);
|
26
|
+
ALTER TEXT SEARCH CONFIGURATION runestone
|
26
27
|
ALTER MAPPING FOR hword, hword_part, word
|
27
28
|
WITH unaccent, simple;
|
28
29
|
SQL
|
data/lib/runestone.rb
CHANGED
@@ -6,7 +6,8 @@ module Runestone
|
|
6
6
|
autoload :WebSearch, "#{File.dirname(__FILE__)}/runestone/web_search"
|
7
7
|
autoload :IndexingJob, "#{File.dirname(__FILE__)}/runestone/indexing_job"
|
8
8
|
|
9
|
-
mattr_accessor :dictionary, default: :
|
9
|
+
mattr_accessor :dictionary, default: :runestone
|
10
|
+
mattr_accessor :normalization, default: 16
|
10
11
|
mattr_accessor :runner, default: :inline
|
11
12
|
mattr_accessor :job_queue, default: :runestone_indexing
|
12
13
|
mattr_accessor :typo_tolerances, default: { 1 => 4..7, 2 => 8.. }
|
@@ -15,6 +16,37 @@ module Runestone
|
|
15
16
|
{ }
|
16
17
|
end
|
17
18
|
|
19
|
+
DEFAULT_APPROXIMATIONS = {
|
20
|
+
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
|
21
|
+
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
|
22
|
+
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
|
23
|
+
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
|
24
|
+
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
|
25
|
+
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
|
26
|
+
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
|
27
|
+
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
|
28
|
+
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
|
29
|
+
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
|
30
|
+
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
|
31
|
+
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
|
32
|
+
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
|
33
|
+
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
|
34
|
+
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
|
35
|
+
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
|
36
|
+
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
|
37
|
+
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
|
38
|
+
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
|
39
|
+
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
|
40
|
+
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
|
41
|
+
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
|
42
|
+
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
|
43
|
+
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
|
44
|
+
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
|
45
|
+
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
|
46
|
+
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
|
47
|
+
"Ž"=>"Z", "ž"=>"z"
|
48
|
+
}.freeze
|
49
|
+
|
18
50
|
def self.normalize(string)
|
19
51
|
string = string.downcase
|
20
52
|
string = string.unicode_normalize!
|
@@ -22,7 +54,17 @@ module Runestone
|
|
22
54
|
rescue Encoding::CompatibilityError
|
23
55
|
string
|
24
56
|
end
|
25
|
-
|
57
|
+
|
58
|
+
def self.normalize!(string)
|
59
|
+
string.downcase!
|
60
|
+
string.unicode_normalize!
|
61
|
+
rescue Encoding::CompatibilityError
|
62
|
+
end
|
63
|
+
|
64
|
+
def transliterate(string)
|
65
|
+
string.gsub(/[^\x00-\x7f]/u) { |char| approximations[char] || char }
|
66
|
+
end
|
67
|
+
|
26
68
|
def self.add_synonyms(dictionary)
|
27
69
|
dictionary.each do |k, v|
|
28
70
|
add_synonym(k, *v)
|
@@ -53,7 +95,7 @@ module Runestone
|
|
53
95
|
syn[last].uniq!
|
54
96
|
end
|
55
97
|
|
56
|
-
def search(query, dictionary: nil, prefix: :last)
|
98
|
+
def search(query, dictionary: nil, prefix: :last, normalization: nil)
|
57
99
|
exact_search = Runestone::WebSearch.parse(query, prefix: prefix)
|
58
100
|
typo_search = exact_search.typos
|
59
101
|
syn_search = typo_search.synonymize
|
@@ -65,11 +107,11 @@ module Runestone
|
|
65
107
|
q = if select_values.empty?
|
66
108
|
select(
|
67
109
|
klass.arel_table[Arel.star],
|
68
|
-
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
110
|
+
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary, normalization: normalization), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
69
111
|
)
|
70
112
|
else
|
71
113
|
select(
|
72
|
-
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
114
|
+
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary, normalization: normalization), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
73
115
|
)
|
74
116
|
end
|
75
117
|
|
@@ -39,14 +39,17 @@ module Runestone::ActiveRecord
|
|
39
39
|
)
|
40
40
|
end
|
41
41
|
|
42
|
-
def ts_rank_cd(vector, query, dictionary: nil)
|
42
|
+
def ts_rank_cd(vector, query, dictionary: nil, normalization: nil)
|
43
|
+
normalization ||= Runestone.normalization
|
44
|
+
|
43
45
|
Arel::Nodes::TSRankCD.new(
|
44
46
|
ts_vector(vector, dictionary: dictionary),
|
45
|
-
ts_query(query, dictionary: dictionary)
|
47
|
+
ts_query(query, dictionary: dictionary),
|
48
|
+
normalization
|
46
49
|
)
|
47
50
|
end
|
48
51
|
|
49
|
-
def search(query, dictionary: nil, prefix: nil)
|
52
|
+
def search(query, dictionary: nil, prefix: nil, normalization: nil)
|
50
53
|
exact_search = Runestone::WebSearch.parse(query, prefix: prefix)
|
51
54
|
typo_search = exact_search.typos
|
52
55
|
syn_search = typo_search.synonymize
|
@@ -58,11 +61,11 @@ module Runestone::ActiveRecord
|
|
58
61
|
q = if select_values.empty?
|
59
62
|
select(
|
60
63
|
klass.arel_table[Arel.star],
|
61
|
-
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
64
|
+
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary, normalization: normalization), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
62
65
|
)
|
63
66
|
else
|
64
67
|
select(
|
65
|
-
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
68
|
+
*tsqueries.each_with_index.map { |q, i| Arel::Nodes::As.new(ts_rank_cd(:vector, q, dictionary: dictionary, normalization: normalization), Arel::Nodes::SqlLiteral.new("rank#{i}")) }
|
66
69
|
)
|
67
70
|
end
|
68
71
|
|
data/lib/runestone/corpus.rb
CHANGED
@@ -6,21 +6,22 @@ module Runestone::Corpus
|
|
6
6
|
conn = Runestone::Model.connection
|
7
7
|
conn.execute(<<-SQL)
|
8
8
|
INSERT INTO runestone_corpus ( word )
|
9
|
-
VALUES (#{words.map { |w| conn.quote(w
|
9
|
+
VALUES (#{words.map { |w| conn.quote(Runestone.normalize(w)) }.join('),(')})
|
10
10
|
ON CONFLICT DO NOTHING
|
11
11
|
SQL
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.similar_words(*words)
|
15
15
|
lut = {}
|
16
|
+
conn = Runestone::Model.connection
|
16
17
|
words = words.inject([]) do |ws, w|
|
17
18
|
tt = typo_tolerance(w)
|
18
|
-
ws << "#{
|
19
|
+
ws << "#{conn.quote(w)}, #{conn.quote(w.downcase)}, #{tt}" if tt > 0
|
19
20
|
ws
|
20
21
|
end
|
21
22
|
return lut if words.size == 0
|
22
23
|
|
23
|
-
result =
|
24
|
+
result = conn.execute(<<-SQL)
|
24
25
|
WITH tokens (token, token_downcased, typo_tolerance) AS (VALUES (#{words.join('), (')}))
|
25
26
|
SELECT token, word, levenshtein(runestone_corpus.word, tokens.token_downcased)
|
26
27
|
FROM tokens
|
data/lib/runestone/version.rb
CHANGED
data/lib/runestone/web_search.rb
CHANGED
@@ -26,11 +26,7 @@ class Runestone::WebSearch
|
|
26
26
|
# prefix options: :all, :last, :none (default: :last)
|
27
27
|
def self.parse(query, prefix: :last)
|
28
28
|
prefix ||= :last
|
29
|
-
|
30
|
-
query.unicode_normalize!
|
31
|
-
rescue Encoding::CompatibilityError
|
32
|
-
end
|
33
|
-
query.downcase!
|
29
|
+
Runestone.normalize!(query)
|
34
30
|
|
35
31
|
q = []
|
36
32
|
stack = []
|
data/runestone.gemspec
CHANGED
@@ -27,6 +27,6 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_development_dependency 'activejob', '>= 6.0'
|
28
28
|
|
29
29
|
# Runtime
|
30
|
-
s.add_runtime_dependency 'arel-extensions', '>= 6.0'
|
30
|
+
s.add_runtime_dependency 'arel-extensions', '>= 6.0.0.9'
|
31
31
|
s.add_runtime_dependency 'activerecord', '>= 6.0'
|
32
32
|
end
|
data/test/corpus_test.rb
CHANGED
@@ -38,5 +38,24 @@ class CorpusTest < ActiveSupport::TestCase
|
|
38
38
|
Runestone::Corpus.similar_words('Allee')
|
39
39
|
)
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
|
+
test 'adding words to corpus normalizes them' do
|
43
|
+
Runestone::Corpus.add("all\u00e9e")
|
44
|
+
assert_equal(
|
45
|
+
{
|
46
|
+
"Allee" => ["all\u00e9e"]
|
47
|
+
},
|
48
|
+
Runestone::Corpus.similar_words('Allee')
|
49
|
+
)
|
50
|
+
|
51
|
+
Runestone::Model.connection.execute('DELETE FROM runestone_corpus')
|
52
|
+
Runestone::Corpus.add("all\u0065\u0301e")
|
53
|
+
assert_equal(
|
54
|
+
{
|
55
|
+
"Allee" => ["all\u00e9e"]
|
56
|
+
},
|
57
|
+
Runestone::Corpus.similar_words('Allee')
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
42
61
|
end
|
data/test/database.rb
CHANGED
@@ -56,8 +56,8 @@ ActiveRecord::Migration.suppress_messages do
|
|
56
56
|
CREATE INDEX runestone_corpus_trgm_idx ON runestone_corpus USING GIN (word gin_trgm_ops);
|
57
57
|
|
58
58
|
|
59
|
-
CREATE TEXT SEARCH CONFIGURATION
|
60
|
-
ALTER TEXT SEARCH CONFIGURATION
|
59
|
+
CREATE TEXT SEARCH CONFIGURATION runestone (COPY = simple);
|
60
|
+
ALTER TEXT SEARCH CONFIGURATION runestone
|
61
61
|
ALTER MAPPING FOR hword, hword_part, word
|
62
62
|
WITH unaccent, simple;
|
63
63
|
SQL
|
data/test/delayed_index_test.rb
CHANGED
@@ -2,9 +2,9 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class DelayedIndexingTest < ActiveSupport::TestCase
|
4
4
|
|
5
|
-
test '
|
5
|
+
test 'runestone index' do
|
6
6
|
region = assert_no_difference 'Runestone::Model.count' do
|
7
|
-
assert_no_sql(/setweight\(to_tsvector\('
|
7
|
+
assert_no_sql(/setweight\(to_tsvector\('runestone', 'address name'\), 'A'\)/) do
|
8
8
|
Region.create(name: 'Region name')
|
9
9
|
end
|
10
10
|
end
|
data/test/highlight_test.rb
CHANGED
@@ -9,6 +9,7 @@ class HighlightTest < ActiveSupport::TestCase
|
|
9
9
|
tsmodels = Runestone::Model.search('state')
|
10
10
|
Runestone::Model.highlight(tsmodels, 'state')
|
11
11
|
assert_equal([
|
12
|
+
{ "name"=>"address of <b>state</b> duo" },
|
12
13
|
{
|
13
14
|
"name"=>"Big <b>state</b> building",
|
14
15
|
"addresses"=> [{"name"=>"address of <b>state</b> duo"}]
|
@@ -17,10 +18,31 @@ class HighlightTest < ActiveSupport::TestCase
|
|
17
18
|
"name"=>"Empire <b>state</b> building",
|
18
19
|
"addresses"=> [{"name"=>"address uno"}]
|
19
20
|
},
|
21
|
+
|
22
|
+
], tsmodels.map(&:highlights))
|
23
|
+
end
|
24
|
+
|
25
|
+
test '::highlights(query) with an accent in the result' do
|
26
|
+
Property.create(name: 'Émpire state building', addresses: [Address.create(name: 'address uno')])
|
27
|
+
Property.create(name: 'Big state building', addresses: [Address.create(name: 'addréss of state duo')])
|
28
|
+
|
29
|
+
tsmodels = Runestone::Model.search('empire')
|
30
|
+
Runestone::Model.highlight(tsmodels, 'empire')
|
31
|
+
assert_equal([
|
20
32
|
{
|
21
|
-
"name"=>"
|
33
|
+
"name"=>"<b>Émpire</b> state building",
|
34
|
+
"addresses"=>[ {"name"=>"address uno"} ]
|
22
35
|
}
|
23
36
|
], tsmodels.map(&:highlights))
|
37
|
+
|
38
|
+
tsmodels = Runestone::Model.search('address')
|
39
|
+
Runestone::Model.highlight(tsmodels, 'address')
|
40
|
+
assert_equal([
|
41
|
+
{"name"=>"<b>address</b> uno"},
|
42
|
+
{"name"=>"<b>addréss</b> of state duo"},
|
43
|
+
{"addresses"=>[{"name"=>"<b>address</b> uno"}], "name"=>"Émpire state building"},
|
44
|
+
{"addresses"=>[{"name"=>"<b>addréss</b> of state duo"}], "name"=>"Big state building"}
|
45
|
+
], tsmodels.map(&:highlights))
|
24
46
|
end
|
25
47
|
|
26
48
|
end
|
data/test/indexing_test.rb
CHANGED
@@ -2,9 +2,9 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class IndexingTest < ActiveSupport::TestCase
|
4
4
|
|
5
|
-
test '
|
5
|
+
test 'runestone index' do
|
6
6
|
address = assert_difference 'Runestone::Model.count', 1 do
|
7
|
-
assert_sql(/setweight\(to_tsvector\('
|
7
|
+
assert_sql(/setweight\(to_tsvector\('runestone', 'address name'\), 'A'\)/) do
|
8
8
|
Address.create(name: 'Address name')
|
9
9
|
end
|
10
10
|
end
|
data/test/multi_index_test.rb
CHANGED
@@ -33,16 +33,16 @@ class MultiIndexTest < ActiveSupport::TestCase
|
|
33
33
|
|
34
34
|
query = Runestone::Model.search('empire')
|
35
35
|
assert_sql(<<~SQL, query.to_sql)
|
36
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
36
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'empire:*'), 16) AS rank0
|
37
37
|
FROM "runestones"
|
38
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
38
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'empire:*')
|
39
39
|
ORDER BY rank0 DESC
|
40
40
|
SQL
|
41
41
|
|
42
42
|
query = Runestone::Model.search('empire', dictionary: 'english')
|
43
43
|
assert_sql(<<~SQL, query.to_sql)
|
44
44
|
SELECT
|
45
|
-
"runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('english', 'empire:*')) AS rank0
|
45
|
+
"runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('english', 'empire:*'), 16) AS rank0
|
46
46
|
FROM "runestones"
|
47
47
|
WHERE "runestones"."vector" @@ to_tsquery('english', 'empire:*')
|
48
48
|
AND "runestones"."dictionary" = 'english'
|
@@ -51,7 +51,7 @@ class MultiIndexTest < ActiveSupport::TestCase
|
|
51
51
|
|
52
52
|
query = Runestone::Model.search('Эмпайр', dictionary: 'russian')
|
53
53
|
assert_sql(<<~SQL, query.to_sql)
|
54
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('russian', 'эмпайр:*')) AS rank0
|
54
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('russian', 'эмпайр:*'), 16) AS rank0
|
55
55
|
FROM "runestones"
|
56
56
|
WHERE "runestones"."vector" @@ to_tsquery('russian', 'эмпайр:*')
|
57
57
|
AND "runestones"."dictionary" = 'russian'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OrderTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test 'smaller documents come first' do
|
6
|
+
a2 = Address.create(name: 'a big square')
|
7
|
+
a1 = Address.create(name: 'Square')
|
8
|
+
|
9
|
+
query = Runestone::Model.search('square')
|
10
|
+
assert_sql(<<~SQL, query.to_sql)
|
11
|
+
SELECT
|
12
|
+
"runestones".*,
|
13
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'square:*'), 16) AS rank0
|
14
|
+
FROM "runestones"
|
15
|
+
WHERE
|
16
|
+
"runestones"."vector" @@ to_tsquery('runestone', 'square:*')
|
17
|
+
ORDER BY rank0 DESC
|
18
|
+
SQL
|
19
|
+
|
20
|
+
assert_equal(query.map(&:record).map(&:name), [
|
21
|
+
'Square',
|
22
|
+
'a big square'
|
23
|
+
])
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
end
|
data/test/query_test.rb
CHANGED
@@ -6,9 +6,9 @@ class QueryTest < ActiveSupport::TestCase
|
|
6
6
|
query = Runestone::Model.search('seaerch for this')
|
7
7
|
|
8
8
|
assert_sql(<<~SQL, query.to_sql)
|
9
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
9
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & this:*'), 16) AS rank0
|
10
10
|
FROM "runestones"
|
11
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
11
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'seaerch & for & this:*')
|
12
12
|
ORDER BY rank0 DESC
|
13
13
|
SQL
|
14
14
|
end
|
@@ -17,9 +17,9 @@ class QueryTest < ActiveSupport::TestCase
|
|
17
17
|
query = Runestone::Model.search("the search for \u0065\u0301")
|
18
18
|
|
19
19
|
assert_sql(<<~SQL, query.to_sql)
|
20
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
20
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'the & search & for & \u00e9:*'), 16) AS rank0
|
21
21
|
FROM "runestones"
|
22
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
22
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'the & search & for & \u00e9:*')
|
23
23
|
ORDER BY rank0 DESC
|
24
24
|
SQL
|
25
25
|
end
|
@@ -27,17 +27,17 @@ class QueryTest < ActiveSupport::TestCase
|
|
27
27
|
test "::search(query with ')" do
|
28
28
|
query = Runestone::Model.search("seaerch for ' this")
|
29
29
|
assert_sql(<<~SQL, query.to_sql)
|
30
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
30
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & this:*'), 16) AS rank0
|
31
31
|
FROM "runestones"
|
32
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
32
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'seaerch & for & this:*')
|
33
33
|
ORDER BY rank0 DESC
|
34
34
|
SQL
|
35
35
|
|
36
36
|
query = Runestone::Model.search("seaerch for james' map")
|
37
37
|
assert_sql(<<~SQL, query.to_sql)
|
38
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
38
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & james'' & map:*'), 16) AS rank0
|
39
39
|
FROM "runestones"
|
40
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
40
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'seaerch & for & james'' & map:*')
|
41
41
|
ORDER BY rank0 DESC
|
42
42
|
SQL
|
43
43
|
end
|
@@ -46,9 +46,9 @@ class QueryTest < ActiveSupport::TestCase
|
|
46
46
|
query = Runestone::Model.search('seaerch for this', prefix: :all)
|
47
47
|
|
48
48
|
assert_sql(<<~SQL, query.to_sql)
|
49
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
49
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch:* & for:* & this:*'), 16) AS rank0
|
50
50
|
FROM "runestones"
|
51
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
51
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'seaerch:* & for:* & this:*')
|
52
52
|
ORDER BY rank0 DESC
|
53
53
|
SQL
|
54
54
|
end
|
@@ -57,9 +57,9 @@ class QueryTest < ActiveSupport::TestCase
|
|
57
57
|
query = Runestone::Model.search('seaerch for this').limit(10)
|
58
58
|
|
59
59
|
assert_sql(<<~SQL, query.to_sql)
|
60
|
-
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
60
|
+
SELECT "runestones".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & this:*'), 16) AS rank0
|
61
61
|
FROM "runestones"
|
62
|
-
WHERE "runestones"."vector" @@ to_tsquery('
|
62
|
+
WHERE "runestones"."vector" @@ to_tsquery('runestone', 'seaerch & for & this:*')
|
63
63
|
ORDER BY rank0 DESC
|
64
64
|
LIMIT 10
|
65
65
|
SQL
|
@@ -70,13 +70,13 @@ class QueryTest < ActiveSupport::TestCase
|
|
70
70
|
|
71
71
|
assert_sql(<<~SQL, query.to_sql)
|
72
72
|
SELECT
|
73
|
-
"properties".*, ts_rank_cd("runestones"."vector", to_tsquery('
|
73
|
+
"properties".*, ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & this:*'), 16) AS rank0
|
74
74
|
FROM "properties"
|
75
75
|
INNER JOIN "runestones"
|
76
|
-
ON "runestones"."
|
77
|
-
AND "runestones"."
|
76
|
+
ON "runestones"."record_type" = 'Property'
|
77
|
+
AND "runestones"."record_id" = "properties"."id"
|
78
78
|
WHERE
|
79
|
-
"runestones"."vector" @@ to_tsquery('
|
79
|
+
"runestones"."vector" @@ to_tsquery('runestone', 'seaerch & for & this:*')
|
80
80
|
ORDER BY rank0 DESC
|
81
81
|
SQL
|
82
82
|
end
|
@@ -88,14 +88,14 @@ class QueryTest < ActiveSupport::TestCase
|
|
88
88
|
assert_sql(<<~SQL, query.to_sql)
|
89
89
|
SELECT
|
90
90
|
"properties".*,
|
91
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
92
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
91
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'seaerch & for & this:*'), 16) AS rank0,
|
92
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(seaerch | search) & for & this:*'), 16) AS rank1
|
93
93
|
FROM "properties"
|
94
|
-
INNER JOIN "runestones"
|
95
|
-
"runestones"."
|
96
|
-
AND "runestones"."
|
94
|
+
INNER JOIN "runestones"
|
95
|
+
ON "runestones"."record_type" = 'Property'
|
96
|
+
AND "runestones"."record_id" = "properties"."id"
|
97
97
|
WHERE
|
98
|
-
"runestones"."vector" @@ to_tsquery('
|
98
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(seaerch | search) & for & this:*')
|
99
99
|
ORDER BY
|
100
100
|
rank0 DESC,
|
101
101
|
rank1 DESC
|
@@ -113,12 +113,12 @@ class QueryTest < ActiveSupport::TestCase
|
|
113
113
|
assert_sql(<<~SQL, Runestone::Model.search('avenue').to_sql)
|
114
114
|
SELECT
|
115
115
|
"runestones".*,
|
116
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
117
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
118
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
116
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'avenue:*'), 16) AS rank0,
|
117
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(avenue:* | aveneue)'), 16) AS rank1,
|
118
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '((avenue:* | aveneue) | av | ave | avn | aven | avenu | avnue)'), 16) AS rank2
|
119
119
|
FROM "runestones"
|
120
120
|
WHERE
|
121
|
-
"runestones"."vector" @@ to_tsquery('
|
121
|
+
"runestones"."vector" @@ to_tsquery('runestone', '((avenue:* | aveneue) | av | ave | avn | aven | avenu | avnue)')
|
122
122
|
ORDER BY
|
123
123
|
rank0 DESC,
|
124
124
|
rank1 DESC,
|
data/test/synonym_test.rb
CHANGED
@@ -13,11 +13,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
13
13
|
assert_sql(<<~SQL, query.to_sql)
|
14
14
|
SELECT
|
15
15
|
"runestones".*,
|
16
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
17
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
16
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '17 & spruce:*'), 16) AS rank0,
|
17
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)'), 16) AS rank1
|
18
18
|
FROM "runestones"
|
19
19
|
WHERE
|
20
|
-
"runestones"."vector" @@ to_tsquery('
|
20
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)')
|
21
21
|
ORDER BY rank0 DESC, rank1 DESC
|
22
22
|
SQL
|
23
23
|
end
|
@@ -40,11 +40,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
40
40
|
assert_sql(<<~SQL, query.to_sql)
|
41
41
|
SELECT
|
42
42
|
"runestones".*,
|
43
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
44
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
43
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '17 & spruce:*'), 16) AS rank0,
|
44
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)'), 16) AS rank1
|
45
45
|
FROM "runestones"
|
46
46
|
WHERE
|
47
|
-
"runestones"."vector" @@ to_tsquery('
|
47
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)')
|
48
48
|
ORDER BY rank0 DESC, rank1 DESC
|
49
49
|
SQL
|
50
50
|
end
|
@@ -59,11 +59,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
59
59
|
assert_sql(<<~SQL, query.to_sql)
|
60
60
|
SELECT
|
61
61
|
"runestones".*,
|
62
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
63
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
62
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '17 & !spruce'), 16) AS rank0,
|
63
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & !spruce'), 16) AS rank1
|
64
64
|
FROM "runestones"
|
65
65
|
WHERE
|
66
|
-
"runestones"."vector" @@ to_tsquery('
|
66
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & !spruce')
|
67
67
|
ORDER BY rank0 DESC, rank1 DESC
|
68
68
|
SQL
|
69
69
|
end
|
@@ -78,11 +78,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
78
78
|
assert_sql(<<~SQL, query.to_sql)
|
79
79
|
SELECT
|
80
80
|
"runestones".*,
|
81
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
82
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
81
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '17 & spruce'), 16) AS rank0,
|
82
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & spruce'), 16) AS rank1
|
83
83
|
FROM "runestones"
|
84
84
|
WHERE
|
85
|
-
"runestones"."vector" @@ to_tsquery('
|
85
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & spruce')
|
86
86
|
ORDER BY rank0 DESC, rank1 DESC
|
87
87
|
SQL
|
88
88
|
end
|
@@ -98,11 +98,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
98
98
|
assert_sql(<<~SQL, query.to_sql)
|
99
99
|
SELECT
|
100
100
|
"runestones".*,
|
101
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
102
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
101
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '17 & spruce:*'), 16) AS rank0,
|
102
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)'), 16) AS rank1
|
103
103
|
FROM "runestones"
|
104
104
|
WHERE
|
105
|
-
"runestones"."vector" @@ to_tsquery('
|
105
|
+
"runestones"."vector" @@ to_tsquery('runestone', '(17 | 17th | seventeen | seventeenth) & (spruce:* | pine)')
|
106
106
|
ORDER BY rank0 DESC, rank1 DESC
|
107
107
|
SQL
|
108
108
|
end
|
@@ -116,11 +116,11 @@ class SynonymTest < ActiveSupport::TestCase
|
|
116
116
|
assert_sql(<<~SQL, query.to_sql)
|
117
117
|
SELECT
|
118
118
|
"runestones".*,
|
119
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
120
|
-
ts_rank_cd("runestones"."vector", to_tsquery('
|
119
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', 'one & hundred & spruce:*'), 16) AS rank0,
|
120
|
+
ts_rank_cd("runestones"."vector", to_tsquery('runestone', '((one & hundred | 100) & spruce:* | (one & hundred | one hundy) & spruce:*)'), 16) AS rank1
|
121
121
|
FROM "runestones"
|
122
122
|
WHERE
|
123
|
-
"runestones"."vector" @@ to_tsquery('
|
123
|
+
"runestones"."vector" @@ to_tsquery('runestone', '((one & hundred | 100) & spruce:* | (one & hundred | one hundy) & spruce:*)')
|
124
124
|
ORDER BY rank0 DESC, rank1 DESC
|
125
125
|
SQL
|
126
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: runestone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
145
|
+
version: 6.0.0.9
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
152
|
+
version: 6.0.0.9
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: activerecord
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -177,7 +177,7 @@ files:
|
|
177
177
|
- LICENSE
|
178
178
|
- README.md
|
179
179
|
- Rakefile
|
180
|
-
- db/migrate/
|
180
|
+
- db/migrate/20181101150207_create_runestone_tables.rb
|
181
181
|
- lib/runestone.rb
|
182
182
|
- lib/runestone/active_record/base_methods.rb
|
183
183
|
- lib/runestone/active_record/relation_methods.rb
|
@@ -200,6 +200,7 @@ files:
|
|
200
200
|
- test/highlight_test.rb
|
201
201
|
- test/indexing_test.rb
|
202
202
|
- test/multi_index_test.rb
|
203
|
+
- test/ordering_test.rb
|
203
204
|
- test/query_test.rb
|
204
205
|
- test/synonym_test.rb
|
205
206
|
- test/test_helper.rb
|
@@ -207,7 +208,7 @@ homepage: https://github.com/malomalo/runestone
|
|
207
208
|
licenses:
|
208
209
|
- MIT
|
209
210
|
metadata: {}
|
210
|
-
post_install_message:
|
211
|
+
post_install_message:
|
211
212
|
rdoc_options: []
|
212
213
|
require_paths:
|
213
214
|
- lib
|
@@ -222,8 +223,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
223
|
- !ruby/object:Gem::Version
|
223
224
|
version: '0'
|
224
225
|
requirements: []
|
225
|
-
rubygems_version: 3.
|
226
|
-
signing_key:
|
226
|
+
rubygems_version: 3.1.4
|
227
|
+
signing_key:
|
227
228
|
specification_version: 4
|
228
229
|
summary: Full Text Search for Active Record / Rails
|
229
230
|
test_files:
|
@@ -234,6 +235,7 @@ test_files:
|
|
234
235
|
- test/highlight_test.rb
|
235
236
|
- test/indexing_test.rb
|
236
237
|
- test/multi_index_test.rb
|
238
|
+
- test/ordering_test.rb
|
237
239
|
- test/query_test.rb
|
238
240
|
- test/synonym_test.rb
|
239
241
|
- test/test_helper.rb
|