activedocument 0.6.2 → 1.0

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.
@@ -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