redisearch-rb 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '2.4.0'
4
+
5
+ gem 'redis', '~> 4.0.0.rc1'
6
+
7
+ group :development, :test do
8
+ gem 'subprocess'
9
+ gem 'minitest', '~> 5.0'
10
+ end
11
+
12
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 vruizext
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # redisearch-rb
2
+
3
+ A simple Ruby client for RediSearch module
4
+ http://redisearch.io/
5
+
6
+
7
+ ## Installation
8
+
9
+ First of all, you need to install RediSearch, if you haven't yet:
10
+
11
+ 1. Install Redis 4.0.0-rc3 https://github.com/antirez/redis/releases/tag/4.0-rc3
12
+ 2. Install RediSearch 0.16 http://redisearch.io/Quick_Start/
13
+ 3. Edit your `redis.conf` file and add a `loadmodule` directive to load the RediSearch module.
14
+ 4. You have to
15
+ To install this gem, add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'redisearch-rb'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle install
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install redisearch-rb
28
+
29
+ ## Usage
30
+
31
+
32
+ ```ruby
33
+ require 'redisearch-rb'
34
+
35
+ redis = Redis.new(REDIS_URL)
36
+ redisearch_client = RediSearch.new('test_idx', redis)
37
+
38
+ schema = ['title', 'TEXT', 'WEIGHT', '2.0',
39
+ 'director', 'TEXT', 'WEIGHT', '1.0',
40
+ 'year', 'NUMERIC']
41
+
42
+ redisearch_client.create_index(schema)
43
+ # => "OK"
44
+
45
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
46
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
47
+ redisearch_client.add_docs(docs)
48
+ # => ["OK", "OK"]
49
+
50
+ # See query syntax here: http://redisearch.io/Query_Syntax/
51
+ redisearch_client.search('lost|machina', { withscores: true })
52
+ # => [{"title"=>"Ex Machina", "director"=>"Alex Garland", "year"=>"2014", "score"=>"2", "id"=>"id_2"},
53
+ {"title"=>"Lost in translation", "director"=>"Sofia Coppola", "year"=>"2004", "score"=>"1", "id"=>"id_1"}]
54
+
55
+ redisearch_client.search('@year:[2003 2004]')
56
+ # => [{"title"=>"Lost in translation", "director"=>"Sofia Coppola", "year"=>"2004", "id"=>"id_1"}]
57
+ ```
58
+
59
+
60
+
61
+ ## Contributing
62
+
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vruizext/redisearch-rb.
64
+
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
69
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,2 @@
1
+ require 'redisearch'
2
+ require 'redisearch/version'
@@ -0,0 +1,150 @@
1
+ require 'redis'
2
+
3
+ ##
4
+ # Ruby client for RediSearch module
5
+ # http://redisearch.io/
6
+ #
7
+ class RediSearch
8
+ # VERSION = '0.1.0'
9
+
10
+ # Create RediSearch client instance
11
+ #
12
+ # @param [String] idx_name name of the index
13
+ # @param [Redis] redis_client Redis instance
14
+ # If no `redis_client` is given, `RediSearch` tries to connect to the default redis url
15
+ # i.e. redis://127.0.0.1:6379
16
+ #
17
+ # @return [RediSearch] a new client instance
18
+ def initialize(idx_name, redis_client = nil)
19
+ @idx_name = idx_name
20
+ @redis = redis_client || Redis.new
21
+ end
22
+
23
+ # Create new index with the given `schema`
24
+ # @param [Array] schema
25
+ #
26
+ # Example:
27
+ #
28
+ # redisearch = RediSearch.new('my_idx')
29
+ # redisearch.create_idx(['title', 'TEXT', 'WEIGHT', '2.0', 'director', 'TEXT', 'WEIGHT', '1.0'])
30
+ #
31
+ # See http://redisearch.io/Commands/#ftcreate
32
+ #
33
+ # @return [String] "OK" on success
34
+ def create_index(schema)
35
+ call(ft_create(schema))
36
+ end
37
+
38
+ # Drop all the keys in the current index
39
+ #
40
+ # See http://redisearch.io/Commands/#ftdrop
41
+ # @return [String] "OK" on success
42
+ def drop_index
43
+ call(ft_drop)
44
+ end
45
+
46
+ # Add a single doc to the index, with the given `doc_id` and `fields`
47
+ #
48
+ # @param [String] doc_id id assigned to the document
49
+ # @param [Array] fields name-value pairs to be indexed
50
+ #
51
+ # Example:
52
+ #
53
+ # redisearch = RediSearch.new('my_idx')
54
+ # redisearch.add_doc('id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola'])
55
+ #
56
+ # See http://redisearch.io/Commands/#ftadd
57
+ # @return [String] "OK" on success
58
+ def add_doc(doc_id, fields)
59
+ call(ft_add(doc_id, fields))
60
+ end
61
+
62
+ # Add a set of docs to the index. Uses redis `multi` to make a single bulk insert.
63
+ #
64
+ # @param [Array] docs array of tuples doc_id-fields e.g. [[`id_1`, [fields_doc_1]], [`id_2`, [fields_doc_2]]]
65
+ #
66
+ # Example:
67
+ #
68
+ # redisearch = RediSearch.new('my_idx')
69
+ # docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola'],
70
+ # ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland']]
71
+ # redisearch.add_docs(docs)
72
+ #
73
+ # See http://redisearch.io/Commands/#ftadd
74
+ # @return [String] "OK" on success
75
+ def add_docs(docs)
76
+ multi { docs.each { |doc_id, fields| @redis.call(ft_add(doc_id, fields)) } }
77
+ end
78
+
79
+ # Search the index with the given `query`
80
+ # @param [String] query text query, see syntax here http://redisearch.io/Query_Syntax/
81
+ # @param [Hash] opts
82
+ # @option opts [Boolean] :nostopwords If `true` do not filter stopwords from the query.
83
+ #
84
+ #
85
+ def search(query, opts = {})
86
+ results_to_hash(call(ft_search(query, opts)))
87
+ end
88
+
89
+ def info
90
+ call(ft_info)
91
+ end
92
+
93
+ private
94
+
95
+ def multi
96
+ @redis.with_reconnect { @redis.multi { yield } }
97
+ end
98
+
99
+ def call(command)
100
+ @redis.with_reconnect { @redis.call(command) }
101
+ end
102
+
103
+ def add(doc_id, fields)
104
+ @redis.call(ft_add(doc_id, fields))
105
+ end
106
+
107
+ def ft_create(schema)
108
+ ['FT.CREATE', @idx_name , 'SCHEMA', *schema]
109
+ end
110
+
111
+ def ft_drop
112
+ ['FT.DROP', @idx_name]
113
+ end
114
+
115
+ def ft_info
116
+ ['FT.INFO', @idx_name]
117
+ end
118
+
119
+ def ft_add(doc_id, fields)
120
+ ['FT.ADD', @idx_name , doc_id, '1.0', 'REPLACE', 'FIELDS', *fields]
121
+ end
122
+
123
+ 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].to_i} #{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
135
+ end
136
+
137
+ def results_to_hash(results)
138
+ return {} if results.nil? || results[0] == 0
139
+ results.shift
140
+ offset = results.size % 3 == 0 ? 1 : 0
141
+ rows_per_doc = 2 + offset
142
+ nr_of_docs = results.size / (2 + offset)
143
+ (0..nr_of_docs-1).map do |n|
144
+ doc = Hash[*results[rows_per_doc * n + 1 + offset]]
145
+ doc['score'] = results[rows_per_doc * n + offset] if offset > 0
146
+ doc['id'] = results[rows_per_doc * n]
147
+ doc
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,3 @@
1
+ class RediSearch
2
+ VERSION = '0.1.3'
3
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../lib/redisearch/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "redisearch-rb"
6
+ spec.version = RediSearch::VERSION
7
+ spec.authors = ["Victor Ruiz"]
8
+ spec.email = ["vruizext@gmail.com"]
9
+
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/"
13
+ spec.homepage = "https://github.com/vruizext/redisearch-rb"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.test_files = `git ls-files -- {test}/*`.split("\n")
18
+ end
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+
3
+ class RediSearchTest < Minitest::Test
4
+ REDIS_URL = 'redis://127.0.0.1:6379'
5
+
6
+ def setup
7
+ @redis_server = RedisTestServer.new
8
+ fail('error starting redis-server') unless @redis_server.start
9
+ sleep(0.25)
10
+ @redis_client = Redis.new(url: REDIS_URL)
11
+ @redis_client.flushdb
12
+ @redisearch_client = RediSearch.new('test_idx', @redis_client)
13
+ @schema = ['title', 'TEXT', 'WEIGHT', '2.0',
14
+ 'director', 'TEXT', 'WEIGHT', '1.0',
15
+ 'year', 'NUMERIC']
16
+
17
+ end
18
+
19
+ def teardown
20
+ @redis_server&.stop
21
+ end
22
+
23
+ def test_that_it_has_a_version_number
24
+ refute_nil ::RediSearch::VERSION
25
+ end
26
+
27
+ def test_create_idx
28
+ assert @redisearch_client.create_index(@schema)
29
+ info = @redis_client.call(['FT.INFO', 'test_idx'])
30
+ assert_includes(info, 'test_idx')
31
+ end
32
+
33
+ def test_create_idx_fails_with_wrong_schema
34
+ @schema << ['foo', 'BAR', 'WEIGHT', 'woz']
35
+ assert_raises(Redis::CommandError) { @redisearch_client.create_index(@schema) }
36
+ end
37
+
38
+ def test_drop_idx
39
+ assert(@redisearch_client.create_index(@schema))
40
+ assert(@redisearch_client.drop_index)
41
+ assert_raises(Redis::CommandError) { @redisearch_client.info }
42
+ end
43
+
44
+ def test_add_doc
45
+ assert(@redisearch_client.create_index(@schema))
46
+ doc = ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']
47
+ assert(@redisearch_client.add_doc('id_1', doc))
48
+ assert_includes(@redis_client.call(['FT.SEARCH', 'test_idx', 'lost']).to_s, 'Lost in translation')
49
+ end
50
+
51
+ def test_add_docs
52
+ assert(@redisearch_client.create_index(@schema))
53
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
54
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
55
+ assert(@redisearch_client.add_docs(docs))
56
+ search_result = @redis_client.call(['FT.SEARCH', 'test_idx', 'lost|ex'])
57
+ assert_includes(search_result.to_s, 'Lost in translation')
58
+ assert_includes(search_result.to_s, 'Ex Machina')
59
+ end
60
+
61
+ def test_search_simple_query
62
+ assert(@redisearch_client.create_index(@schema))
63
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
64
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
65
+ assert(@redisearch_client.add_docs(docs))
66
+ matches = @redisearch_client.search('lost|machina', { withscores: true })
67
+ assert_equal(2, matches.count)
68
+ assert matches.any? { |doc| 'Lost in translation' == doc['title'] }
69
+ assert matches.any? { |doc| 'Ex Machina' == doc['title'] }
70
+ matches.each { |doc| assert doc['score'].to_i > 0 }
71
+ end
72
+
73
+ def test_search_field_selector
74
+ assert(@redisearch_client.create_index(@schema))
75
+ doc = ['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']]
76
+ assert(@redisearch_client.add_doc(*doc))
77
+ matches = @redisearch_client.search('@title:lost')
78
+ assert_equal(1, matches.count)
79
+ assert 'Lost in translation' == matches[0]['title']
80
+ assert_empty @redisearch_client.search('@director:lost')
81
+ assert_equal 1, @redisearch_client.search('@year:[2004 2005]').count
82
+ end
83
+ end
@@ -0,0 +1,33 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'logger'
4
+ require 'minitest/autorun'
5
+ require 'subprocess'
6
+
7
+ require './lib/redisearch'
8
+
9
+ class RedisTestServer
10
+ DEFAULT_REDIS_PATH = 'redis-server'
11
+ DEFAULT_REDIS_CONFIG = './test/redis/redis.conf'
12
+
13
+ attr_reader :process
14
+
15
+ def initialize(path = nil, config = nil)
16
+ @path = path || DEFAULT_REDIS_PATH
17
+ @config = config || DEFAULT_REDIS_CONFIG
18
+ end
19
+
20
+ def start
21
+ @process = Subprocess.popen([@path, @config])
22
+ sleep(0.25)
23
+ rescue => error
24
+ Logger.new(STDERR).error(error.message)
25
+ @process = nil
26
+ end
27
+
28
+ def stop
29
+ @process.terminate
30
+ rescue => error
31
+ Logger.new(STDERR).error(error.message)
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redisearch-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Victor Ruiz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-01 00:00:00.000000000 Z
12
+ dependencies: []
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/
16
+ email:
17
+ - vruizext@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ".gitignore"
23
+ - ".idea/misc.xml"
24
+ - ".idea/modules.xml"
25
+ - ".idea/redisearch-rb.iml"
26
+ - ".idea/vcs.xml"
27
+ - ".idea/workspace.xml"
28
+ - ".travis.yml"
29
+ - Gemfile
30
+ - LICENSE.txt
31
+ - README.md
32
+ - Rakefile
33
+ - lib/redisearch-rb.rb
34
+ - lib/redisearch.rb
35
+ - lib/redisearch/version.rb
36
+ - redisearch-rb.gemspec
37
+ - test/redisearch_test.rb
38
+ - test/test_helper.rb
39
+ homepage: https://github.com/vruizext/redisearch-rb
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.6.8
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: A simple Ruby client library for RediSearch
63
+ test_files: []