activedocument 0.6.2 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ require 'rubygems'
17
17
  require 'nokogiri'
18
18
  require 'yaml'
19
19
  require 'ActiveDocument/mark_logic_http'
20
- require 'ActiveDocument/mark_logic_query_builder'
20
+ require 'ActiveDocument/corona_interface'
21
21
  require 'ActiveDocument/search_results'
22
22
  require 'ActiveDocument/finder'
23
23
  require "ActiveDocument/inheritable"
@@ -68,7 +68,7 @@ module ActiveDocument
68
68
  inheritable_attributes_list :my_namespaces, :my_default_namespace, :root, :my_attribute_namespaces, :my_default_attribute_namespaces
69
69
  @my_namespaces = Hash.new
70
70
  @my_default_namespace = nil
71
- attr_reader :document, :uri, :my_namespaces, :my_default_namespace, :root, :my_attribute_namespaces, :my_default_attribute_namespaces
71
+ attr_reader :document, :uri, :my_default_namespace, :my_namespaces, :root, :my_attribute_namespaces, :my_default_attribute_namespaces
72
72
 
73
73
 
74
74
  # create a new instance with an optional xml string to use for constructing the model
@@ -76,14 +76,20 @@ module ActiveDocument
76
76
  @document = Nokogiri::XML(xml_string) do |config|
77
77
  config.noblanks
78
78
  end
79
- if !xml_string.empty? then
79
+ if !xml_string.empty? and self.class.my_root.nil? then
80
80
  @root = @document.root.name
81
+ else
82
+ @root = self.class.my_root
81
83
  end
82
84
  @uri = uri
83
85
  end
84
86
 
85
87
  def to_s
86
- @document.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
88
+ if @document
89
+ @document.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
90
+ else
91
+ super.to_s
92
+ end
87
93
  end
88
94
 
89
95
  # saves this document to the repository. If _uri_ is provided then that will be the value used for the uri.
@@ -92,11 +98,12 @@ module ActiveDocument
92
98
  def save(uri = nil)
93
99
  doc_uri = (uri || @uri)
94
100
  if doc_uri then
95
- @@ml_http.send_xquery(@@xquery_builder.save(self, doc_uri))
101
+ response_array = ActiveDocument::CoronaInterface.save(doc_uri)
102
+ uri_array = response_array[:uri]
103
+ @@ml_http.send_corona_request(uri_array[0], uri_array[1], self.document.to_s)
96
104
  else
97
105
  raise ArgumentError, "uri must not be nil", caller
98
106
  end
99
-
100
107
  end
101
108
 
102
109
  # Returns the root element for this object
@@ -128,7 +135,7 @@ module ActiveDocument
128
135
  end
129
136
  access_element $1
130
137
  elsif method =~ /^(\w*)=$/ && arguments.length == 1 # methods with no '.' in them and ending in '='
131
- set_element($1, arguments)
138
+ set_element($1, arguments[0])
132
139
  else
133
140
  super
134
141
  end
@@ -140,8 +147,8 @@ module ActiveDocument
140
147
 
141
148
  def namespace_for_element(element)
142
149
  namespace = nil
143
- if !@my_namespaces.nil? && @my_namespaces[element]
144
- namespace = @my_namespaces[element]
150
+ if !@my_namespaces.nil? && @my_namespaces[element.to_sym]
151
+ namespace = @my_namespaces[element.to_sym]
145
152
  else
146
153
  namespace = @my_default_namespace unless @my_default_namespace.nil?
147
154
  end
@@ -150,52 +157,110 @@ module ActiveDocument
150
157
 
151
158
  def namespace_for_attribute(attribute)
152
159
  namespace = nil
153
- if !@my_attribute_namespaces.nil? && @my_attribute_namespaces[attribute]
154
- namespace = @my_attribute_namespaces[attribute]
160
+ if !@my_attribute_namespaces.nil? && @my_attribute_namespaces[attribute.to_sym]
161
+ namespace = @my_attribute_namespaces[attribute.to_sym]
155
162
  else
156
163
  namespace = @my_default_attribute_namespace unless @my_default_attribute_namespace.nil?
157
164
  end
158
165
  namespace
159
166
  end
160
167
 
168
+
169
+ # Sets the hash of elements to namespace prefixes. All prefixes should have already been registered with
170
+ # the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime
171
+ # @param namespace_hash [A hash where they keys are element names as strings and the values are namespace
172
+ # prefixes as strings]
173
+ # @return [the resultant hash]
161
174
  def namespaces(namespace_hash)
162
175
  @my_namespaces = namespace_hash
176
+ @my_namespaces
163
177
  end
164
178
 
179
+ # Sets the hash of attributes to namespace prefixes. All prefixes should have already been registered with
180
+ # the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime
181
+ # @param namespace_hash [A hash where they keys are element names as strings and the values are namespace
182
+ # prefixes as strings]
183
+ # @return [the resultant hash]
165
184
  def attribute_namespaces(namespace_hash)
166
185
  @my_attribute_namespaces = namespace_hash
186
+ @my_attribute_namespaces
167
187
  end
168
188
 
169
- def add_namespace(element, uri)
170
- @my_namespaces[element.to_s] == uri
189
+ # Adds an element / namespace prefix pair to the existing hash. All prefixes should have already been registered with
190
+ # the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime
191
+ # @param element [The element]
192
+ # @param prefix [the namespace prefix to be associated with the element]
193
+ # @return [the resultant updated hash of elements to namespace prefixes]
194
+ def add_namespace(element, prefix)
195
+ @my_namespaces[element.to_s] = prefix
196
+ @my_namespaces
171
197
  end
172
198
 
173
- def add_attribute_namespace(attribute, uri)
174
- @my_attribute_namespaces[attribute.to_s] == uri
199
+ # Adds an attribute / namespace prefix pair to the existing hash. All prefixes should have already been registered with
200
+ # the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime
201
+ # @param attribute [The attribute]
202
+ # @param prefix [the namespace prefix to be associated with the attribute]
203
+ # @return [the resultant updated hash of attributes to namespace prefixes]
204
+ def add_attribute_namespace(attribute, prefix)
205
+ @my_attribute_namespaces[attribute.to_s] = prefix
206
+ @my_attribute_namespaces
175
207
  end
176
208
 
209
+ # Removes an element / namespace prefix pair from the existing hash. #todo what about the corona config?
210
+ # @param element [the element to be removed)]
211
+ # @return [the resultant updated hash of elements to namespace prefixes]
177
212
  def remove_namespace(element)
178
- @my_namespaces.delete element
213
+ @my_namespaces.delete element.to_s
214
+ @my_namespaces
179
215
  end
180
216
 
217
+ # Removes an attribute / namespace prefix pair from the existing hash. #todo what about the corona config?
218
+ # @param attribute [the attribute to be removed)]
219
+ # @return [the resultant updated hash of attributes to namespace prefixes]
181
220
  def remove_attribute_namespace(attribute)
182
- @my_attribute_namespaces.delete attribute
221
+ @my_attribute_namespaces.delete attribute.to_s
222
+ @my_attribute_namespaces
223
+ end
224
+
225
+ # defines the default namespace prefix to be used for all otherwise unspecified elements. The prefix should have
226
+ # already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise,
227
+ # errors will likely occur at runtime
228
+ # @param prefix [the registered namespace prefix for all otherwise unspecified elements]
229
+ # @return [the default element namespace prefix]
230
+ def default_namespace(prefix)
231
+ @my_default_namespace = prefix
232
+ @my_default_namespace
183
233
  end
184
234
 
185
- def default_namespace(namespace)
186
- @my_default_namespace = namespace # todo should this just be an entry in namespaces?
235
+ # defines the default namespace prefix to be used for all otherwise unspecified attributes. The prefix should have
236
+ # already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise,
237
+ # errors will likely occur at runtime
238
+ # @param prefix [the registered namespace prefix for all otherwise unspecified attributes]
239
+ # @return [the default attribute namespace prefix]
240
+ def default_attribute_namespace(prefix)
241
+ @my_default_attribute_namespace = prefix
242
+ @my_default_attribute_namespace
187
243
  end
188
244
 
245
+ # sets the root element of the document. If not set it will default to the classname
189
246
  def root(root)
190
247
  @root = root
191
248
  end
192
249
 
193
- def default_attribute_namespace(namespace)
194
- @my_default_attribute_namespace = namespace # todo should this just be an entry in namespaces?
250
+ def my_root
251
+ @root
195
252
  end
196
253
 
254
+
197
255
  def delete(uri)
198
- @@ml_http.send_xquery(@@xquery_builder.delete(uri))
256
+ doc_uri = (uri || @uri)
257
+ if doc_uri then
258
+ response_array = ActiveDocument::CoronaInterface.delete(doc_uri)
259
+ uri_array = response_array[:uri]
260
+ @@ml_http.send_corona_request(uri_array[0], uri_array[1])
261
+ else
262
+ raise ArgumentError, "uri must not be nil", caller
263
+ end
199
264
  end
200
265
 
201
266
  # enables the dynamic finders
@@ -235,7 +300,7 @@ module ActiveDocument
235
300
  execute_attribute_finder(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options)
236
301
  elsif method =~ /find_by_(.*)$/ and arguments.length > 0 # identify element search methods
237
302
  value = arguments[0]
238
- element = $1.to_sym
303
+ element = $1 # todo: this used to be converted to a symbol. Make sure that keeping it as a string doesn't break something
239
304
  if arguments[1]
240
305
  root = arguments[1]
241
306
  else
@@ -249,7 +314,7 @@ module ActiveDocument
249
314
  if arguments[3]
250
315
  root_namespace = arguments[3]
251
316
  else
252
- root_namespace = namespace_for_element(root)
317
+ root_namespace = namespace_for_element(root) unless root.nil?
253
318
  end
254
319
  if arguments[4]
255
320
  options = arguments[4]
@@ -266,7 +331,13 @@ module ActiveDocument
266
331
  # Returns an ActiveXML object representing the requested information. If no document exists at that uri then
267
332
  # a LoadException is thrown
268
333
  def load(uri)
269
- document = @@ml_http.send_xquery(@@xquery_builder.load(uri))
334
+ response_array = ActiveDocument::CoronaInterface.load(uri)
335
+ uri_array = response_array[:uri]
336
+ begin
337
+ document = @@ml_http.send_corona_request(uri_array[0], uri_array[1])
338
+ rescue Net::HTTPServerException => exception
339
+ raise LoadException, "File #{uri} not found", caller
340
+ end
270
341
  if document.empty?
271
342
  raise LoadException, "File #{uri} not found", caller
272
343
  end
@@ -275,9 +346,10 @@ module ActiveDocument
275
346
 
276
347
  # Finds all documents of this type that contain the word anywhere in their structure
277
348
  def find_by_word(word, root=@root, namespace=@my_default_namespace)
278
- xquery = @@xquery_builder.find_by_word(word, root, namespace)
279
- @@log.info("ActiveDocument.execute_find_by_word at line #{__LINE__}: #{xquery}")
280
- SearchResults.new(@@ml_http.send_xquery(xquery))
349
+ response_array = ActiveDocument::CoronaInterface.find_by_word(word, root, namespace)
350
+ uri_array = response_array[:uri]
351
+ @@log.info("ActiveDocument.execute_find_by_word at line #{__LINE__}: #{response_array}")
352
+ SearchResults.new(@@ml_http.send_corona_request(uri_array[0], uri_array[1], nil, response_array[:post_parameters]))
281
353
  end
282
354
 
283
355
  end # end inner class
@@ -394,7 +466,7 @@ module ActiveDocument
394
466
 
395
467
  end
396
468
 
397
- # end class
469
+ # end class
398
470
 
399
471
  class ActiveDocumentException < Exception
400
472
 
@@ -0,0 +1,186 @@
1
+ # Copyright 2010 Mark Logic, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'ActiveDocument/mark_logic_search_options'
15
+ module ActiveDocument
16
+
17
+ #todo create a configuration class for logging info and such
18
+
19
+ # CoronaInterface methods always return a hash. This hash will always contain at least a uri key with associated
20
+ # value
21
+ class CoronaInterface
22
+
23
+ def self.load(uri)
24
+ {:uri => ["/store?uri=#{uri}", :get]}
25
+ end
26
+
27
+ # @param uri the uri of the record to be deleted
28
+ # @return A hash containing the necessary return values. This hash contains:
29
+ # uri: an array where the first element is the uri to be used for the REST call and the second element is the
30
+ # http verb
31
+ def self.delete(uri)
32
+ {:uri => ["/store?uri=#{uri}", :delete]}
33
+ end
34
+
35
+ # @param uri the uri of the record to be saved
36
+ # @return A hash containing the necessary return values. This hash contains:
37
+ # uri: an array where the first element is the uri to be used for the REST call and the second element is the
38
+ # http verb
39
+ def self.save(uri)
40
+ {:uri => ["/store?uri=#{uri}", :put]}
41
+ end
42
+
43
+ # This method does a full text search
44
+ # @return A hash containing the necessary return values. This hash contains:
45
+ # uri: an array where the first element is the uri to be used for the REST call and the second element is the
46
+ # http verb
47
+ # post_parameters: a hash of all post parameters to be submitted
48
+ def self.find_by_word(word, root, root_namespace, options = nil)
49
+ #todo deal with paging
50
+ response = Hash.new
51
+ post_parameters = Hash.new
52
+ options = self.setup_options(options, root, root_namespace)
53
+ unless root.nil?
54
+ if root_namespace.nil?
55
+ root_expression = root
56
+ else
57
+ root_expression = options.searchable_expression[root_namespace] + ":" + root unless root_namespace.nil?
58
+ end
59
+ end
60
+ structured_query = "{\"underElement\":\"#{root_expression}\",\"query\":{\"wordAnywhere\":\"#{word}\"}}"
61
+ response[:uri] = ["/search", :post]
62
+ post_parameters[:structuredQuery] = structured_query
63
+ post_parameters[:outputFormat] = "xml"
64
+ post_parameters[:include] = "snippet"
65
+ post_parameters[:include] = "confidence"
66
+ if options.directory_constraint
67
+ if options.directory_depth == 1
68
+ post_parameters[:inDirectory] = options.directory_constraint
69
+ else
70
+ post_parameters[:underDirectory] = options.directory_constraint
71
+ end
72
+ end
73
+ response[:post_parameters] = post_parameters
74
+ response
75
+ end
76
+
77
+ def self.find_by_element(element, value, root, element_namespace, root_namespace, options = nil)
78
+ response = Hash.new
79
+ post_parameters = Hash.new
80
+ options = self.setup_options(options, root, root_namespace)
81
+ unless root.nil?
82
+ if root_namespace.nil?
83
+ root_expression = root
84
+ else
85
+ root_expression = options.searchable_expression[root_namespace] + ":" + root unless root_namespace.nil?
86
+ end
87
+ end
88
+ element_qname = element.to_s
89
+ element_qname.insert(0, element_namespace + ":") unless element_namespace.nil?
90
+ # todo this query is the more permissive contains. Deal with more restrictive equals as well
91
+ structured_query = "{\"element\":\"#{element_qname}\", \"contains\":\"#{value}\"}"
92
+ response[:uri] = ["/search", :post]
93
+ post_parameters[:structuredQuery] = structured_query
94
+ post_parameters[:outputFormat] = "xml"
95
+ post_parameters[:include] = "snippet"
96
+ post_parameters[:include] = "confidence"
97
+ if options.directory_constraint
98
+ if options.directory_depth == 1
99
+ post_parameters[:inDirectory] = options.directory_constraint
100
+ else
101
+ post_parameters[:underDirectory] = options.directory_constraint
102
+ end
103
+ end
104
+ response[:post_parameters] = post_parameters
105
+ response
106
+ end
107
+
108
+ def self.find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options = nil)
109
+ response = Hash.new
110
+ post_parameters = Hash.new
111
+ options = self.setup_options(options, root, root_namespace)
112
+ unless root.nil?
113
+ if root_namespace.nil?
114
+ root_expression = root
115
+ else
116
+ root_expression = options.searchable_expression[root_namespace] + ":" + root unless root_namespace.nil?
117
+ end
118
+ end
119
+ element_qname = element
120
+ element_qname.insert(0, element_namespace + ":") unless element_namespace.nil?
121
+ attribute_qname = attribute.to_s
122
+ attribute_qname.insert(0, attribute_namespace + ":") unless attribute_namespace.nil?
123
+ # todo this query is the more permissive contains. Deal with more restrictive equals as well
124
+ structured_query = "{\"element\":\"#{element_qname}\",\"attribute\":\"#{attribute_qname}\", \"contains\":\"#{value}\"}"
125
+ response[:uri] = ["/search", :post]
126
+ post_parameters[:structuredQuery] = structured_query
127
+ post_parameters[:outputFormat] = "xml"
128
+ post_parameters[:include] = "snippet"
129
+ post_parameters[:include] = "confidence"
130
+ if options.directory_constraint
131
+ if options.directory_depth == 1
132
+ post_parameters[:inDirectory] = options.directory_constraint
133
+ else
134
+ post_parameters[:underDirectory] = options.directory_constraint
135
+ end
136
+ end
137
+ response[:post_parameters] = post_parameters
138
+ response
139
+ end
140
+
141
+ def self.search(search_text, start, page_length, options)
142
+ if options && options.directory_constraint
143
+ directory_string = nil
144
+ if options.directory_depth == 1
145
+ directory_string = "&inDirectory=" + options.directory_constraint
146
+ else
147
+ directory_string = "&underDirectory=" +options.directory_constraint
148
+ end
149
+ end
150
+ ["/search?stringQuery=#{search_text}&start=#{start}&end=#{start + page_length -1}&outputFormat=xml&include=snippet&include=confidence#{directory_string}", :get]
151
+ end
152
+
153
+ def self.co_occurrence(element1, element1_namespace, element2, element2_namespace, query)
154
+ # Not supported by Corona at this time
155
+ end
156
+
157
+ def self.declare_namespace(prefix, uri)
158
+ ["/manage/namespace/#{prefix}?uri=#{uri}", :post]
159
+ end
160
+
161
+ # @param uri [The uri for which the matching, if any, prefix should be found]
162
+ # @return [An array where the first item is the string uri for the request and the second item is the http verb]
163
+ def self.lookup_namespace(uri)
164
+ ["/manage/namespace/#{uri}", :get]
165
+ end
166
+
167
+ def self.delete_all_namespaces
168
+ ["/manage/namespaces/", :delete]
169
+ end
170
+
171
+ private
172
+
173
+ def self.setup_options(options, root, root_namespace)
174
+ if options then
175
+ search_options = options
176
+ else
177
+ search_options = ActiveDocument::MarkLogicSearchOptions.new
178
+ end
179
+ if (search_options.searchable_expression.empty?)
180
+ search_options.searchable_expression[root_namespace] = root unless root.nil?
181
+ end
182
+ return search_options
183
+ end
184
+
185
+ end # end class
186
+ end # end module
@@ -0,0 +1,71 @@
1
+ # Copyright 2010 Mark Logic, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require "yaml"
15
+ require 'ActiveDocument/mark_logic_http'
16
+ require 'ActiveDocument/corona_interface'
17
+ require "ActiveDocument/finder"
18
+ module ActiveDocument
19
+
20
+ # This class is used to manage the configuration of MarkLogic. It can create, list and change / delete a variety of
21
+ # configuration options including indexes namespaces and fields
22
+ class DatabaseConfiguration
23
+
24
+ attr_reader :namespaces
25
+
26
+ # @param yaml_file [The yaml file containing the configuration information for the server connection]
27
+ def self.initialize(yaml_file)
28
+ config = YAML.load_file(yaml_file)
29
+ @@ml_http = ActiveDocument::MarkLogicHTTP.new(config['uri'], config['user_name'], config['password'])
30
+ @@namespaces = Hash.new
31
+ end
32
+
33
+
34
+ # @param namespaces [a Hash of namespaces prefixes to namespaces]
35
+ def self.define_namespaces(namespaces)
36
+ namespaces.keys.each do |key|
37
+ corona_array = ActiveDocument::CoronaInterface.declare_namespace(key, namespaces[key])
38
+ @@ml_http.send_corona_request(corona_array[0], corona_array[1])
39
+ end
40
+ end
41
+
42
+ # @param prefix [The prefix for which you wish to find a matching namespace]
43
+ # @return The matching namespace as a string or nil if there is no matching namespace for the prefix
44
+ def self.lookup_namespace(prefix)
45
+ corona_array = ActiveDocument::CoronaInterface.lookup_namespace(prefix)
46
+ begin
47
+ @@ml_http.send_corona_request(corona_array[0], corona_array[1])
48
+ rescue Exception => exception
49
+ if exception.response.code == "404"
50
+ nil #return nil when no namespace is found
51
+ else
52
+ raise exception
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.delete_all_namespaces
58
+ corona_array = ActiveDocument::CoronaInterface.delete_all_namespaces
59
+ begin
60
+ @@ml_http.send_corona_request(corona_array[0], corona_array[1])
61
+ rescue Exception => exception
62
+ if exception.response && exception.response.code == "404"
63
+ nil
64
+ else
65
+ raise exception
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,12 @@
1
+ class Facets
2
+
3
+ @facets = Hash.new
4
+ @results_document.xpath("/search:response/search:facet").each do |facet|
5
+ name = facet.xpath("./@name").to_s
6
+ detail = Hash.new
7
+ @facets[name] = detail
8
+ facet.xpath("search:facet-value").each do |facet_value|
9
+ detail[facet_value.xpath("./@name").to_s] = facet_value.xpath("./@count").to_s
10
+ end
11
+ end
12
+ end
@@ -14,8 +14,8 @@
14
14
 
15
15
  require 'rubygems'
16
16
  require 'nokogiri'
17
- require 'ActiveDocument/mark_logic_query_builder'
18
17
  require 'ActiveDocument/mark_logic_http'
18
+ require 'ActiveDocument/corona_interface'
19
19
  require 'ActiveDocument/search_results'
20
20
  require 'logger'
21
21
 
@@ -24,8 +24,6 @@ module ActiveDocument
24
24
  class Finder
25
25
 
26
26
 
27
- @@xquery_builder = ActiveDocument::MarkLogicQueryBuilder.new
28
-
29
27
  def self.config(yaml_file)
30
28
  config = YAML.load_file(yaml_file)
31
29
  @@ml_http = ActiveDocument::MarkLogicHTTP.new(config['uri'], config['user_name'], config['password'])
@@ -50,27 +48,29 @@ module ActiveDocument
50
48
  end
51
49
 
52
50
  def self.execute_finder(element, value, root = nil, element_namespace = nil, root_namespace = nil, options = nil)
53
- xquery = @@xquery_builder.find_by_element(element, value, root, element_namespace, root_namespace, options)
54
- @@log.info("Finder.execute_finder at line #{__LINE__}: #{xquery}")
55
- SearchResults.new(@@ml_http.send_xquery(xquery))
51
+ response_array = ActiveDocument::CoronaInterface.find_by_element(element, value, root, element_namespace, root_namespace, options)
52
+ uri_array = response_array[:uri]
53
+ @@log.info("ActiveDocument.execute_element_finder at line #{__LINE__}: #{response_array}")
54
+ SearchResults.new(@@ml_http.send_corona_request(uri_array[0], uri_array[1], nil, response_array[:post_parameters]))
56
55
  end
57
56
 
58
- def self.execute_attribute_finder(element, attribute, value, root = nil, element_namespace = nil, attribute_namespace = nil, root_namespace = nil, options = nil)
59
- xquery = @@xquery_builder.find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options)
60
- @@log.info("Finder.execute_attribute_finder at line #{__LINE__}: #{xquery}")
61
- SearchResults.new(@@ml_http.send_xquery(xquery))
57
+ def self.execute_attribute_finder(element, attribute, value, root = nil, element_namespace = nil, attribute_namespace = nil, root_namespace = nil, options = nil)
58
+ response_array = ActiveDocument::CoronaInterface.find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options)
59
+ uri_array = response_array[:uri]
60
+ @@log.info("ActiveDocument.execute_attribute_find_by_word at line #{__LINE__}: #{response_array}")
61
+ SearchResults.new(@@ml_http.send_corona_request(uri_array[0], uri_array[1], nil, response_array[:post_parameters]))
62
62
  end
63
63
 
64
64
  def self.search(search_string, start = 1, page_length = 10, options = nil)
65
65
  start ||= 1
66
66
  page_length ||= 10
67
- search_text = @@xquery_builder.search(search_string, start, page_length, options)
68
- SearchResults.new(@@ml_http.send_xquery(search_text))
67
+ corona_array = ActiveDocument::CoronaInterface.search(search_string, start, page_length, options)
68
+ SearchResults.new(@@ml_http.send_corona_request(corona_array[0], corona_array[1]))
69
69
  end
70
70
 
71
71
  # returns a hash where the key is the terms of the co-occurrence separated by a | and the value is the frequency count
72
72
  def self.co_occurrence(element1, element1_namespace, element2, element2_namespace, query)
73
- pairs = @@ml_http.send_xquery(@@xquery_builder.co_occurrence(element1, element1_namespace, element2, element2_namespace, query)).split("*")
73
+ pairs = @@ml_http.send_xquery(ActiveDocument::CoronaInterface.co_occurrence(element1, element1_namespace, element2, element2_namespace, query)).split("*")
74
74
  pair_hash = Hash.new
75
75
  pairs.each do |p|
76
76
  temp = p.split("|")
@@ -85,30 +85,30 @@ module ActiveDocument
85
85
 
86
86
  begin
87
87
  log_location = if config['logger']['file']
88
- config['logger']['file']
89
- else
90
- STDERR
91
- end
88
+ config['logger']['file']
89
+ else
90
+ STDERR
91
+ end
92
92
  log_level = case config['logger']['level']
93
- when "debug" then
94
- Logger::DEBUG
95
- when "info" then
96
- Logger::INFO
97
- when "warn" then
98
- Logger::WARN
99
- when "error" then
100
- Logger::ERROR
101
- when "fatal" then
102
- Logger::FATAL
103
- else
104
- Logger::WARN
105
- end
106
-
107
- rotation = if config['logger']['rotation']
108
- config['logger']['rotation']
109
- else
110
- "daily"
111
- end
93
+ when "debug" then
94
+ Logger::DEBUG
95
+ when "info" then
96
+ Logger::INFO
97
+ when "warn" then
98
+ Logger::WARN
99
+ when "error" then
100
+ Logger::ERROR
101
+ when "fatal" then
102
+ Logger::FATAL
103
+ else
104
+ Logger::WARN
105
+ end
106
+
107
+ rotation = if config['logger']['rotation']
108
+ config['logger']['rotation']
109
+ else
110
+ "daily"
111
+ end
112
112
  file = open(log_location, File::WRONLY | File::APPEND | File::CREAT)
113
113
  @@log = Logger.new(file, rotation)
114
114
  @@log.level = log_level
@@ -68,30 +68,53 @@ module ActiveDocument
68
68
  @password = password
69
69
  end
70
70
 
71
- def send_xquery(xquery)
72
- if xquery.nil? or xquery.empty? then
73
- return nil
71
+ # @param uri [the uri endpoint for the request]
72
+ # @param body [the optional body]
73
+ # @param verb [The HTTP verb to be used]
74
+ # @param post_fields [a hash of post fields. They key should be the field name and the value is the field value]
75
+ # @return [nil if there if no uri or it is an empty string. Otherwise, returns the http response]
76
+ def send_corona_request(uri, verb=:get, body="", post_fields=nil)
77
+ return nil if uri.nil? or uri.empty?
78
+ target_url = @url + URI.escape(uri)
79
+ http = Net::HTTP.new(target_url.host, target_url.port)
80
+ if target_url.query
81
+ endpoint = target_url.path + "?" + target_url.query
82
+ else
83
+ endpoint = target_url.path
74
84
  end
75
- req = authenticate()
76
- req.set_form_data({'request'=>"#{xquery}"})
77
- res = Net::HTTP.new(@url.host, @url.port).start {|http| http.request(req) }
85
+ case verb
86
+ when :post
87
+ req = Net::HTTP::Post.new(endpoint)
88
+ req.set_form_data(post_fields) unless post_fields.nil?
89
+ #puts URI.decode_www_form(req.body)
90
+ when :put
91
+ req = Net::HTTP::Put.new(endpoint)
92
+ when :get
93
+ req = Net::HTTP::Get.new(endpoint)
94
+ when :delete
95
+ req = Net::HTTP::Delete.new(endpoint)
96
+ else
97
+ req = Net::HTTP::Get.new(endpoint) # safe default
98
+ end
99
+ if ((! body.nil?) and (verb == :put or verb == :post) ) then
100
+ if (req.body.nil?) then req.body = body
101
+ else
102
+ req.body << body
103
+ end
104
+ end
105
+ res = http.head(target_url.request_uri)
106
+ #puts "body::::: #{req.body}" unless verb == :put
107
+ req.digest_auth(@user_name, @password, res)
108
+ res = http.request(req)
78
109
  case res
79
110
  when Net::HTTPSuccess, Net::HTTPRedirection
80
- # puts res.body
111
+ #puts res.body
81
112
  res.body
82
113
  else
83
- res.error!
114
+ #puts req.path
115
+ raise res.error!
84
116
  end
85
117
  end
86
118
 
87
- private
88
- def authenticate
89
- req = Net::HTTP::Post.new(@url.path)
90
- Net::HTTP.start(@url.host, @url.port) do |http|
91
- res = http.head(@url.request_uri)
92
- req.digest_auth(@user_name, @password, res)
93
- end
94
- return req
95
- end
96
119
  end
97
120
  end
@@ -30,8 +30,8 @@ module ActiveDocument
30
30
  def to_s
31
31
  value = @node.xpath("./node()").to_s
32
32
  begin
33
- value[/<search:highlight>/] = ""
34
- value[/<\/search:highlight>/] = ""
33
+ value[/<span class="hit">/] = ""
34
+ value[/<\/span>/] = ""
35
35
  rescue IndexError
36
36
  end
37
37
  return value
@@ -41,8 +41,8 @@ module ActiveDocument
41
41
  value = @node.xpath("./node()").to_s
42
42
  unless highlight_tag.nil?
43
43
  begin
44
- value[/<search:highlight>/] = "<#{highlight_tag}>"
45
- value[/<\/search:highlight>/] = "</#{highlight_tag}>"
44
+ value[/<span class="hit">/] = "<#{highlight_tag}>"
45
+ value[/<\/span>/] = "</#{highlight_tag}>"
46
46
  rescue IndexError
47
47
  value
48
48
  end
@@ -24,52 +24,53 @@ module ActiveDocument
24
24
  @node = node
25
25
  end
26
26
 
27
- def index
28
- Integer(@node.xpath("./@index").to_s)
29
- end
27
+ #def index
28
+ # Integer(@node.xpath("./@index").to_s)
29
+ #end
30
30
 
31
31
  def uri
32
- @node.xpath("./@uri").to_s
32
+ @node.xpath("./corona:result/corona:uri").text.to_s
33
33
  end
34
34
 
35
- def path
36
- @node.xpath("./@path").to_s
37
- end
35
+ #def path
36
+ # @node.xpath("./@path").to_s
37
+ #end
38
38
 
39
- def score
40
- Float(@node.xpath("./@score").to_s)
41
- end
39
+ #def score
40
+ # Float(@node.xpath("./@score").to_s)
41
+ #end
42
42
 
43
43
  def confidence
44
- Float(@node.xpath("./@confidence").to_s)
44
+ Float(@node.xpath("./corona:result/corona:confidence").text.to_s)
45
45
  end
46
46
 
47
- def fitness
48
- Float(@node.xpath("./@fitness").to_s)
49
- end
47
+ #def fitness
48
+ # Float(@node.xpath("./@fitness").to_s)
49
+ #end
50
50
 
51
51
  def each(&block)
52
- nodeset = @node.xpath("./search:snippet/search:match")
52
+ nodeset = @node.xpath("./corona:result/corona:snippet/span")
53
53
  if nodeset.length == 1
54
54
  yield SearchMatch.new(nodeset[0])
55
55
  else
56
- @node.xpath("./search:snippet/search:match").each {|node| yield SearchMatch.new(node)}
56
+ @node.xpath("./corona:result/corona:snippet/span").each { |node| yield SearchMatch.new(node) }
57
57
  end
58
58
  end
59
59
 
60
60
  def root_type
61
- full_path = @node.xpath("./search:snippet/search:match")[0].xpath("./@path").to_s
62
- root = full_path.match(/:[[:alpha:]]+\/|:[[:alpha:]]+$/) # find the first :something/ which should indicate the root
61
+ full_path = @node.xpath("./corona:result/corona:snippet/span").xpath("./@path").to_s
62
+ root = full_path.match(/^\/[[:alpha:]]*((:)[[:alpha:]]*)?/) # find the first :something/ which should indicate the root
63
63
  root.to_s.delete(":/") # remove the : and / to get the root element name
64
64
  end
65
65
 
66
66
  def [](index)
67
- SearchMatch.new(@node.xpath("./search:snippet/search:match")[index])
67
+ SearchMatch.new(@node.xpath("./corona:result/corona:snippet/span")[index])
68
68
  end
69
69
 
70
70
  def length
71
- @node.xpath("./search:snippet/search:match").length
71
+ @node.xpath("./corona:result/corona:snippet/span").length
72
72
  end
73
+
73
74
  def realize(klass)
74
75
  klass.load(uri)
75
76
  end
@@ -23,64 +23,39 @@ module ActiveDocument
23
23
 
24
24
  def initialize(results)
25
25
  @results_document = Nokogiri::XML(results)
26
- @facets = Hash.new
27
- @results_document.xpath("/search:response/search:facet").each do |facet|
28
- name = facet.xpath("./@name").to_s
29
- detail = Hash.new
30
- @facets[name] = detail
31
- facet.xpath("search:facet-value").each do |facet_value|
32
- detail[facet_value.xpath("./@name").to_s] = facet_value.xpath("./@count").to_s
33
- end
34
- end
35
26
  end
36
27
 
37
28
  def total
38
- Integer(@results_document.xpath("/search:response/@total").to_s)
39
- end
40
-
41
- def start
42
- Integer(@results_document.xpath("/search:response/@start").to_s)
43
- end
44
-
45
- def page_length
46
- Integer(@results_document.xpath("/search:response/@page-length").to_s)
29
+ Integer(@results_document.xpath("/corona:response/corona:meta/corona:total/text()").to_s)
47
30
  end
48
31
 
49
- def search_text
50
- @results_document.xpath("/search:response/search:qtext/text()").to_s
32
+ def execution_time
33
+ Integer(@results_document.xpath("/corona:response/corona:meta/corona:executionTime/text()").to_s)
51
34
  end
52
35
 
53
- def query_resolution_time
54
- @results_document.xpath("/search:response/search:metrics/search:query-resolution-time/text()").to_s
55
- end
56
-
57
- def snippet_resolution_time
58
- @results_document.xpath("/search:response/search:metrics/search:snippet-resolution-time/text()").to_s
59
- end
60
-
61
- def facet_resolution_time
62
- @results_document.xpath("/search:response/search:metrics/search:facet-resolution-time/text()").to_s
36
+ def start
37
+ Integer(@results_document.xpath("/corona:response/corona:meta/corona:start/text()").to_s)
63
38
  end
64
39
 
65
- def total_time
66
- @results_document.xpath("/search:response/search:metrics/search:total-time/text()").to_s
40
+ def page_length
41
+ total - start + 1
67
42
  end
68
43
 
69
44
  def each(&block)
70
- nodeset = @results_document.xpath("/search:response/search:result")
45
+ nodeset = @results_document.xpath("/corona:response/corona:results/corona:result")
71
46
  if nodeset.length == 1
72
47
  yield SearchResult.new(nodeset[0])
73
48
  else
74
- @results_document.xpath("/search:response/search:result").each {|node| yield SearchResult.new(node)}
49
+ @results_document.xpath("/corona:response/corona:results/corona:result").each {|node| yield SearchResult.new(node)}
75
50
  end
76
51
  end
77
52
 
78
53
  def [](index)
79
- SearchResult.new(@results_document.xpath("/search:response/search:result")[index])
54
+ SearchResult.new(@results_document.xpath("/corona:response/corona:results/corona:result")[index])
80
55
  end
81
56
 
82
57
  def length
83
- @results_document.xpath("/search:response/search:result").length
58
+ @results_document.xpath("/corona:response/corona:results/corona:result").length
84
59
  end
85
60
 
86
61
  end
@@ -2,7 +2,7 @@ require 'ActiveDocument/active_document'
2
2
  require 'ActiveDocument/finder'
3
3
  require 'ActiveDocument/inheritable'
4
4
  require 'ActiveDocument/mark_logic_http'
5
- require 'ActiveDocument/mark_logic_query_builder'
5
+ require 'ActiveDocument/corona_interface'
6
6
  require 'ActiveDocument/search_match'
7
7
  require 'ActiveDocument/mark_logic_search_options'
8
8
  require 'ActiveDocument/search_result'
metadata CHANGED
@@ -1,8 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activedocument
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.6.2
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ version: "1.0"
6
9
  platform: ruby
7
10
  authors:
8
11
  - Clark D. Richey, Jr.
@@ -10,16 +13,20 @@ autorequire:
10
13
  bindir: bin
11
14
  cert_chain: []
12
15
 
13
- date: 2011-04-10 00:00:00 Z
16
+ date: 2012-01-04 00:00:00 -05:00
17
+ default_executable:
14
18
  dependencies:
15
19
  - !ruby/object:Gem::Dependency
16
20
  name: nokogiri
17
21
  prerelease: false
18
22
  requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
23
  requirements:
21
24
  - - ">="
22
25
  - !ruby/object:Gem::Version
26
+ segments:
27
+ - 1
28
+ - 4
29
+ - 1
23
30
  version: 1.4.1
24
31
  type: :runtime
25
32
  version_requirements: *id001
@@ -32,17 +39,20 @@ extensions: []
32
39
  extra_rdoc_files: []
33
40
 
34
41
  files:
42
+ - lib/active_document.rb
35
43
  - lib/ActiveDocument/active_document.rb
44
+ - lib/ActiveDocument/corona_interface.rb
45
+ - lib/ActiveDocument/database_configuration.rb
46
+ - lib/ActiveDocument/facets.rb
36
47
  - lib/ActiveDocument/finder.rb
37
48
  - lib/ActiveDocument/inheritable.rb
38
49
  - lib/ActiveDocument/mark_logic_http.rb
39
- - lib/ActiveDocument/mark_logic_query_builder.rb
40
50
  - lib/ActiveDocument/mark_logic_search_options.rb
41
51
  - lib/ActiveDocument/search_match.rb
42
52
  - lib/ActiveDocument/search_result.rb
43
53
  - lib/ActiveDocument/search_results.rb
44
- - lib/active_document.rb
45
54
  - lib/activedocument.rb
55
+ has_rdoc: true
46
56
  homepage: http://github.com/crichey/ActiveDocument
47
57
  licenses: []
48
58
 
@@ -52,21 +62,23 @@ rdoc_options: []
52
62
  require_paths:
53
63
  - lib
54
64
  required_ruby_version: !ruby/object:Gem::Requirement
55
- none: false
56
65
  requirements:
57
66
  - - ">="
58
67
  - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
59
70
  version: "0"
60
71
  required_rubygems_version: !ruby/object:Gem::Requirement
61
- none: false
62
72
  requirements:
63
73
  - - ">="
64
74
  - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
65
77
  version: "0"
66
78
  requirements: []
67
79
 
68
80
  rubyforge_project:
69
- rubygems_version: 1.7.2
81
+ rubygems_version: 1.3.6
70
82
  signing_key:
71
83
  specification_version: 3
72
84
  summary: Object Mapper for XML Database
@@ -1,111 +0,0 @@
1
- # Copyright 2010 Mark Logic, Inc.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- require 'ActiveDocument/mark_logic_search_options'
15
- module ActiveDocument
16
- # todo create new unit tests for this class - the old ones were no good
17
- class MarkLogicQueryBuilder
18
-
19
- def load(uri)
20
- "fn:doc('#{uri}')"
21
- end
22
-
23
- def delete(uri)
24
- "xdmp:document-delete('#{uri}')"
25
- end
26
-
27
- def save(document, uri)
28
- xquery = <<-GENERATED
29
- xdmp:document-insert(
30
- "#{uri}",
31
- #{document.to_s} ,
32
- xdmp:default-permissions(),
33
- xdmp:default-collections())
34
- GENERATED
35
- end
36
-
37
- # This method does a full text search
38
- def find_by_word(word, root, root_namespace, options = nil)
39
- xquery = <<-GENERATED
40
- import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
41
- search:search("#{word}",
42
- GENERATED
43
- search_options = setup_options(options, root, root_namespace)
44
- xquery << search_options.to_s
45
- xquery << ')'
46
- end
47
-
48
- def find_by_element(element, value, root, element_namespace, root_namespace, options = nil)
49
- xquery = <<-GENERATED
50
- import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
51
- search:search('find_by_element:\"#{value}\"',
52
- GENERATED
53
- search_options = setup_options(options, root, root_namespace)
54
- search_options.word_constraints["find_by_element"] = {"namespace" => element_namespace, "element" => element}
55
- xquery << search_options.to_s
56
- xquery << ')'
57
- end
58
-
59
-
60
- def find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options = nil)
61
- xquery = <<-GENERATED
62
- import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
63
- search:search("attribute:#{value}",
64
- GENERATED
65
- search_options = setup_options(options, root, root_namespace)
66
- attribute_constraint = ActiveDocument::MarkLogicSearchOptions::AttributeConstraint.new(attribute_namespace, attribute, element_namespace, element)
67
- search_options.attribute_constraints["attribute"] = attribute_constraint
68
- xquery << search_options.to_s
69
- xquery << ')'
70
- end
71
-
72
- def search(search_text, start, page_length, options)
73
- if options.nil?
74
- option = '()'
75
- else
76
- option = options.to_s
77
- end
78
- <<-GENERATED
79
- import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
80
- search:search('#{search_text}', #{option}, #{start}, #{page_length})
81
- GENERATED
82
- end
83
-
84
- def co_occurrence(element1, element1_namespace, element2, element2_namespace, query)
85
- <<-GENERATED
86
- declare namespace one = "#{element1_namespace}";
87
- declare namespace two = "#{element2_namespace}";
88
- import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
89
- let $pairs := cts:element-value-co-occurrences(xs:QName('one:#{element1}'), xs:QName('two:#{element2}'), ('frequency-order', 'fragment-frequency','ordered'), cts:query(search:parse('#{query}')))
90
- return
91
- for $pair in $pairs
92
- return
93
- ($pair/cts:value[1]/text(),"|",$pair/cts:value[2]/text(),"|",cts:frequency($pair),"*")
94
- GENERATED
95
- end
96
- private
97
-
98
- def setup_options(options, root, root_namespace)
99
- if options then
100
- search_options = options
101
- else
102
- search_options = ActiveDocument::MarkLogicSearchOptions.new
103
- end
104
- if (search_options.searchable_expression.empty?)
105
- search_options.searchable_expression[root_namespace] = root unless root.nil?
106
- end
107
- return search_options
108
- end
109
-
110
- end # end class
111
- end # end module