busca 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE +19 -0
- data/README.md +105 -0
- data/busca-0.0.1.gem +0 -0
- data/busca.gemspec +18 -0
- data/lib/busca.rb +78 -0
- data/lib/lua/index.lua +36 -0
- data/lib/lua/remove.lua +34 -0
- data/lib/lua/search.lua +51 -0
- data/makefile +2 -0
- data/tests/busca_test.rb +45 -0
- data/tests/helper.rb +7 -0
- data/tests/remove_test.rb +34 -0
- data/tests/search_test.rb +74 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b4bb8a3f394f361469f5737cff36bbf104fadfdc
|
4
|
+
data.tar.gz: 9427a9b9fbe01dae878c272de043b810e560f63d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19b15e5d14ba75446d2b5edf0880e16857b5a7b98a1df26e8839061e4b4bd7bc0f0b8835a710da534af88978832c1775c72ac8ad00a1e7b231a5ac56c4282d2d
|
7
|
+
data.tar.gz: b05df704a480f6e9a6e7be0549dea7bf264f67b5a6708620d6c01bb9a531a24dffa8af69e029bc6112b0362c49785b739e2549104f57c7dd6e55ab9f997b0feb
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Julián Porta
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
Busca
|
2
|
+
====
|
3
|
+
|
4
|
+
Busca is a simple redis search. Nothing more, nothing less.
|
5
|
+
|
6
|
+
Description
|
7
|
+
-----------
|
8
|
+
|
9
|
+
Busca is a simple redis search that uses bitmaps to index words and performs searches. It is NOT recommended for your *big data*, unless you have *big <sup>big</sup> memory*. Also, there are no tests (yet) of how it performs with a that *big data* (if you are feeling adventurous, let me know).
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
As usual, you can install it using rubygems.
|
14
|
+
|
15
|
+
```
|
16
|
+
$ gem install busca
|
17
|
+
```
|
18
|
+
|
19
|
+
## What kind of sorcery is this?
|
20
|
+
|
21
|
+
Well, that's a fair question. Busca is a ruby gem that indexes text and perform searches. It does that using redis as a backend, which is nice, because redis is nice.
|
22
|
+
|
23
|
+
To be honest, I have no idea how a fulltext search works, but I'm pretty sure it's complex. Busca is simple and works (at least for me).
|
24
|
+
|
25
|
+
It started as a proof of concept, and *it's still* a proof of concept, but running in production in a couple of tiny sites.
|
26
|
+
|
27
|
+
## Before usage
|
28
|
+
|
29
|
+
Busca works (more or less) like this:
|
30
|
+
|
31
|
+
* You create a new instance of Busca, something like `busca = Busca.new`
|
32
|
+
* You pass some *identifier* (more on this later) and a string to the `index` function. Something like `busca.index(101, "Hey, please index this fancy text for me, will you?")`
|
33
|
+
* Then, you search, like `result = busca.search("fancy")`
|
34
|
+
|
35
|
+
That's it. `result` now contains an array of the *identifiers* that contain the word "fancy".
|
36
|
+
|
37
|
+
This is pretty much it. Internally, however, a few things happen.
|
38
|
+
|
39
|
+
`Busca` uses two gems:
|
40
|
+
|
41
|
+
* [Separa](https://github.com/Porta/separa) splits the input (a string, an object, you name it) into an array of terms.
|
42
|
+
* [Filtra](https://github.com/Porta/filtra) filters that array (removing duplicates, stemming, changing case, removing stopwords) to only index the terms you really want to index and make a effective use of space.
|
43
|
+
|
44
|
+
While those gems are included in `Busca` and used by default, you're not tied to them. You can implement your own separator and filter based on your particular scenario. Feel free to check the docs of both in order to get an idea of what they do and how.
|
45
|
+
|
46
|
+
Overall, this is the flow:
|
47
|
+
|
48
|
+
#### Index:
|
49
|
+
|
50
|
+
* You create a new instance of `Busca`. This is where you pass any configuration.
|
51
|
+
|
52
|
+
* Then, you `index` something. A unique identifier is required first (in most cases, the id of whatever you're indexing, so you can retrieve it from it's store later). Then, whatever you want to index. It can be a string or a ruby object.
|
53
|
+
|
54
|
+
* `Busca` receives whatever you passed to index and passes that to the separator.
|
55
|
+
|
56
|
+
* Then, the resulting array is passed to the filter.
|
57
|
+
|
58
|
+
* The resulting array is then indexed (saved into redis).
|
59
|
+
|
60
|
+
### Search
|
61
|
+
|
62
|
+
* With your instance of `Busca`, you call the `search` method with whatever you want to search for (string or object)
|
63
|
+
|
64
|
+
* Busca passes that through the same separator and filter used in the indexing process.
|
65
|
+
|
66
|
+
* The result is an array with the identifiers you passed on the indexing process.
|
67
|
+
|
68
|
+
* Done!
|
69
|
+
|
70
|
+
## Usage
|
71
|
+
|
72
|
+
### Index/search a string
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
document = <<-eos
|
76
|
+
Ay, marry, is't:
|
77
|
+
But to my mind, though I am native here
|
78
|
+
And to the manner born, it is a custom
|
79
|
+
More honour'd in the breach than the observance.
|
80
|
+
This heavy-headed revel east and west
|
81
|
+
Makes us traduced and tax'd of other nations:
|
82
|
+
They clepe us drunkards, and with swinish phrase
|
83
|
+
Soil our addition; and indeed it takes
|
84
|
+
From our achievements, though perform'd at height,
|
85
|
+
The pith and marrow of our attribute.
|
86
|
+
eos
|
87
|
+
|
88
|
+
busca = Busca.new
|
89
|
+
indexed_id = busca.index(123, document) #indexed_id return id assigned in the index.
|
90
|
+
#not especially relevant, but in case you need it for something
|
91
|
+
#... later on
|
92
|
+
result = busca.search("breach")
|
93
|
+
result == [123] #the identifier of the document.
|
94
|
+
```
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
data/busca-0.0.1.gem
ADDED
Binary file
|
data/busca.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "busca"
|
5
|
+
s.version = "0.0.2"
|
6
|
+
s.summary = "Busca is a simple redis search"
|
7
|
+
s.description = "Busca is a simple redis search that uses bitmaps to index words"
|
8
|
+
s.authors = ["Julián Porta"]
|
9
|
+
s.email = ["julian@porta.sh"]
|
10
|
+
s.homepage = "https://github.com/Porta/busca"
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.license = "MIT"
|
13
|
+
s.add_development_dependency "cutest", '~>1.2'
|
14
|
+
s.add_runtime_dependency "redic", '~> 1.4'
|
15
|
+
s.add_runtime_dependency "msgpack", '~> 0.5'
|
16
|
+
s.add_runtime_dependency "filtra", '~> 0.0.2'
|
17
|
+
s.add_runtime_dependency "separa", '~> 0.0.3'
|
18
|
+
end
|
data/lib/busca.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
require 'msgpack'
|
3
|
+
require 'redic'
|
4
|
+
require 'separa'
|
5
|
+
require 'filtra'
|
6
|
+
|
7
|
+
class Busca
|
8
|
+
NAMESPACE = 'Busca' #TODO: Confirm gem name
|
9
|
+
LUA_CACHE = Hash.new { |h, k| h[k] = Hash.new }
|
10
|
+
LUA_INDEX = File.expand_path("./lib/lua/index.lua")
|
11
|
+
LUA_SEARCH = File.expand_path("./lib/lua/search.lua")
|
12
|
+
LUA_REMOVE = File.expand_path("./lib/lua/remove.lua")
|
13
|
+
|
14
|
+
attr_reader :namespace, :redis, :separa, :filtra
|
15
|
+
|
16
|
+
def initialize(opts = {})
|
17
|
+
@namespace = opts[:namespace] || NAMESPACE
|
18
|
+
@redis = opts[:redis] || Redic.new
|
19
|
+
@separa = opts[:separa] || Separa.new
|
20
|
+
@filtra = opts[:filtra] || Filtra.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def split_and_filter(words)
|
24
|
+
terms = @separa.call(words)
|
25
|
+
filtered = @filtra.call(terms)
|
26
|
+
return filtered
|
27
|
+
end
|
28
|
+
|
29
|
+
def index(document_id, string)
|
30
|
+
words = split_and_filter(string)
|
31
|
+
|
32
|
+
index_id = script( LUA_INDEX, 0,
|
33
|
+
@namespace.to_msgpack,
|
34
|
+
document_id.to_msgpack,
|
35
|
+
words.to_msgpack
|
36
|
+
)
|
37
|
+
return index_id
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def search(string)
|
42
|
+
words = split_and_filter(string)
|
43
|
+
document_ids = script( LUA_SEARCH, 0,
|
44
|
+
@namespace.to_msgpack,
|
45
|
+
words.to_msgpack
|
46
|
+
)
|
47
|
+
bitstring = document_ids.unpack('B*').join("")
|
48
|
+
ids = []
|
49
|
+
bitstring.each_char.each_with_index{|c, i| c == "1" ? ids.push(i) : nil}
|
50
|
+
ids
|
51
|
+
# document_ids
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def remove(id)
|
56
|
+
result = script( LUA_REMOVE, 0,
|
57
|
+
@namespace.to_msgpack,
|
58
|
+
id.to_msgpack
|
59
|
+
)
|
60
|
+
return result
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def script(file, *args)
|
66
|
+
cache = LUA_CACHE[redis.url]
|
67
|
+
|
68
|
+
if cache.key?(file)
|
69
|
+
sha = cache[file]
|
70
|
+
else
|
71
|
+
src = File.read(file)
|
72
|
+
sha = redis.call("SCRIPT", "LOAD", src)
|
73
|
+
cache[file] = sha
|
74
|
+
end
|
75
|
+
redis.call("EVALSHA", sha, *args)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/lib/lua/index.lua
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
local namespace = cmsgpack.unpack(ARGV[1])
|
2
|
+
local document_id = cmsgpack.unpack(ARGV[2])
|
3
|
+
local words = cmsgpack.unpack(ARGV[3])
|
4
|
+
|
5
|
+
local function check_for_vacants()
|
6
|
+
local id = redis.call('RPOP', namespace .. ':vacants')
|
7
|
+
return id
|
8
|
+
end
|
9
|
+
|
10
|
+
local function save(document_id, words)
|
11
|
+
-- first try to get one available id from the vacants list
|
12
|
+
local id = check_for_vacants()
|
13
|
+
|
14
|
+
if id == false then
|
15
|
+
id = redis.call('INCR', namespace .. ':document:ids')
|
16
|
+
end
|
17
|
+
|
18
|
+
redis.call('SET', namespace .. ':document:' .. id, document_id)
|
19
|
+
|
20
|
+
return id
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
local function index(words, id)
|
25
|
+
for i, word in pairs(words) do
|
26
|
+
redis.call('SETBIT', namespace .. ':' .. word, id, 1)
|
27
|
+
redis.call('RPUSH', namespace .. ':document:' .. id .. ':words', word)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
local id = save(document_id, words)
|
33
|
+
|
34
|
+
index(words, id)
|
35
|
+
|
36
|
+
return id
|
data/lib/lua/remove.lua
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
local namespace = cmsgpack.unpack(ARGV[1])
|
2
|
+
local id = cmsgpack.unpack(ARGV[2])
|
3
|
+
|
4
|
+
|
5
|
+
local function get_words(id)
|
6
|
+
local words = redis.call('LRANGE', namespace .. ':document:' .. id .. ':words', 0, -1)
|
7
|
+
return words
|
8
|
+
end
|
9
|
+
|
10
|
+
local function set_new_vacants(id)
|
11
|
+
local id = redis.call('RPUSH', namespace .. ':vacants', id)
|
12
|
+
return id
|
13
|
+
end
|
14
|
+
|
15
|
+
local function delete(id)
|
16
|
+
-- add document id to vacants list
|
17
|
+
set_new_vacants(id)
|
18
|
+
redis.call('DEL', namespace .. ':document:' .. id)
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
local function remove_from_index(words, id)
|
23
|
+
for i, word in pairs(words) do
|
24
|
+
redis.call('SETBIT', namespace .. ':' .. word, id, 0)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
local words = get_words(id)
|
30
|
+
delete(id)
|
31
|
+
|
32
|
+
remove_from_index(words, id)
|
33
|
+
|
34
|
+
return true
|
data/lib/lua/search.lua
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
local namespace = cmsgpack.unpack(ARGV[1])
|
2
|
+
local words = cmsgpack.unpack(ARGV[2])
|
3
|
+
|
4
|
+
|
5
|
+
local function join(list, char)
|
6
|
+
local temp_str = ""
|
7
|
+
for k, v in pairs( list ) do
|
8
|
+
temp_str = temp_str .. char .. v
|
9
|
+
end
|
10
|
+
temp_str = string.sub(temp_str, 2) --remove first '_'
|
11
|
+
return temp_str
|
12
|
+
end
|
13
|
+
|
14
|
+
local function namespace_keys(list)
|
15
|
+
local keys = {}
|
16
|
+
for k, v in pairs( list ) do
|
17
|
+
table.insert(keys, k, namespace .. ':' .. v)
|
18
|
+
end
|
19
|
+
return keys
|
20
|
+
end
|
21
|
+
|
22
|
+
local function temp_dest_key(words)
|
23
|
+
local temp_key = join(words, '_')
|
24
|
+
local cached_key = namespace .. ':query_cache:' .. temp_key
|
25
|
+
|
26
|
+
local cached = redis.call('GET', cached_key)
|
27
|
+
|
28
|
+
return cached_key, cached
|
29
|
+
end
|
30
|
+
|
31
|
+
local function cache_results(temp_dest_key, results)
|
32
|
+
-- no need to cache anything since the bitop operation
|
33
|
+
-- already writes the result to cache
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
local function search(words)
|
38
|
+
local cached_key, cached = temp_dest_key(words)
|
39
|
+
if cached == false then
|
40
|
+
redis.call('BITOP', 'AND', cached_key, unpack( namespace_keys(words) ) )
|
41
|
+
cached = redis.call('GET', cached_key)
|
42
|
+
if cached == false then
|
43
|
+
cached = ""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
return cached
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
local results = search(words)
|
51
|
+
return results
|
data/makefile
ADDED
data/tests/busca_test.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
scope do
|
3
|
+
setup do
|
4
|
+
document = "this is some document that I've indexed"
|
5
|
+
busca = Busca.new()
|
6
|
+
busca.redis.call('flushdb')
|
7
|
+
[busca, document]
|
8
|
+
end
|
9
|
+
|
10
|
+
test "index a document and get it's id" do |busca, document|
|
11
|
+
document_id = busca.index(5, document)
|
12
|
+
assert_equal document_id, 1
|
13
|
+
end
|
14
|
+
|
15
|
+
test "index two documents" do |busca, document|
|
16
|
+
first = busca.index(3, document)
|
17
|
+
second = busca.index(7, "uno dos tres cuatro cinco seis")
|
18
|
+
assert_equal first, 1
|
19
|
+
assert_equal second, 2
|
20
|
+
end
|
21
|
+
|
22
|
+
test "trying a more complex separator" do |a, b|
|
23
|
+
separa = Separa.new(regexp: /,/)
|
24
|
+
busca = Busca.new(separa: separa)
|
25
|
+
document = "hey,there,this,is,sparta"
|
26
|
+
document_id = busca.index(1, document)
|
27
|
+
assert_equal 1, document_id
|
28
|
+
another_document = "i,cannot,believe,in,sparta"
|
29
|
+
busca.index(2, another_document)
|
30
|
+
sparta = busca.search("sparta")
|
31
|
+
assert_equal [1,2], sparta
|
32
|
+
end
|
33
|
+
|
34
|
+
test "trying a more complex document" do |a, b|
|
35
|
+
separa = Separa.new(Separa::Obj)
|
36
|
+
busca = Busca.new(separa: separa)
|
37
|
+
document = {uno: 'uno', dos: 'dos', tres: {cuatro: 4, cinco: 'SINCO'}}
|
38
|
+
document_id = busca.index(1, document)
|
39
|
+
assert_equal 1, document_id
|
40
|
+
cinco = busca.search(dos: 'dos')
|
41
|
+
assert_equal [1], cinco
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/tests/helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
scope do
|
3
|
+
setup do
|
4
|
+
document = "this is some document that I've indexed"
|
5
|
+
busca = Busca.new()
|
6
|
+
busca.redis.call('flushdb')
|
7
|
+
[busca, document]
|
8
|
+
end
|
9
|
+
|
10
|
+
test "delete a document from index" do |busca, document|
|
11
|
+
first = busca.index(4, document)
|
12
|
+
second = busca.index(5, 'My name is julian porta and Im a good person')
|
13
|
+
busca.remove(second)
|
14
|
+
assert_equal busca.redis.call('GET', 'Busca:document:1'), "4"
|
15
|
+
assert_equal busca.redis.call('GET', 'Busca:document:2'), nil
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should vacant an id upon document deletion" do |busca, document|
|
19
|
+
first = busca.index(11, document)
|
20
|
+
second = busca.index(12, 'My name is julian porta and Im a good person')
|
21
|
+
busca.remove(second)
|
22
|
+
assert_equal busca.redis.call('GET', 'Busca:document:2'), nil
|
23
|
+
assert_equal busca.redis.call('LINDEX', 'Busca:vacants', 0), "2"
|
24
|
+
end
|
25
|
+
|
26
|
+
test "should use the vacant id on new document index" do |busca, document|
|
27
|
+
first = busca.index(13, document)
|
28
|
+
assert_equal busca.redis.call('GET', 'Busca:document:1'), "13"
|
29
|
+
busca.redis.call('RPUSH', 'Busca:vacants', 3)
|
30
|
+
second = 'My name is julian porta and Im a good person'
|
31
|
+
busca.index(14, second)
|
32
|
+
assert_equal busca.redis.call('GET', 'Busca:document:3'), "14"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
scope do
|
2
|
+
|
3
|
+
setup do
|
4
|
+
document = <<-eos
|
5
|
+
Ay, marry, is't:
|
6
|
+
But to my mind, though I am native here
|
7
|
+
And to the manner born, it is a custom
|
8
|
+
More honour'd in the breach than the observance.
|
9
|
+
This heavy-headed revel east and west
|
10
|
+
Makes us traduced and tax'd of other nations:
|
11
|
+
They clepe us drunkards, and with swinish phrase
|
12
|
+
Soil our addition; and indeed it takes
|
13
|
+
From our achievements, though perform'd at height,
|
14
|
+
The pith and marrow of our attribute.
|
15
|
+
So, oft it chances in particular men,
|
16
|
+
That for some vicious mole of nature in them,
|
17
|
+
As, in their birth--wherein they are not guilty,
|
18
|
+
Since nature cannot choose his origin--
|
19
|
+
By the o'ergrowth of some complexion,
|
20
|
+
Oft breaking down the pales and forts of reason,
|
21
|
+
Or by some habit that too much o'er-leavens
|
22
|
+
The form of plausive manners, that these men,
|
23
|
+
Carrying, I say, the stamp of one defect,
|
24
|
+
Being nature's livery, or fortune's star,--
|
25
|
+
Their virtues else--be they as pure as grace,
|
26
|
+
As infinite as man may undergo--
|
27
|
+
Shall in the general censure take corruption
|
28
|
+
From that particular fault: the dram of eale
|
29
|
+
Doth all the noble substance of a doubt
|
30
|
+
To his own scandal.
|
31
|
+
eos
|
32
|
+
compare = <<-eos
|
33
|
+
Angels and ministers of grace defend us!
|
34
|
+
Be thou a spirit of health or goblin damn'd,
|
35
|
+
Bring with thee airs from heaven or blasts from hell,
|
36
|
+
Be thy intents wicked or charitable,
|
37
|
+
Thou comest in such a questionable shape
|
38
|
+
That I will speak to thee: I'll call thee Hamlet,
|
39
|
+
King, father, royal Dane: O, answer me!
|
40
|
+
Let me not burst in ignorance; but tell
|
41
|
+
Why thy canonized bones, hearsed in death,
|
42
|
+
Have burst their cerements; why the sepulchre,
|
43
|
+
Wherein we saw thee quietly inurn'd,
|
44
|
+
Hath oped his ponderous and marble jaws,
|
45
|
+
To cast thee up again. What may this mean,
|
46
|
+
That thou, dead corse, again in complete steel
|
47
|
+
Revisit'st thus the glimpses of the moon,
|
48
|
+
Making night hideous; and we fools of nature
|
49
|
+
So horridly to shake our disposition
|
50
|
+
With thoughts beyond the reaches of our souls?
|
51
|
+
Say, why is this? wherefore? what should we do?
|
52
|
+
eos
|
53
|
+
control = <<-eos
|
54
|
+
This is a control string
|
55
|
+
eos
|
56
|
+
chongo = <<-eos
|
57
|
+
Grace is here
|
58
|
+
eos
|
59
|
+
busca = Busca.new()
|
60
|
+
busca.redis.call('flushdb')
|
61
|
+
|
62
|
+
[busca, document, compare, control, chongo]
|
63
|
+
end
|
64
|
+
|
65
|
+
test "search a document" do |busca, document, compare, control, chongo|
|
66
|
+
first = busca.index(1, document)
|
67
|
+
second = busca.index(2, compare)
|
68
|
+
third = busca.index(3, control)
|
69
|
+
fourth = busca.index(4, chongo)
|
70
|
+
result = busca.search('grace')
|
71
|
+
assert_equal result, [1, 2, 4]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: busca
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julián Porta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cutest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: msgpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: filtra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.0.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.0.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: separa
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.0.3
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.3
|
83
|
+
description: Busca is a simple redis search that uses bitmaps to index words
|
84
|
+
email:
|
85
|
+
- julian@porta.sh
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- CHANGELOG.md
|
92
|
+
- LICENSE
|
93
|
+
- README.md
|
94
|
+
- busca-0.0.1.gem
|
95
|
+
- busca.gemspec
|
96
|
+
- lib/busca.rb
|
97
|
+
- lib/lua/index.lua
|
98
|
+
- lib/lua/remove.lua
|
99
|
+
- lib/lua/search.lua
|
100
|
+
- makefile
|
101
|
+
- tests/busca_test.rb
|
102
|
+
- tests/helper.rb
|
103
|
+
- tests/remove_test.rb
|
104
|
+
- tests/search_test.rb
|
105
|
+
homepage: https://github.com/Porta/busca
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.4.6
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Busca is a simple redis search
|
129
|
+
test_files: []
|