dm-lucene-adapter 0.1.0-java

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.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Kristian Meier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require 'java'
2
+ begin
3
+ include_class "de.saumya.lucene.LuceneService"
4
+ rescue
5
+ require 'dm-lucene-adapter_ext'
6
+ retry
7
+ end
8
+ require 'dm_lucene_adapter/dm_lucene_adapter'
Binary file
@@ -0,0 +1,196 @@
1
+ module DataMapper
2
+ module Adapters
3
+ class LuceneAdapter < AbstractAdapter
4
+
5
+ private
6
+ def index_directory(model)
7
+ @path / "#{model.storage_name(name)}"
8
+ end
9
+
10
+ def lucene(model)
11
+ LuceneService.new(java.io.File.new(index_directory(model).to_s))
12
+ end
13
+
14
+ public
15
+
16
+ def initialize(name, options = {})
17
+ super
18
+ (@path = Pathname(@options[:path]).freeze).mkpath
19
+ end
20
+
21
+ # @param [Enumerable<Resource>] resources
22
+ # The list of resources (model instances) to create
23
+ #
24
+ # @return [Integer]
25
+ # The number of records that were actually saved into the data-store
26
+ #
27
+ # @api semipublic
28
+ def create(resources)
29
+ count = 0
30
+ indexer = lucene(resources.first.model).create_indexer
31
+ resources.each do |resource|
32
+ resource.id = indexer.next_id
33
+ map = {}
34
+ resource.attributes.each { |k,v| map[k.to_s] = v.to_s}
35
+ indexer.index(map)
36
+ count += 1
37
+ end
38
+ count
39
+ ensure
40
+ indexer.close if indexer
41
+ end
42
+
43
+ # @param [Query] query
44
+ # the query to match resources in the datastore
45
+ #
46
+ # @return [Enumerable<Hash>]
47
+ # an array of hashes to become resources
48
+ #
49
+ # @api semipublic
50
+ def read(query)
51
+ is_sorting = query.order.size > 1 || query.order[0].target.name != :id || query.order[0].operator != :asc
52
+ offset, limit = is_sorting ? [0, -1] : [query.offset, query.limit || -1]
53
+
54
+ resources = do_read(offset, limit, query)
55
+ resources.each do |resource|
56
+ query.fields.each do |field|
57
+ attribute = field.name.to_s
58
+ resource[attribute] = field.typecast(resource[attribute])
59
+ end
60
+ end
61
+
62
+ if is_sorting
63
+ #records = records.uniq if unique?
64
+ resources = query.sort_records(resources)
65
+ resources = query.limit_records(resources)
66
+ end
67
+ resources
68
+ end
69
+
70
+ private
71
+
72
+ def do_read(offset, limit, query)
73
+ reader = lucene(query.model).create_reader
74
+ if(query.conditions.nil?)
75
+ result = []
76
+
77
+ #p query
78
+ # TODO set limit, offset to default of sorting is non default
79
+ reader.read_all(offset, limit).each do |resource|
80
+ map = {}
81
+ resource.each do |k,v|
82
+ map[k] = v
83
+ end
84
+ result << map
85
+ end
86
+ result
87
+ else
88
+ ops = query.conditions.operands
89
+ if(ops.size == 1 && ops[0].class == DataMapper::Query::Conditions::EqualToComparison && ops[0].subject.name == :id)
90
+ map = {}
91
+ reader.read(query.conditions.operands[0].value).each do |k,v|
92
+ map[k] = v
93
+ end
94
+ [map]
95
+ else
96
+ op = query.conditions.slug.to_s.upcase
97
+ lquery = make_query("", ops, op)
98
+ lquery.sub!(/#{op} $/, '')
99
+ result = []
100
+ reader.read_all(offset, limit, lquery).each do |resource|
101
+ map = {}
102
+ resource.each do |k,v|
103
+ map[k] = v
104
+ end
105
+ result << map
106
+ end
107
+ result
108
+ end
109
+ end
110
+ ensure
111
+ reader.close if reader
112
+ end
113
+
114
+ private
115
+
116
+ def make_query(lquery, ops, operator)
117
+ ops.each do |comp|
118
+ case comp.slug
119
+ when :like
120
+ lquery += "#{comp.subject.name.to_s}:#{comp.value}"
121
+ unless comp.value =~ /%|_|\?|\*/
122
+ lquery += "~"
123
+ end
124
+ lquery += " #{operator} "
125
+ when :eql
126
+ lquery += "#{comp.subject.name.to_s}:\"#{comp.value}\" #{operator} "
127
+ when :not
128
+ if lquery.size == 0
129
+ lquery = "NOT "
130
+ else
131
+ lquery.sub!(/#{operator}/, "NOT")
132
+ end
133
+ lquery = make_query(lquery, comp.operands, operator)
134
+ else
135
+ warn "ignore unsupported operand #{comp.slug}"
136
+ end
137
+ end
138
+ lquery
139
+ end
140
+
141
+ public
142
+
143
+ # @param [Hash(Property => Object)] attributes
144
+ # hash of attribute values to set, keyed by Property
145
+ # @param [Collection] collection
146
+ # collection of records to be updated
147
+ #
148
+ # @return [Integer]
149
+ # the number of records updated
150
+ #
151
+ # @api semipublic
152
+ def update(attributes, collection)
153
+ count = 0
154
+ service = lucene(collection.model)
155
+ deleter = service.create_deleter
156
+ resources = read(collection.query)
157
+ resources.each do |resource|
158
+ deleter.delete(resource["id"])
159
+ end
160
+ deleter.close
161
+ deleter = nil
162
+ indexer = service.create_indexer
163
+ attributes = attributes_as_fields(attributes)
164
+ resources.each do |resource|
165
+ resource.update(attributes)
166
+ map = {}
167
+ resource.each { |k,v| map[k.to_s] = v.to_s}
168
+ indexer.index(map)
169
+ end
170
+ count
171
+ ensure
172
+ indexer.close if indexer
173
+ deleter.close if deleter
174
+ end
175
+
176
+ # @param [Collection] collection
177
+ # collection of records to be deleted
178
+ #
179
+ # @return [Integer]
180
+ # the number of records deleted
181
+ #
182
+ # @api semipublic
183
+ def delete(collection)
184
+ count = 0
185
+ indexer = lucene(collection.model).create_deleter
186
+ collection.each do |resource|
187
+ indexer.delete(resource.id)
188
+ count += 1
189
+ end
190
+ count
191
+ ensure
192
+ indexer.close if indexer
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,3 @@
1
+ module DmLuceneAdapter
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,148 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe DataMapper::Adapters::LuceneAdapter do
4
+
5
+ it "should create" do
6
+ Book.create(:author => "kristian", :title => "me and the corner").new?.should be_false
7
+ end
8
+
9
+ it "should read all" do
10
+ size = Book.all.size
11
+ (1..3).each do
12
+ Book.create(:author => "kristian", :title => "me and the corner")
13
+ end
14
+ b = Book.all
15
+ b.size.should == size + 3
16
+ end
17
+
18
+ it "should delete" do
19
+ books = []
20
+ (1..4).each do
21
+ books << Book.create(:author => "kristian", :title => "me and the corner")
22
+ end
23
+ id = books.last.id
24
+ size = Book.all.size
25
+ books.each do |b|
26
+ b.destroy if b.id % 2 == 0
27
+ end
28
+ b = Book.all
29
+ size.should == b.size + 2
30
+ book = Book.create(:author => "kristian", :title => "me and the corner")
31
+ id.should < book.id
32
+ end
33
+
34
+ it 'should read a single' do
35
+ book = Book.create(:author => "kristian", :title => "me and the corner")
36
+ b = Book.get(book.id)
37
+ b.id.should == book.id
38
+ end
39
+
40
+ it 'should update' do
41
+ book = Book.create(:author => "kristian", :title => "me and the corner")
42
+ book.author = "sanuka"
43
+ book.save
44
+ b = Book.get(book.id)
45
+ b.author.should == "sanuka"
46
+ end
47
+
48
+ describe "search" do
49
+
50
+ before :all do
51
+ @size = Book.all(:author => "sanuka").size
52
+ @len = 5
53
+ (1..@len).each do |i|
54
+ Book.create(:author => "kristian", :title => "me and the corner #{i}")
55
+ Book.create(:author => "sanuka", :title => "me and the corner #{i}")
56
+ Book.create(:author => "#{i}sanuka", :title => "me and the corner")
57
+ Book.create(:author => "#{i}sanuka#{i}", :title => "me and the corner")
58
+ end
59
+ end
60
+
61
+ describe "ascending" do
62
+
63
+ after :each do
64
+ id = 0
65
+ @books.each do |b|
66
+ b.id.should > id
67
+ id = b.id
68
+ end
69
+ end
70
+
71
+ it 'should find 5 with simple query' do
72
+ @books = Book.all(:author => "sanuka")
73
+ @books.size.should == @size + @len
74
+ @books.each { |b| b.author.should == "sanuka" }
75
+ end
76
+
77
+ it 'should find 5 with not query' do
78
+ @books = Book.all(:author.not => "sanuka")
79
+ @books.each { |b| b.author.should_not == "sanuka" }
80
+ (@books.size + @size + @len).should == Book.all.size
81
+ end
82
+
83
+ it 'should find nothing with simple query' do
84
+ @books = Book.all(:author => "saumya")
85
+ @books.size.should == 0
86
+ end
87
+
88
+ it 'should find 5 with fuzzy query' do
89
+ @books = Book.all(:author.like => "sanaku")
90
+ @books.size.should == @size + @len
91
+ @books.each { |b| b.author.should =~ /sanuka/ }
92
+ end
93
+
94
+ it 'should find 5 with single char wildcards query' do
95
+ @books = Book.all(:author.like => "san?k?")
96
+ @books.size.should == @size + @len
97
+ @books.each { |b| b.author.should =~ /sanuka/ }
98
+ end
99
+
100
+ it 'should find 5 with wildcards query' do
101
+ @books = Book.all(:author.like => "sa*ka")
102
+ @books.size.should == @size + @len
103
+ @books.each { |b| b.author.should =~ /sanuka/ }
104
+ end
105
+
106
+ it 'should obey offset' do
107
+ @books = Book.all(:author.like => "san?k?", :limit => @size)
108
+ @books.size.should == @size
109
+ end
110
+
111
+ it 'should obey offset with empty query' do
112
+ @books = Book.all(:limit => @size)
113
+ @books.size.should == @size
114
+ end
115
+
116
+ it 'should obey offset and limit' do
117
+ @books = Book.all(:author.like => "san?k?", :offset => @size, :limit => @len)
118
+ @books.size.should == @len
119
+ end
120
+
121
+ it 'should obey offset and limit with empty query' do
122
+ @books = Book.all(:offset => @size, :limit => @len)
123
+ @books.size.should == @len
124
+ end
125
+ end
126
+
127
+ describe "descending" do
128
+
129
+ after :each do
130
+ id = 12311230123321
131
+ @books.each do |b|
132
+ b.id.should < id
133
+ id = b.id
134
+ end
135
+ end
136
+
137
+ it 'should obey offset and limit descending order' do
138
+ @books = Book.all(:author.like => "san?k?", :offset => @size, :limit => @len, :order => [:id.desc])
139
+ @books.size.should == @len
140
+ end
141
+
142
+ it 'should obey offset and limit descending order with emtpy query' do
143
+ @books = Book.all(:offset => @len, :limit => @size, :order => [:id.desc])
144
+ @books.size.should == @size
145
+ end
146
+ end
147
+ end
148
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --loadby random
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+
3
+ gem 'dm-core', ">0.10.0"
4
+ require 'pathname'
5
+ $LOAD_PATH << Pathname(__FILE__).dirname.parent.expand_path + 'lib'
6
+
7
+ require 'dm-core'
8
+ require 'dm-lucene-adapter'
9
+
10
+ class Book
11
+
12
+ include DataMapper::Resource
13
+
14
+ property :id, Serial
15
+
16
+ property :author, String, :length => 128
17
+
18
+ property :title, String, :length => 255
19
+
20
+ property :published, Boolean, :default => true
21
+
22
+ end
23
+
24
+ DataMapper.setup(:default, :adapter => :lucene, :path => "tmp")
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-lucene-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: java
6
+ authors: []
7
+
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-28 00:00:00 +05:30
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dm-core
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.10.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "1.2"
34
+ version:
35
+ description: datamapper adapter for search index lucene
36
+ email:
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/dm-lucene-adapter.rb
45
+ - lib/dm-lucene-adapter_ext.jar
46
+ - lib/dm_lucene_adapter/dm_lucene_adapter.rb
47
+ - lib/dm_lucene_adapter/version.rb
48
+ - spec/dm_lucene_adapter_spec.rb
49
+ - spec/spec_helper.rb
50
+ - spec/spec.opts
51
+ - MIT-LICENSE
52
+ has_rdoc: true
53
+ homepage: http://github.com/mkristian/dm-lucene-adapter
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.5
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: dm adapter for lucene
80
+ test_files: []
81
+