mongoid-giza 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +152 -0
- data/Rakefile +11 -0
- data/lib/mongoid/giza.rb +160 -0
- data/lib/mongoid/giza/configuration.rb +136 -0
- data/lib/mongoid/giza/dynamic_index.rb +37 -0
- data/lib/mongoid/giza/index.rb +101 -0
- data/lib/mongoid/giza/index/attribute.rb +39 -0
- data/lib/mongoid/giza/index/field.rb +27 -0
- data/lib/mongoid/giza/indexer.rb +33 -0
- data/lib/mongoid/giza/models/giza_id.rb +27 -0
- data/lib/mongoid/giza/railtie.rb +17 -0
- data/lib/mongoid/giza/search.rb +83 -0
- data/lib/mongoid/giza/version.rb +5 -0
- data/lib/mongoid/giza/xml_pipe2.rb +67 -0
- data/mongoid-giza.gemspec +32 -0
- data/spec/mongoid/giza/configuration_spec.rb +340 -0
- data/spec/mongoid/giza/dynamic_index_spec.rb +32 -0
- data/spec/mongoid/giza/index/attribute_spec.rb +36 -0
- data/spec/mongoid/giza/index/field_spec.rb +25 -0
- data/spec/mongoid/giza/index_spec.rb +162 -0
- data/spec/mongoid/giza/indexer_spec.rb +87 -0
- data/spec/mongoid/giza/models/giza_id_spec.rb +30 -0
- data/spec/mongoid/giza/search_spec.rb +100 -0
- data/spec/mongoid/giza/xml_pipe2_spec.rb +98 -0
- data/spec/mongoid/giza_spec.rb +282 -0
- data/spec/spec_helper.rb +51 -0
- metadata +227 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Giza::Index do
|
4
|
+
let(:klass) do
|
5
|
+
klass = double("klass")
|
6
|
+
allow(klass).to receive(:name) { "Klass" }
|
7
|
+
allow(klass).to receive(:all)
|
8
|
+
klass
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:index) { Mongoid::Giza::Index.new(klass) }
|
12
|
+
|
13
|
+
it "should have a list of fields" do
|
14
|
+
expect(index.fields).to be_a_kind_of(Array)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a list of attributes" do
|
18
|
+
expect(index.attributes).to be_a_kind_of(Array)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should accept a settings hash" do
|
22
|
+
settings = {setting1: 1, setting2: 2}
|
23
|
+
index = Mongoid::Giza::Index.new(klass, settings)
|
24
|
+
expect(index.settings).to be(settings)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "klass" do
|
28
|
+
it "should be mandatory" do
|
29
|
+
expect { Mongoid::Giza::Index.new }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be set on creation" do
|
33
|
+
expect(index.klass).to be(klass)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "field" do
|
38
|
+
let(:field) { double("field") }
|
39
|
+
|
40
|
+
let(:name) { "field" }
|
41
|
+
|
42
|
+
it "should require a name" do
|
43
|
+
expect { index.field }.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should create a new Field" do
|
47
|
+
expect(Mongoid::Giza::Index::Field).to receive(:new).with(name, false)
|
48
|
+
index.field(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should accept :attribute as an option" do
|
52
|
+
expect(Mongoid::Giza::Index::Field).to receive(:new).with(name, true)
|
53
|
+
index.field(name, attribute: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should add the new field to the list of fields" do
|
57
|
+
allow(Mongoid::Giza::Index::Field).to receive(:new) { field }
|
58
|
+
index.field(name)
|
59
|
+
expect(index.fields.first).to be(field)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "attribute" do
|
64
|
+
let(:name) { "attribute" }
|
65
|
+
|
66
|
+
let(:type) { :uint }
|
67
|
+
|
68
|
+
it "should require a name" do
|
69
|
+
expect { index.attribute }.to raise_error(ArgumentError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should accept a type" do
|
73
|
+
expect(Mongoid::Giza::Index::Attribute).to receive(:new).with(name, type)
|
74
|
+
index.attribute(name, type)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should automatically define the type when it is not supplied" do
|
78
|
+
type = String
|
79
|
+
allow(klass).to receive(:fields) do
|
80
|
+
fields = double("fields")
|
81
|
+
allow(fields).to receive(:[]).with(name) do
|
82
|
+
field = double("field")
|
83
|
+
allow(field).to receive(:type) { type }
|
84
|
+
field
|
85
|
+
end
|
86
|
+
fields
|
87
|
+
end
|
88
|
+
expect(Mongoid::Giza::Index::Attribute).to receive(:new).with(name, Mongoid::Giza::Index::TYPES_MAP[type])
|
89
|
+
index.attribute(name)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should default to the first type when the field is not found" do
|
93
|
+
allow(klass).to receive(:fields) do
|
94
|
+
fields = double("fields")
|
95
|
+
allow(fields).to receive(:[]).with(name) { nil }
|
96
|
+
fields
|
97
|
+
end
|
98
|
+
expect(Mongoid::Giza::Index::Attribute).to receive(:new).with(name, Mongoid::Giza::Index::TYPES_MAP.values.first)
|
99
|
+
index.attribute(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should default to the first type when the type is not mapped" do
|
103
|
+
allow(klass).to receive(:fields) do
|
104
|
+
fields = double("fields")
|
105
|
+
allow(fields).to receive(:[]).with(name) do
|
106
|
+
field = double("field")
|
107
|
+
allow(field).to receive(:type) { Object }
|
108
|
+
field
|
109
|
+
end
|
110
|
+
fields
|
111
|
+
end
|
112
|
+
expect(Mongoid::Giza::Index::Attribute).to receive(:new).with(name, Mongoid::Giza::Index::TYPES_MAP.values.first)
|
113
|
+
index.attribute(name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "name" do
|
118
|
+
it "should be the class name by default" do
|
119
|
+
allow(klass).to receive(:name) { "Klass" }
|
120
|
+
expect(index.name).to eql(:Klass)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should define a new name when supplied" do
|
124
|
+
index.name(:Index)
|
125
|
+
expect(index.name).to eql(:Index)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "generate_xmlpipe2" do
|
130
|
+
let(:xmlpipe2) { double("XMLPipe2") }
|
131
|
+
|
132
|
+
let(:buffer) { double("buffer") }
|
133
|
+
|
134
|
+
it "should create a new XMLPipe2 object" do
|
135
|
+
expect(Mongoid::Giza::XMLPipe2).to receive(:new).with(index, buffer) { xmlpipe2.as_null_object }
|
136
|
+
index.generate_xmlpipe2(buffer)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should generate the xml" do
|
140
|
+
allow(Mongoid::Giza::XMLPipe2).to receive(:new) { xmlpipe2 }
|
141
|
+
expect(xmlpipe2).to receive(:generate!)
|
142
|
+
index.generate_xmlpipe2(buffer)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "criteria" do
|
147
|
+
let(:all) { double("all") }
|
148
|
+
|
149
|
+
let(:criteria) { double("criteria") }
|
150
|
+
|
151
|
+
it "should default to all" do
|
152
|
+
allow(klass).to receive(:all) { all }
|
153
|
+
expect(index.criteria).to be(all)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should accept a new criteria as a parameter" do
|
157
|
+
allow(klass).to receive(:where) { criteria }
|
158
|
+
index.criteria(klass.where(name: "one"))
|
159
|
+
expect(index.criteria).to be(criteria)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Giza::Indexer do
|
4
|
+
before do
|
5
|
+
@controller = double("controller")
|
6
|
+
allow(Riddle::Controller).to receive(:new) { @controller }
|
7
|
+
@indexer = Mongoid::Giza::Indexer.send(:new)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:config) { Mongoid::Giza::Configuration.instance }
|
11
|
+
|
12
|
+
describe "index!" do
|
13
|
+
it "should create the sphinx configuration file" do
|
14
|
+
allow(@controller).to receive(:index)
|
15
|
+
expect(config).to receive(:render)
|
16
|
+
@indexer.index!
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should execute the sphinx indexer" do
|
20
|
+
allow(config).to receive(:render)
|
21
|
+
expect(@controller).to receive(:index).with(verbose: true)
|
22
|
+
@indexer.index!
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should accept an index list" do
|
26
|
+
allow(config).to receive(:render)
|
27
|
+
expect(@controller).to receive(:index).with(:Person, :Person_2, verbose: true)
|
28
|
+
@indexer.index!(:Person, :Person_2)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "full_index" do
|
33
|
+
let(:klass) { double("class") }
|
34
|
+
|
35
|
+
before do
|
36
|
+
allow(@indexer).to receive(:index!)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should clear the generated indexes from the configuration" do
|
40
|
+
expect(config).to receive(:clear_generated_indexes)
|
41
|
+
allow(@indexer).to receive(:giza_classes) { [klass] }
|
42
|
+
allow(klass).to receive(:regenerate_dynamic_sphinx_indexes)
|
43
|
+
@indexer.full_index
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should regenerate all dynamic indexes of the giza classes" do
|
47
|
+
allow(config).to receive(:clear_generated_indexes)
|
48
|
+
allow(@indexer).to receive(:giza_classes) { [klass] }
|
49
|
+
expect(klass).to receive(:regenerate_dynamic_sphinx_indexes)
|
50
|
+
@indexer.full_index
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should execute the indexer" do
|
54
|
+
allow(config).to receive(:clear_generated_indexes)
|
55
|
+
allow(@indexer).to receive(:giza_classes) { [klass] }
|
56
|
+
allow(klass).to receive(:regenerate_dynamic_sphinx_indexes)
|
57
|
+
expect(@indexer).to receive(:index!).with(no_args)
|
58
|
+
@indexer.full_index
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "giza_classes" do
|
63
|
+
before(:all) do
|
64
|
+
class One
|
65
|
+
include Mongoid::Document
|
66
|
+
include Mongoid::Giza
|
67
|
+
end
|
68
|
+
|
69
|
+
class Two
|
70
|
+
include Mongoid::Document
|
71
|
+
end
|
72
|
+
|
73
|
+
class Three
|
74
|
+
include Mongoid::Document
|
75
|
+
include Mongoid::Giza
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should return all classes that include the Giza module" do
|
80
|
+
expect(@indexer.giza_classes).to include(One, Three)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not return classes that do not include the Giza module" do
|
84
|
+
expect(@indexer.giza_classes).not_to include(Two)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Giza::GizaID do
|
4
|
+
describe "instance" do
|
5
|
+
let(:giza_id) { Mongoid::Giza::GizaID.new(id: :Person) }
|
6
|
+
|
7
|
+
it "should be valid" do
|
8
|
+
expect(giza_id).to be_valid
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have the specified id" do
|
12
|
+
expect(giza_id.id).to eql(:Person)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a sequence default to zero" do
|
16
|
+
expect(giza_id.seq).to eql(0)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "next_id" do
|
21
|
+
before do
|
22
|
+
Mongoid::Giza::GizaID.create(id: :Person)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return the next id for the given class" do
|
26
|
+
expect(Mongoid::Giza::GizaID.next_id(:Person)).to eql(1)
|
27
|
+
expect(Mongoid::Giza::GizaID.next_id(:Person)).to eql(2)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Giza::Search do
|
4
|
+
let(:client) do
|
5
|
+
client = double("client")
|
6
|
+
allow(Riddle::Client).to receive(:new).with("localhost", 9132) { client }
|
7
|
+
client
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:search) { Mongoid::Giza::Search.new("localhost", 9132) }
|
11
|
+
|
12
|
+
let(:filters) do
|
13
|
+
filters = double("filters")
|
14
|
+
allow(client).to receive(:filters) { filters }
|
15
|
+
filters
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:filter) { double("filter") }
|
19
|
+
|
20
|
+
describe "initialize" do
|
21
|
+
it "should create a new client with the given host and port" do
|
22
|
+
expect(Riddle::Client).to receive(:new).with("localhost", 9132)
|
23
|
+
search
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should accept a index list" do
|
27
|
+
indexes = Mongoid::Giza::Search.new("localhost", 9132, :index1, :index2).indexes
|
28
|
+
expect(indexes).to eql([:index1, :index2])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "fulltext" do
|
33
|
+
it "should append a query on the specified indexes" do
|
34
|
+
expect(client).to receive(:append_query).with("query", "index1 index2")
|
35
|
+
allow(search).to receive(:indexes) { [:index1, :index2] }
|
36
|
+
search.fulltext("query")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should search all indexes by default" do
|
40
|
+
expect(client).to receive(:append_query).with("query", "*")
|
41
|
+
allow(search).to receive(:indexes) { [] }
|
42
|
+
search.fulltext("query")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "with" do
|
47
|
+
it "should add a filter to the search" do
|
48
|
+
expect(Riddle::Client::Filter).to receive(:new).with("attr", 1, false) { filter }
|
49
|
+
expect(filters).to receive(:<<).with(filter)
|
50
|
+
client
|
51
|
+
search.with(:attr, 1)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "without" do
|
56
|
+
it "should add a filter to the search" do
|
57
|
+
expect(Riddle::Client::Filter).to receive(:new).with("attr", 1, true) { filter }
|
58
|
+
expect(filters).to receive(:<<).with(filter)
|
59
|
+
client
|
60
|
+
search.without(:attr, 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "order_by" do
|
65
|
+
it "should set the search order" do
|
66
|
+
expect(client).to receive(:sort_by=).with("attr ASC")
|
67
|
+
search.order_by(:attr, :asc)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "run" do
|
72
|
+
it "should run the query" do
|
73
|
+
expect(client).to receive(:run) { [1, 2] }
|
74
|
+
search.run
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return the result array" do
|
78
|
+
expect(client).to receive(:run) { [1, 2] }
|
79
|
+
expect(search.run).to eql([1, 2])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "riddle methods mapping" do
|
84
|
+
it "should respond to method from riddle" do
|
85
|
+
allow(client).to receive(:respond_to?).with("offset=") { true }
|
86
|
+
expect(search.respond_to?(:offset)).to eql(true)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should call the equivalent method from riddle" do
|
90
|
+
allow(client).to receive(:respond_to?).with("offset=") { true }
|
91
|
+
expect(client).to receive(:offset=).with(1)
|
92
|
+
search.offset(1)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should raise an error when the equivalent riddle's method does not exists" do
|
96
|
+
allow(client).to receive(:respond_to?).with("idontexist=") { false }
|
97
|
+
expect { search.idontexist }.to raise_error(NoMethodError)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mongoid::Giza::XMLPipe2 do
|
4
|
+
let(:xmlpipe2) { Mongoid::Giza::XMLPipe2.new(@index, @buffer) }
|
5
|
+
|
6
|
+
before do
|
7
|
+
@buffer = ""
|
8
|
+
@index = double("index")
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "generate_schema" do
|
12
|
+
before do
|
13
|
+
@field = double("field")
|
14
|
+
@attribute = double("attribute")
|
15
|
+
allow(@index).to receive(:fields) { [@field] }
|
16
|
+
allow(@index).to receive(:attributes) { [@attribute] }
|
17
|
+
allow(@field).to receive(:name) { :name }
|
18
|
+
allow(@attribute).to receive(:name) { :age }
|
19
|
+
allow(@attribute).to receive(:type) { :uint }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should generate the schema of the docset" do
|
23
|
+
allow(@field).to receive(:attribute) { false }
|
24
|
+
xmlpipe2.generate_schema
|
25
|
+
expect(@buffer).to eql('<sphinx:schema><sphinx:field name="name"/><sphinx:attr name="age" type="uint"/></sphinx:schema>')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should generate a field attribute" do
|
29
|
+
allow(@field).to receive(:attribute) { true }
|
30
|
+
xmlpipe2.generate_schema
|
31
|
+
expect(@buffer).to eql('<sphinx:schema><sphinx:field name="name" attr="string"/><sphinx:attr name="age" type="uint"/></sphinx:schema>')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "generate_docset" do
|
36
|
+
before do
|
37
|
+
field = double("field")
|
38
|
+
attribute = double("attribute")
|
39
|
+
person = double("person")
|
40
|
+
allow(@index).to receive(:fields) { [field] }
|
41
|
+
allow(@index).to receive(:attributes) { [attribute] }
|
42
|
+
allow(@index).to receive(:criteria) { [person] }
|
43
|
+
allow(person).to receive(:giza_id) { 1 }
|
44
|
+
allow(xmlpipe2).to receive(:generate_doc_tags).with([field], person) do
|
45
|
+
@buffer << "<name>Person One</name>"
|
46
|
+
end
|
47
|
+
allow(xmlpipe2).to receive(:generate_doc_tags).with([attribute], person) do
|
48
|
+
@buffer << "<age>25</age>"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "static fields and attributes" do
|
53
|
+
it "should generate the document entries" do
|
54
|
+
xmlpipe2.generate_docset
|
55
|
+
expect(@buffer).to eql('<sphinx:document id="1"><name>Person One</name><age>25</age></sphinx:document>')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "generate_doc_tags" do
|
61
|
+
before do
|
62
|
+
name = double("name")
|
63
|
+
bio = double("bio")
|
64
|
+
@fields = [name, bio]
|
65
|
+
@person = {name: "Person One", bio: "About me"}
|
66
|
+
allow(name).to receive(:name) { :name }
|
67
|
+
allow(name).to receive(:block) do
|
68
|
+
Proc.new { |document| document[:name].upcase }
|
69
|
+
end
|
70
|
+
allow(bio).to receive(:name) { :bio }
|
71
|
+
allow(bio).to receive(:block) { nil }
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should generate all tags for the given fields or attributes" do
|
75
|
+
xmlpipe2.generate_doc_tags(@fields, @person)
|
76
|
+
expect(@buffer).to eql('<name>PERSON ONE</name><bio>About me</bio>')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "generate!" do
|
81
|
+
it "should generate a xml file of the index" do
|
82
|
+
result = '<?xml version="1.0" encoding="utf-8"?>'
|
83
|
+
result << '<sphinx:docset><sphinx:schema>'
|
84
|
+
result << '<sphinx:field name="name"/><sphinx:attr name="age" type="uint"/>'
|
85
|
+
result << '</sphinx:schema>'
|
86
|
+
result << '<sphinx:document id="1"><name>Person One</name><age>25</age></sphinx:document>'
|
87
|
+
result << '</sphinx:docset>'
|
88
|
+
expect(xmlpipe2).to receive(:generate_schema) do
|
89
|
+
@buffer << '<sphinx:schema><sphinx:field name="name"/><sphinx:attr name="age" type="uint"/></sphinx:schema>'
|
90
|
+
end
|
91
|
+
expect(xmlpipe2).to receive(:generate_docset) do
|
92
|
+
@buffer << '<sphinx:document id="1"><name>Person One</name><age>25</age></sphinx:document>'
|
93
|
+
end
|
94
|
+
xmlpipe2.generate!
|
95
|
+
expect(@buffer).to eql(result)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|