redisearch-rb 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 223cca42fa38796e6abd0b5780b589dc2d0536bb
4
- data.tar.gz: ee5aa7e8e327dd933cf8079d02fdbca425abb3aa
3
+ metadata.gz: 38fdd275f958d4e5df2905efaca54d8d50d98c5b
4
+ data.tar.gz: f96e95211a060b7ba444be7d6edc8d264fd77f03
5
5
  SHA512:
6
- metadata.gz: 003b2d21b74b25530e9126028e416b003b690a05d29c7e9cee79abb46e5874ca1c9bbb2f4ae532f766897834f993729f3530785220ca66c1426385c8145b3619
7
- data.tar.gz: 5a9856724a08319139ad6ad9af814f77eca878a247db345248b59ec051afe2016810f5c58f8bce718ce6db62b666493f130eaca390dcbee22730f4c596bb849e
6
+ metadata.gz: a1e81b076d263436bbbf1586df2ad7397be0804bce9c0885f79e54d0cc5b43db735cf6e880656aff995d0bef0ae09df1d8cf8b28cd72c34014cc323dc9ccb76c
7
+ data.tar.gz: 5cc4a44605e1e420319d5daa68482e4c684321bdbcb2bb2af1401509b964b952781c4c2cd46fe285e9ccfebce7c2fba73b150aa7510823716e3d95d3b3b80de4
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  .idea/
12
12
  *.gem
13
13
  *.rdb
14
+ *.so
data/.travis.yml CHANGED
@@ -1,14 +1,18 @@
1
- sudo: false
1
+ sudo: required
2
+ dist: trusty
2
3
  language: ruby
3
4
  rvm:
4
- - 2.4.0
5
+ - 2.4.1
5
6
  before_install:
6
7
  - gem install bundler -v 1.14.6
7
- - git clone --depth 1 https://github.com/antirez/redis.git
8
+ - git clone --depth 10 https://github.com/antirez/redis.git
8
9
  - cd redis
10
+ - git fetch origin 4.0.1
11
+ - git checkout ae43e164d4b09d7753e27bb7cba6f7f4f85326b3
9
12
  - make
10
13
  - cd ..
11
14
  - git clone --depth 1 https://github.com/RedisLabsModules/RediSearch.git
12
15
  - cd RediSearch/src
13
16
  - make all
17
+ - ls -la *.so
14
18
  - cd ..
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ruby '2.4.0'
3
+ ruby '2.4.1'
4
4
 
5
5
  gem 'redis', '~> 4.0.0.rc1'
6
6
 
data/TODO.md ADDED
@@ -0,0 +1,16 @@
1
+ ## TODO
2
+
3
+ - [ x ] Add options hash in `FT.CREATE` (`[NOOFFSETS] [NOFIELDS] [NOSCOREIDX]`) / Allow _raw_ options block
4
+
5
+ - [ x ] Add options hash in `FT.ADD` (`[NOSAVE] [REPLACE] [LANGUAGE {language}] [PAYLOAD {payload}]`) / Allow _raw_ options block
6
+
7
+ - [ x ] Add more options to options hash in `FT.SEARCH` / Allow _raw_ options block
8
+
9
+ - [ ] Add function `FT.DEL`
10
+
11
+ - [ ] Add function `FT.OPTIMIZE`
12
+
13
+ - [ ] Add autocomplete functions: `FT.SUGGADD`, `FT.SUGGET`, `FT.SUGGDEL`, `FT.SUGGLEN`
14
+
15
+ - [ ] Add function `FT.EXPLAIN`
16
+
data/lib/redisearch.rb CHANGED
@@ -5,6 +5,22 @@ require 'redis'
5
5
  # http://redisearch.io/
6
6
  #
7
7
  class RediSearch
8
+ DEFAULT_WEIGHT = '1.0'
9
+
10
+ # Supported options
11
+ # Flags options can be only true or false,
12
+ #
13
+ # { verbatim: true, withscores: true, withsortkey: false }
14
+
15
+ CREATE_OPTIONS_FLAGS = [:nooffsets, :nofreqs, :noscoreidx, :nofields]
16
+ ADD_OPTIONS_FLAGS = [:nosave, :replace]
17
+ SEARCH_OPTIONS_FLAGS = [:nocontent, :verbatim, :nostopwords, :withscores, :withsortkeys]
18
+
19
+ # Params options need an array with the values for the option
20
+ # { limit: ['0', '50'], sortby: ['year', 'desc'], return: ['2', 'title', 'year'] }
21
+ ADD_OPTIONS_PARAMS = [:language]
22
+ SEARCH_OPTIONS_PARAMS = [:filter, :return, :infields, :inkeys, :slop, :scorer, :sortby, :limit]
23
+
8
24
  # Create RediSearch client instance
9
25
  #
10
26
  # @param [String] idx_name name of the index
@@ -29,8 +45,8 @@ class RediSearch
29
45
  # See http://redisearch.io/Commands/#ftcreate
30
46
  #
31
47
  # @return [String] "OK" on success
32
- def create_index(schema)
33
- call(ft_create(schema))
48
+ def create_index(schema, opts = {})
49
+ call(ft_create(schema, opts))
34
50
  end
35
51
 
36
52
  # Drop all the keys in the current index
@@ -45,7 +61,7 @@ class RediSearch
45
61
  #
46
62
  # @param [String] doc_id id assigned to the document
47
63
  # @param [Array] fields name-value pairs to be indexed
48
- #
64
+ # @param [Hash] opts optional parameters
49
65
  # Example:
50
66
  #
51
67
  # redisearch = RediSearch.new('my_idx')
@@ -53,8 +69,8 @@ class RediSearch
53
69
  #
54
70
  # See http://redisearch.io/Commands/#ftadd
55
71
  # @return [String] "OK" on success
56
- def add_doc(doc_id, fields)
57
- call(ft_add(doc_id, fields))
72
+ def add_doc(doc_id, fields, opts = {})
73
+ call(ft_add(doc_id, fields, opts))
58
74
  end
59
75
 
60
76
  # Add a set of docs to the index. Uses redis `multi` to make a single bulk insert.
@@ -70,8 +86,8 @@ class RediSearch
70
86
  #
71
87
  # See http://redisearch.io/Commands/#ftadd
72
88
  # @return [String] "OK" on success
73
- def add_docs(docs)
74
- multi { docs.each { |doc_id, fields| @redis.call(ft_add(doc_id, fields)) } }
89
+ def add_docs(docs, opts = {})
90
+ multi { docs.each { |doc_id, fields| @redis.call(ft_add(doc_id, fields, opts)) } }
75
91
  end
76
92
 
77
93
  # Search the index with the given `query`
@@ -80,7 +96,7 @@ class RediSearch
80
96
  #
81
97
  #@return [Array] documents matching the query
82
98
  def search(query, opts = {})
83
- results_to_hash(call(ft_search(query, opts)))
99
+ results_to_hash(call(ft_search(query, opts)), opts)
84
100
  end
85
101
 
86
102
  # Return information and statistics on the index.
@@ -104,8 +120,8 @@ class RediSearch
104
120
  @redis.call(ft_add(doc_id, fields))
105
121
  end
106
122
 
107
- def ft_create(schema)
108
- ['FT.CREATE', @idx_name , 'SCHEMA', *schema]
123
+ def ft_create(schema, opts)
124
+ ['FT.CREATE', @idx_name , *create_options(opts), 'SCHEMA', *schema]
109
125
  end
110
126
 
111
127
  def ft_drop
@@ -116,28 +132,39 @@ class RediSearch
116
132
  ['FT.INFO', @idx_name]
117
133
  end
118
134
 
119
- def ft_add(doc_id, fields)
120
- ['FT.ADD', @idx_name , doc_id, '1.0', 'REPLACE', 'FIELDS', *fields]
135
+ def ft_add(doc_id, fields, opts = {}, weight = nil)
136
+ ['FT.ADD', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *add_options(opts), 'FIELDS', *fields]
121
137
  end
122
138
 
123
139
  def ft_search(query, opts)
124
- command = ['FT.SEARCH', @idx_name, *query]
125
- command << 'NOSTOPWORDS' if opts[:nostopwords]
126
- command << 'VERBATIM' if opts[:verbatim]
127
- if opts[:offset] || opts[:limit]
128
- limit = opts[:limit].to_i > 0 ? opts[:limit].to_i : -1
129
- command << ["LIMIT", opts[:offset], limit]
130
- end
131
- command << 'WITHSCORES' if opts[:withscores]
132
- command << ["SCORER", opts[:scorer]] unless opts[:scorer].to_s.empty?
133
- command << ["SLOP", opts[:slop]] if opts[:slop].to_i > 0
134
- command.flatten
140
+ ['FT.SEARCH', @idx_name, *query, *search_options(opts)].flatten
141
+ end
142
+
143
+ def create_options(opts = {})
144
+ build_options(opts, CREATE_OPTIONS_FLAGS, [])
145
+ end
146
+
147
+ def add_options(opts = {})
148
+ build_options(opts, ADD_OPTIONS_FLAGS, ADD_OPTIONS_PARAMS)
149
+ end
150
+
151
+ def search_options(opts = {})
152
+ build_options(opts, SEARCH_OPTIONS_FLAGS, SEARCH_OPTIONS_PARAMS)
153
+ end
154
+
155
+ def build_options(opts, flags_keys, params_keys)
156
+ flags_keys.map do |key|
157
+ key.to_s.upcase if opts[key]
158
+ end.compact +
159
+ params_keys.map do |key|
160
+ [key.to_s.upcase, *opts[key]] unless opts[key].nil?
161
+ end.compact
135
162
  end
136
163
 
137
- def results_to_hash(results)
164
+ def results_to_hash(results, opts = {})
138
165
  return {} if results.nil? || results[0] == 0
139
166
  results.shift
140
- offset = results.size % 3 == 0 ? 1 : 0
167
+ offset = opts[:withscores] ? 1 : 0
141
168
  rows_per_doc = 2 + offset
142
169
  nr_of_docs = results.size / (2 + offset)
143
170
  (0..nr_of_docs-1).map do |n|
@@ -1,3 +1,3 @@
1
1
  class RediSearch
2
- VERSION = '0.1.5'
2
+ VERSION = '0.1.6'
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.email = ["vruizext@gmail.com"]
9
9
 
10
10
  spec.summary = "A simple Ruby client library for RediSearch"
11
- spec.description = "A simple Ruby client library for RediSearc. RediSearch is a Full-Text search index over Redis,
12
- developed by RedisLabs. http://redisearch.io/"
11
+ spec.description = "A simple Ruby client library for RediSearch.
12
+ RediSearch is a Redis powered search engine, developed by RedisLabs. http://redisearch.io/"
13
13
  spec.homepage = "https://github.com/vruizext/redisearch-rb"
14
14
  spec.license = "MIT"
15
15
 
@@ -2,15 +2,15 @@ require 'test_helper'
2
2
 
3
3
  class RediSearchTest < Minitest::Test
4
4
  def setup
5
- @redis_server = RedisTestServer.new
5
+ @redis_server = RedisTestServer.new(6388)
6
6
  fail('error starting redis-server') unless @redis_server.start
7
7
  sleep(0.25)
8
- @redis_client = Redis.new(url: ENV['REDIS_URL'])
8
+ @redis_client = Redis.new(url: @redis_server.url)
9
9
  @redis_client.flushdb
10
10
  @redisearch_client = RediSearch.new('test_idx', @redis_client)
11
11
  @schema = ['title', 'TEXT', 'WEIGHT', '2.0',
12
12
  'director', 'TEXT', 'WEIGHT', '1.0',
13
- 'year', 'NUMERIC']
13
+ 'year', 'NUMERIC', 'SORTABLE']
14
14
 
15
15
  end
16
16
 
@@ -47,6 +47,21 @@ class RediSearchTest < Minitest::Test
47
47
  assert_includes(@redis_client.call(['FT.SEARCH', 'test_idx', 'lost']).to_s, 'Lost in translation')
48
48
  end
49
49
 
50
+ def test_add_doc_replace
51
+ assert(@redisearch_client.create_index(@schema))
52
+ doc = ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']
53
+ assert(@redisearch_client.add_doc('id_1', doc))
54
+
55
+ doc = ['title', 'Lost in translation', 'director', 'SC', 'year', '2005']
56
+ assert_raises(Redis::CommandError) { @redisearch_client.add_doc('id_1', doc) }
57
+ assert(@redisearch_client.add_doc('id_1', doc, { replace: true }))
58
+
59
+ result = @redis_client.call(['FT.SEARCH', 'test_idx', 'SC'])
60
+ assert result.any?
61
+ assert result.to_s.include?('2005')
62
+ assert result.to_s.include?('id_1')
63
+ end
64
+
50
65
  def test_add_docs
51
66
  assert(@redisearch_client.create_index(@schema))
52
67
  docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
@@ -80,6 +95,57 @@ class RediSearchTest < Minitest::Test
80
95
  assert_equal 1, @redisearch_client.search('@year:[2004 2005]').count
81
96
  end
82
97
 
98
+ def test_search_inkeys
99
+ @redisearch_client.create_index(@schema)
100
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
101
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
102
+ assert(@redisearch_client.add_docs(docs))
103
+ matches = @redisearch_client.search('(lost|garland)', { infields: ['1', 'title'] })
104
+ assert_equal 1, matches.count
105
+ matches = @redisearch_client.search('(lost|garland)', { infields: ['2', 'director', 'title'] })
106
+ assert_equal 2, matches.count
107
+ end
108
+
109
+ def test_search_return_keys
110
+ assert(@redisearch_client.create_index(@schema))
111
+ doc = ['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']]
112
+ assert(@redisearch_client.add_doc(*doc))
113
+ matches = @redisearch_client.search('@title:lost', { return: ['2', 'title', 'year'] })
114
+ assert_equal 1, matches.count
115
+ assert_nil matches[0]['director']
116
+ assert_equal 'Lost in translation', matches[0]['title']
117
+ assert_equal '2004', matches[0]['year']
118
+ end
119
+
120
+ def test_search_sort_by
121
+ @redisearch_client.create_index(@schema)
122
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
123
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
124
+ assert(@redisearch_client.add_docs(docs))
125
+ matches = @redisearch_client.search('@year:[2000 2017]', { sortby: ['year', 'asc'] })
126
+ assert_equal 2, matches.count
127
+ assert_equal 'id_1', matches[0]['id']
128
+ matches = @redisearch_client.search('@year:[2000 2017]', { sortby: ['year', 'desc'] })
129
+ assert_equal 'id_2', matches[0]['id']
130
+ end
131
+
132
+ def test_search_limit
133
+ assert(@redisearch_client.create_index(@schema))
134
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
135
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']],
136
+ ['id_3', ['title', 'Terminator', 'director', 'James Cameron', 'year', '1984']],
137
+ ['id_4', ['title', 'Blade Runner', 'director', 'Ridley Scott', 'year', '1982']]]
138
+ assert(@redisearch_client.add_docs(docs))
139
+ matches = @redisearch_client.search('@year:[1980 2017]', { limit: ['0', '3'], sortby: ['year', 'asc'] })
140
+ assert_equal 3, matches.count
141
+ assert_equal 'id_4', matches[0]['id']
142
+ assert_equal 'id_3', matches[1]['id']
143
+ assert_equal 'id_1', matches[2]['id']
144
+ matches = @redisearch_client.search('@year:[1980 2017]', { limit: ['3', '3'], sortby: ['year', 'asc'] })
145
+ assert_equal 1, matches.count
146
+ assert_equal 'id_2', matches[0]['id']
147
+ end
148
+
83
149
  def test_index_info
84
150
  assert(@redisearch_client.create_index(@schema))
85
151
  doc = ['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']]
data/test/test_helper.rb CHANGED
@@ -10,17 +10,19 @@ require './lib/redisearch-rb'
10
10
  Dotenv.load('./.env')
11
11
 
12
12
  class RedisTestServer
13
- DEFAULT_REDIS_PATH = ''#'redis-server'
13
+ DEFAULT_REDIS_PATH = 'redis-server'
14
14
 
15
- attr_reader :process
15
+ attr_reader :process, :url
16
16
 
17
- def initialize
17
+ def initialize(default_port = nil)
18
18
  @command = [ENV['REDIS_SERVER_PATH'] || DEFAULT_REDIS_PATH]
19
+ @port = ENV['REDIS_PORT'] || default_port
19
20
  end
20
21
 
21
22
  def start
22
23
  build_args
23
24
  @process = Subprocess.popen(@command)
25
+ @url = "redis://127.0.0.1:#{@port}"
24
26
  rescue => error
25
27
  Logger.new(STDERR).error(error.message)
26
28
  @process = nil
@@ -36,7 +38,7 @@ class RedisTestServer
36
38
 
37
39
  def build_args
38
40
  if ENV['REDIS_CONF_PATH'].to_s.empty?
39
- @command << "--port #{ENV['REDIS_PORT']}" unless ENV['REDIS_PORT'].to_s.empty?
41
+ @command << "--port #{@port}" unless @port.to_s.empty?
40
42
  @command << "--loadmodule #{ENV['REDIS_MODULE_PATH']}" unless ENV['REDIS_MODULE_PATH'].to_s.empty?
41
43
  else
42
44
  @command << ENV['REDIS_CONF_PATH']
metadata CHANGED
@@ -1,18 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redisearch-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Ruiz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-08 00:00:00.000000000 Z
11
+ date: 2017-08-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
- A simple Ruby client library for RediSearc. RediSearch is a Full-Text search index over Redis,
15
- developed by RedisLabs. http://redisearch.io/
14
+ A simple Ruby client library for RediSearch.
15
+ RediSearch is a Redis powered search engine, developed by RedisLabs. http://redisearch.io/
16
16
  email:
17
17
  - vruizext@gmail.com
18
18
  executables: []
@@ -26,6 +26,7 @@ files:
26
26
  - LICENSE.txt
27
27
  - README.md
28
28
  - Rakefile
29
+ - TODO.md
29
30
  - lib/redisearch-rb.rb
30
31
  - lib/redisearch.rb
31
32
  - lib/redisearch/version.rb