redcar 0.9.0 → 0.9.1
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/CHANGES +5 -0
- data/Rakefile +1 -2
- data/lib/redcar/installer.rb +2 -1
- data/lib/redcar.rb +2 -2
- data/plugins/project_search/vendor/lucene/CHANGELOG +147 -0
- data/plugins/project_search/vendor/lucene/CONTRIBUTORS +17 -0
- data/plugins/project_search/vendor/lucene/Gemfile +9 -0
- data/plugins/project_search/vendor/lucene/Gemfile.lock +33 -0
- data/plugins/project_search/vendor/lucene/LICENSE +19 -0
- data/plugins/project_search/vendor/lucene/README.rdoc +283 -0
- data/plugins/project_search/vendor/lucene/Rakefile +35 -0
- data/plugins/project_search/vendor/lucene/examples/active_model/serializers.rb +25 -0
- data/plugins/project_search/vendor/lucene/examples/active_model/validation.rb +26 -0
- data/plugins/project_search/vendor/lucene/examples/admin/Rakefile +4 -0
- data/plugins/project_search/vendor/lucene/examples/admin/admin.rb +29 -0
- data/plugins/project_search/vendor/lucene/examples/admin/public/jquery.js +4376 -0
- data/plugins/project_search/vendor/lucene/examples/admin/public/neo4j.css +153 -0
- data/plugins/project_search/vendor/lucene/examples/admin/public/neo_admin.js +18 -0
- data/plugins/project_search/vendor/lucene/examples/admin/spec/admin_spec.rb +26 -0
- data/plugins/project_search/vendor/lucene/examples/admin/views/index.erb +21 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/README.rdoc +9 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/app.rb +7 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/batch.props +5 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/features/step_definitions/add_steps.rb +121 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/features/support/env.rb +30 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/features/support/rspec_helper.rb +50 -0
- data/plugins/project_search/vendor/lucene/examples/filetree/features/treesizes.feature +19 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/1_create_neo_db.rb +66 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/2_index_db.rb +23 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/README +12 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/find_actors.rb +56 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/install.sh +12 -0
- data/plugins/project_search/vendor/lucene/examples/imdb/model.rb +37 -0
- data/plugins/project_search/vendor/lucene/examples/railway/README +111 -0
- data/plugins/project_search/vendor/lucene/examples/railway/railnet-app.rb +31 -0
- data/plugins/project_search/vendor/lucene/examples/railway/railnet-data.rb +42 -0
- data/plugins/project_search/vendor/lucene/examples/rest/example.rb +41 -0
- data/plugins/project_search/vendor/lucene/examples/you_might_know/YouMightKnow.java +60 -0
- data/plugins/project_search/vendor/lucene/examples/you_might_know/all_simple_paths.rb +34 -0
- data/plugins/project_search/vendor/lucene/examples/you_might_know/nodes.rb +34 -0
- data/plugins/project_search/vendor/lucene/examples/you_might_know/you_might_know.rb +50 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/config.rb +145 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/document.rb +96 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/field_info.rb +144 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/hits.rb +54 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/index.rb +267 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/index_info.rb +146 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/index_searcher.rb +157 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/jars.rb +5 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/query_dsl.rb +135 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/transaction.rb +117 -0
- data/plugins/project_search/vendor/lucene/lib/lucene/version.rb +3 -0
- data/plugins/project_search/vendor/lucene/lib/lucene.rb +15 -0
- data/plugins/project_search/vendor/lucene/lucene.gemspec +23 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/document_spec.rb +32 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/field_info_spec.rb +70 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/index_info_spec.rb +76 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/index_spec.rb +643 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/query_dsl_spec.rb +142 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/sort_spec.rb +101 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/spec_helper.rb +10 -0
- data/plugins/project_search/vendor/lucene/spec/lucene/transaction_spec.rb +118 -0
- metadata +62 -4
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Lucene
|
4
|
+
class ConversionNotSupportedException < StandardError; end
|
5
|
+
|
6
|
+
class FieldInfo
|
7
|
+
DEFAULTS = {:store => false, :type => String, :analyzer => :standard}.freeze
|
8
|
+
|
9
|
+
def initialize(values = {})
|
10
|
+
@info = DEFAULTS.dup
|
11
|
+
@info.merge! values
|
12
|
+
$LUCENE_LOGGER.debug{"new FieldInfo: #{@info.inspect}"}
|
13
|
+
end
|
14
|
+
|
15
|
+
def dup
|
16
|
+
FieldInfo.new(@info)
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@info[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key,value)
|
24
|
+
@info[key] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def java_field(key, value)
|
28
|
+
# convert the ruby value to a string that lucene can handle
|
29
|
+
cvalue = convert_to_lucene(value)
|
30
|
+
|
31
|
+
# check if this field should be indexed
|
32
|
+
return nil if cvalue.nil?
|
33
|
+
|
34
|
+
# decide if the field should be stored in the lucene index or not
|
35
|
+
store = store? ? org.apache.lucene.document.Field::Store::YES : org.apache.lucene.document.Field::Store::NO
|
36
|
+
|
37
|
+
# decide if it should be tokenized/analyzed by lucene
|
38
|
+
token_type = tokenized? ? org.apache.lucene.document.Field::Index::ANALYZED : org.apache.lucene.document.Field::Index::NOT_ANALYZED
|
39
|
+
$LUCENE_LOGGER.debug{"java_field store=#{store} key='#{key.to_s}' value='#{cvalue}' token_type=#{token_type}"}
|
40
|
+
|
41
|
+
# create the new Field
|
42
|
+
org.apache.lucene.document.Field.new(key.to_s, cvalue, store, token_type ) #org.apache.lucene.document.Field::Index::NO_NORMS)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def convert_to_ruby(value)
|
47
|
+
if (value.kind_of?(Array))
|
48
|
+
value.collect{|v| convert_to_ruby(v)}
|
49
|
+
else case @info[:type].to_s
|
50
|
+
when NilClass.to_s then "" # TODO, should we accept nil values in indexes ?
|
51
|
+
when String.to_s then value.to_s
|
52
|
+
when Fixnum.to_s then value.to_i
|
53
|
+
when Float.to_s then value.to_f
|
54
|
+
when Date.to_s
|
55
|
+
return value if value.kind_of? Date
|
56
|
+
return nil if value.nil?
|
57
|
+
year = value[0..3].to_i
|
58
|
+
month = value[4..5].to_i
|
59
|
+
day = value[6..7].to_i
|
60
|
+
Date.new year,month,day
|
61
|
+
when DateTime.to_s
|
62
|
+
return value if value.kind_of? DateTime
|
63
|
+
return nil if value.nil?
|
64
|
+
year = value[0..3].to_i
|
65
|
+
month = value[4..5].to_i
|
66
|
+
day = value[6..7].to_i
|
67
|
+
hour = value[8..9].to_i
|
68
|
+
min = value[10..11].to_i
|
69
|
+
sec = value[12..13].to_i
|
70
|
+
DateTime.civil(year,month,day,hour,min,sec)
|
71
|
+
else
|
72
|
+
raise ConversionNotSupportedException.new("Can't convert key '#{value}' of with type '#{@info[:type].class.to_s}'")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def convert_to_lucene(value)
|
78
|
+
if (value.kind_of?(Array))
|
79
|
+
value.collect{|v| convert_to_lucene(v)}
|
80
|
+
elsif value.nil?
|
81
|
+
value
|
82
|
+
else
|
83
|
+
case @info[:type].to_s # otherwise it will match Class
|
84
|
+
when Fixnum.to_s then sprintf('%011d',value) # TODO: configurable
|
85
|
+
when Float.to_s then sprintf('%024.12f', value) # TODO: configurable
|
86
|
+
when Bignum.to_s then sprintf('%024d, value')
|
87
|
+
when Date.to_s
|
88
|
+
t = Time.utc(value.year, value.month, value.day)
|
89
|
+
d = t.to_i * 1000
|
90
|
+
org.apache.lucene.document.DateTools.timeToString(d,org.apache.lucene.document.DateTools::Resolution::DAY )
|
91
|
+
when DateTime.to_s
|
92
|
+
# only utc times are supported
|
93
|
+
t = Time.utc(value.year, value.month, value.day, value.hour, value.min, value.sec)
|
94
|
+
d = t.to_i * 1000
|
95
|
+
org.apache.lucene.document.DateTools.timeToString(d,org.apache.lucene.document.DateTools::Resolution::SECOND )
|
96
|
+
else value.to_s
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def convert_to_query(key,value)
|
102
|
+
if (value.kind_of? Range)
|
103
|
+
first_value = convert_to_lucene(value.first)
|
104
|
+
last_value = convert_to_lucene(value.last)
|
105
|
+
first = org.apache.lucene.index.Term.new(key.to_s, first_value)
|
106
|
+
last = org.apache.lucene.index.Term.new(key.to_s, last_value)
|
107
|
+
$LUCENE_LOGGER.debug{"convert_to_query: Range key '#{key.to_s}' #{first_value}' to '#{last_value}'"}
|
108
|
+
org.apache.lucene.search.RangeQuery.new(first, last, !value.exclude_end?)
|
109
|
+
elsif
|
110
|
+
converted_value = convert_to_lucene(value)
|
111
|
+
term = org.apache.lucene.index.Term.new(key.to_s, converted_value)
|
112
|
+
org.apache.lucene.search.TermQuery.new(term)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def tokenized?
|
117
|
+
@info[:tokenized]
|
118
|
+
end
|
119
|
+
|
120
|
+
def store?
|
121
|
+
@info[:store]
|
122
|
+
end
|
123
|
+
|
124
|
+
def eql?(other)
|
125
|
+
return false unless other.kind_of?(FieldInfo)
|
126
|
+
@info.each_pair do |key,value|
|
127
|
+
return false if other[key] != value
|
128
|
+
end
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
|
132
|
+
def ==(other)
|
133
|
+
eql? other
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
infos = @info.keys.inject(""){|s, key| s << "#{key}=#{@info[key]} "}
|
138
|
+
"FieldInfo(#{self.object_id.to_s}) [#{infos}]"
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Lucene
|
2
|
+
|
3
|
+
|
4
|
+
#
|
5
|
+
# Contains the result as a collection of Documents from a lucene query.
|
6
|
+
# Is a wrapper for the Java org.apache.lucene.search.Hits class
|
7
|
+
#
|
8
|
+
class Hits
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def initialize(field_infos, hits)
|
12
|
+
@hits = hits
|
13
|
+
@field_infos = field_infos
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# Returns the n:th hit document.
|
19
|
+
#
|
20
|
+
def [](n)
|
21
|
+
doc = @hits.doc(n)
|
22
|
+
Document.convert(@field_infos, doc)
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
#
|
27
|
+
# Returns true if there are no hits
|
28
|
+
#
|
29
|
+
def empty?
|
30
|
+
@hits.length == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def each
|
34
|
+
iter = @hits.iterator
|
35
|
+
|
36
|
+
while (iter.hasNext && hit = iter.next)
|
37
|
+
yield Document.convert(@field_infos, hit.getDocument)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
#
|
43
|
+
# The number of documents the query gave.
|
44
|
+
#
|
45
|
+
def size
|
46
|
+
@hits.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"Hits [size=#{size}]"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
require 'lucene/jars'
|
3
|
+
require 'lucene/transaction'
|
4
|
+
require 'lucene/index_searcher'
|
5
|
+
require 'lucene/document'
|
6
|
+
require 'lucene/field_info'
|
7
|
+
require 'lucene/index_info'
|
8
|
+
|
9
|
+
#
|
10
|
+
# A wrapper for the Java lucene search library.
|
11
|
+
#
|
12
|
+
module Lucene
|
13
|
+
|
14
|
+
class DocumentDeletedException < StandardError;
|
15
|
+
end
|
16
|
+
class IdFieldMissingException < StandardError;
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Represents a Lucene Index.
|
21
|
+
# The index is written/updated only when the commit method is called.
|
22
|
+
# This is done since writing to the index file should be done as a batch operation.
|
23
|
+
# (Performance will be bad otherwise).
|
24
|
+
#
|
25
|
+
# For each Thread there is zero or one Index instance. There are at most one Index instance per thread
|
26
|
+
# so there is no need for this class to use synchronization for Thread safety.
|
27
|
+
#
|
28
|
+
class Index
|
29
|
+
attr_reader :path, :uncommited
|
30
|
+
|
31
|
+
|
32
|
+
# locks per index path, must not write to the same index from 2 threads
|
33
|
+
@@locks = {}
|
34
|
+
@@locks.extend MonitorMixin
|
35
|
+
|
36
|
+
def initialize(path, index_info)
|
37
|
+
@path = path # a key (i.e. filepath) where the index is stored on disk/or RAM
|
38
|
+
@index_info = index_info # the actual storage of the index
|
39
|
+
@uncommited = {} # documents to be commited, a hash of Document
|
40
|
+
@deleted_ids = [] # documents to be deleted
|
41
|
+
end
|
42
|
+
|
43
|
+
def field_infos
|
44
|
+
IndexInfo.instance(@path)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Returns an Index instance for the current running transaction.
|
49
|
+
#
|
50
|
+
# Tries to reuse an Index instance for the current running transaction.
|
51
|
+
# If a Lucene::Transaction is running it will register this index in that transaction if
|
52
|
+
# this has not already been done.
|
53
|
+
# When it has been registered in the transaction the transaction will commit the index
|
54
|
+
# when the transaction is finished.
|
55
|
+
# The configuration (kept in the #field_infos) for this index will be the same for all indexes with the same path/key.
|
56
|
+
#
|
57
|
+
# ==== Parameters
|
58
|
+
# path<String>:: The key or location where the index should be stored (relative Lucene::Config[:storage_path]
|
59
|
+
#
|
60
|
+
# ==== Examples
|
61
|
+
# Index.new 'foo/lucene-db'
|
62
|
+
#
|
63
|
+
# ==== Returns
|
64
|
+
# Returns a new or an already existing Index
|
65
|
+
#
|
66
|
+
def self.new(path)
|
67
|
+
# make sure no one modifies the index specified at given path
|
68
|
+
lock(path).synchronize do
|
69
|
+
# create a new transaction if needed
|
70
|
+
Transaction.new unless Transaction.running?
|
71
|
+
|
72
|
+
# create a new instance only if it does not already exist in the current transaction
|
73
|
+
unless Transaction.current.index?(path)
|
74
|
+
info = IndexInfo.instance(path)
|
75
|
+
index = super(path, info)
|
76
|
+
Transaction.current.register_index(path, index)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
# return the index for the current transaction
|
80
|
+
Transaction.current.index(path)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#
|
85
|
+
# Delete all uncommited documents. Also deregister this index
|
86
|
+
# from the current transaction (if there is one transaction)
|
87
|
+
#
|
88
|
+
def clear
|
89
|
+
@uncommited.clear
|
90
|
+
Transaction.current.deregister_index self if Transaction.running?
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# See instance method Index.clear
|
95
|
+
#
|
96
|
+
def self.clear(path)
|
97
|
+
return unless Transaction.running?
|
98
|
+
return unless Transaction.current.index?(path)
|
99
|
+
Transaction.current.index(path).clear
|
100
|
+
end
|
101
|
+
|
102
|
+
# Creates a new document from the given hash of values.
|
103
|
+
# This document will be stored in this instance till it is commited.
|
104
|
+
#
|
105
|
+
# ==== Parameters
|
106
|
+
# path<String>:: The key or location where the index should be stored (relative Lucene::Config[:storage_path]
|
107
|
+
#
|
108
|
+
# ==== Examples
|
109
|
+
# index = Index.new('name_or_path_to_index')
|
110
|
+
# index << {:id=>'1', :name=>'foo'}
|
111
|
+
#
|
112
|
+
# ==== Returns
|
113
|
+
# Returns the index instance so that this method can be chained
|
114
|
+
#
|
115
|
+
def <<(key_values)
|
116
|
+
doc = Document.new(field_infos, key_values)
|
117
|
+
@uncommited[doc.id] = doc
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def id_field
|
122
|
+
@index_info.id_field
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Updates the specified document.
|
127
|
+
# The index file will not be updated until the transaction commits.
|
128
|
+
# The doc is stored in memory till the transaction commits.
|
129
|
+
#
|
130
|
+
def update(doc)
|
131
|
+
@uncommited[doc.id] = doc
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Delete the specified document.
|
136
|
+
# The index file not be updated until the transaction commits.
|
137
|
+
# The id of the deleted document is stored in memory till the transaction commits.
|
138
|
+
#
|
139
|
+
def delete(id)
|
140
|
+
@deleted_ids << id.to_s
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def deleted?(id)
|
145
|
+
@deleted_ids.include?(id.to_s)
|
146
|
+
end
|
147
|
+
|
148
|
+
def updated?(id)
|
149
|
+
@uncommited[id.to_s]
|
150
|
+
end
|
151
|
+
|
152
|
+
# Writes to the index files.
|
153
|
+
# Open and closes an lucene IndexWriter
|
154
|
+
# Close the IndexSearcher so that it will read the updated index next time.
|
155
|
+
# This method will automatically be called from a Lucene::Transaction if it was running when the index was created.
|
156
|
+
#
|
157
|
+
# This method is synchronized since it is not allowed to update a lucene index from several threads at the same time.
|
158
|
+
#
|
159
|
+
def commit
|
160
|
+
lock.synchronize do
|
161
|
+
delete_documents # deletes all documents given @deleted_ids
|
162
|
+
|
163
|
+
# are any updated document deleted ?
|
164
|
+
deleted_ids = @uncommited.keys & @deleted_ids
|
165
|
+
# make sure we don't index deleted document
|
166
|
+
deleted_ids.each {|id| @uncommited.delete(id)}
|
167
|
+
|
168
|
+
# update the remaining documents that has not been deleted
|
169
|
+
|
170
|
+
begin
|
171
|
+
index_writer = org.apache.lucene.index.IndexWriter.new(@index_info.storage, @index_info.analyzer, ! exist?)
|
172
|
+
# removes the document and adds it again
|
173
|
+
@uncommited.each_value { |doc| doc.update(index_writer) }
|
174
|
+
ensure
|
175
|
+
# TODO exception handling, what if ...
|
176
|
+
index_writer.close
|
177
|
+
|
178
|
+
@uncommited.clear
|
179
|
+
@deleted_ids.clear
|
180
|
+
|
181
|
+
# if we are running in a transaction remove this so it will not be committed twice
|
182
|
+
Transaction.current.deregister_index(self) if Transaction.running?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
#
|
189
|
+
# Delegates to the IndexSearcher.find method
|
190
|
+
#
|
191
|
+
def find(*query, &block)
|
192
|
+
# new method is a factory method, does not create if it already exists
|
193
|
+
searcher = IndexSearcher.new(@index_info.storage)
|
194
|
+
|
195
|
+
if block.nil?
|
196
|
+
case query.first
|
197
|
+
when String
|
198
|
+
return searcher.find(@index_info, query)
|
199
|
+
when Hash, Array
|
200
|
+
return searcher.find(@index_info, query.first)
|
201
|
+
end
|
202
|
+
else
|
203
|
+
return searcher.find_dsl(@index_info, &block)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def to_s
|
209
|
+
"Index [path: '#@path', #{@uncommited.size} documents]"
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# -------------------------------------------------------------------------
|
214
|
+
# Private methods
|
215
|
+
#
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
#
|
220
|
+
# There is one lock per index path.
|
221
|
+
#
|
222
|
+
def lock
|
223
|
+
@@locks.synchronize do
|
224
|
+
@@locks[@path] ||= Monitor.new
|
225
|
+
@@locks[@path]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.lock(path)
|
230
|
+
@@locks.synchronize do
|
231
|
+
@@locks[path] ||= Monitor.new
|
232
|
+
@@locks[path]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# Returns true if the index already exists.
|
238
|
+
#
|
239
|
+
def exist?
|
240
|
+
@index_info.index_exists?
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
# --------------------------------------------------------------------------
|
245
|
+
#
|
246
|
+
private
|
247
|
+
|
248
|
+
def delete_documents # :nodoc:
|
249
|
+
return unless exist? # if no index exists then there is nothing to do
|
250
|
+
|
251
|
+
writer = org.apache.lucene.index.IndexWriter.new(@index_info.storage, @index_info.analyzer, false)
|
252
|
+
id_field = @index_info.infos[@index_info.id_field]
|
253
|
+
|
254
|
+
@deleted_ids.each do |id|
|
255
|
+
converted_value = id_field.convert_to_lucene(id)
|
256
|
+
writer.deleteDocuments(org.apache.lucene.index.Term.new(@index_info.id_field.to_s, converted_value))
|
257
|
+
end
|
258
|
+
ensure
|
259
|
+
# TODO exception handling, what if ...
|
260
|
+
writer.close unless writer.nil?
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Lucene
|
2
|
+
|
3
|
+
#
|
4
|
+
# Contains info for a specific Index identified by a path
|
5
|
+
# Contains a
|
6
|
+
# * collection of FieldInfo objects.
|
7
|
+
# * the name of the id field.
|
8
|
+
# * the index storage, either file based or RAM based.
|
9
|
+
#
|
10
|
+
# Fields has default value IndexInfo::DEFAULTS.
|
11
|
+
#
|
12
|
+
class IndexInfo #:nodoc:
|
13
|
+
DEFAULTS = FieldInfo.new({}).freeze
|
14
|
+
|
15
|
+
attr_reader :infos, :path
|
16
|
+
attr_accessor :id_field
|
17
|
+
attr_writer :store_on_file
|
18
|
+
|
19
|
+
# Initializes this object by setting values to default values specified in the Lucene::Config.
|
20
|
+
# The path/id to the index is specified by the the path parameter.
|
21
|
+
# If the index is Lucene::Config[:storage_path]
|
22
|
+
# ==== Block parameters
|
23
|
+
# path<String>:: The id or the (incomplete) path on the filesystem of the index
|
24
|
+
#
|
25
|
+
# :api: private
|
26
|
+
def initialize(path)
|
27
|
+
$LUCENE_LOGGER.debug{"IndexInfo#initialize(#{path})"}
|
28
|
+
@id_field = Lucene::Config[:id_field].to_sym
|
29
|
+
@path = path
|
30
|
+
@store_on_file = Lucene::Config[:store_on_file]
|
31
|
+
@infos = {}
|
32
|
+
# always store the id field
|
33
|
+
@infos[@id_field] = FieldInfo.new(:store => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"IndexInfo [#{@id_field}, #{@infos.inspect}]"
|
38
|
+
end
|
39
|
+
|
40
|
+
def store_on_file?
|
41
|
+
@store_on_file
|
42
|
+
end
|
43
|
+
|
44
|
+
def storage
|
45
|
+
@storage ||= create_storage
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_storage
|
49
|
+
if store_on_file?
|
50
|
+
raise StandardError.new("Lucene::Config[:storage_path] is nil but index configured to be stored on filesystem") if Lucene::Config[:storage_path].nil?
|
51
|
+
Lucene::Config[:storage_path] + @path
|
52
|
+
else
|
53
|
+
org.apache.lucene.store.RAMDirectory.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def self.instance?(path)
|
59
|
+
return false if @instances.nil?
|
60
|
+
! @instances[path].nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Creates and initializes an IndexInfo object by setting values to default
|
64
|
+
# values specified in the Lucene::Config. Does not create new object if it has
|
65
|
+
# already been created before with the given path.
|
66
|
+
#
|
67
|
+
# If the index is stored on the filesystem the complete path will be
|
68
|
+
# Lucene::Config[:storage_path] + /path
|
69
|
+
#
|
70
|
+
# ==== Block parameters
|
71
|
+
# path<String>:: The id or the (incomplete) path on the filesystem of the index
|
72
|
+
#
|
73
|
+
# :api: public
|
74
|
+
def self.instance(path)
|
75
|
+
@instances ||= {}
|
76
|
+
$LUCENE_LOGGER.debug{"IndexInfos#instance(#{path}) : @instances[path]: #{@instances[path]}"}
|
77
|
+
@instances[path] ||= IndexInfo.new(path)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.delete_all
|
81
|
+
$LUCENE_LOGGER.debug{"IndexInfos#delete_all"}
|
82
|
+
@instances = nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.index_exists(path)
|
86
|
+
return false if @instances[path].nil?
|
87
|
+
instance(path).index_exists?
|
88
|
+
end
|
89
|
+
|
90
|
+
def index_exists?
|
91
|
+
org.apache.lucene.index.IndexReader.index_exists(storage)
|
92
|
+
end
|
93
|
+
|
94
|
+
def each_pair
|
95
|
+
@infos.each_pair{|key,value| yield key,value}
|
96
|
+
end
|
97
|
+
|
98
|
+
def analyzer
|
99
|
+
# do all fields have the default value :standard analyzer ?
|
100
|
+
if @infos.values.find {|info| info[:analyzer] != :standard}
|
101
|
+
# no, one or more has set
|
102
|
+
wrapper = org.apache.lucene.analysis.PerFieldAnalyzerWrapper.new(org.apache.lucene.analysis.standard.StandardAnalyzer.new)
|
103
|
+
@infos.each_pair do |key,value|
|
104
|
+
case value[:analyzer]
|
105
|
+
when :keyword
|
106
|
+
wrapper.addAnalyzer(key.to_s, org.apache.lucene.analysis.KeywordAnalyzer.new)
|
107
|
+
when :standard
|
108
|
+
# default
|
109
|
+
when :simple
|
110
|
+
wrapper.addAnalyzer(key.to_s, org.apache.lucene.analysis.SimpleAnalyzer.new)
|
111
|
+
when :whitespace
|
112
|
+
wrapper.addAnalyzer(key.to_s, org.apache.lucene.analysis.WhitespaceAnalyzer.new)
|
113
|
+
when :stop
|
114
|
+
wrapper.addAnalyzer(key.to_s, org.apache.lucene.analysis.StopAnalyzer.new)
|
115
|
+
else
|
116
|
+
raise "Unknown analyzer, supports :keyword, :standard, :simple, :stop, :whitspace, got '#{value}' for field '#{key}'"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
wrapper
|
120
|
+
else
|
121
|
+
# yes, all fields has standard analyzer
|
122
|
+
org.apache.lucene.analysis.standard.StandardAnalyzer.new
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns true if it has one or more tokenized fields
|
127
|
+
def tokenized?
|
128
|
+
@infos.values.find{|field_info| field_info.tokenized?}
|
129
|
+
end
|
130
|
+
|
131
|
+
def [](key)
|
132
|
+
k = key.to_sym
|
133
|
+
$LUCENE_LOGGER.debug{"FieldInfos create new FieldInfo key '#{k}'"} if @infos[k].nil?
|
134
|
+
@infos[k] ||= DEFAULTS.dup
|
135
|
+
@infos[k]
|
136
|
+
end
|
137
|
+
|
138
|
+
def []=(key,value)
|
139
|
+
case value
|
140
|
+
when Hash then @infos[key] = FieldInfo.new(value)
|
141
|
+
when FieldInfo then @infos[key] = value
|
142
|
+
else raise ArgumentError.new("only accept Hash and FieldInfo, got #{value.class.to_s}")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|