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.
- data/lib/ActiveDocument/active_document.rb +101 -29
- data/lib/ActiveDocument/corona_interface.rb +186 -0
- data/lib/ActiveDocument/database_configuration.rb +71 -0
- data/lib/ActiveDocument/facets.rb +12 -0
- data/lib/ActiveDocument/finder.rb +36 -36
- data/lib/ActiveDocument/mark_logic_http.rb +40 -17
- data/lib/ActiveDocument/search_match.rb +4 -4
- data/lib/ActiveDocument/search_result.rb +21 -20
- data/lib/ActiveDocument/search_results.rb +11 -36
- data/lib/active_document.rb +1 -1
- metadata +21 -9
- data/lib/ActiveDocument/mark_logic_query_builder.rb +0 -111
@@ -17,7 +17,7 @@ require 'rubygems'
|
|
17
17
|
require 'nokogiri'
|
18
18
|
require 'yaml'
|
19
19
|
require 'ActiveDocument/mark_logic_http'
|
20
|
-
require 'ActiveDocument/
|
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, :
|
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
|
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
|
-
|
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
|
-
|
170
|
-
|
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
|
-
|
174
|
-
|
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
|
-
|
186
|
-
|
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
|
194
|
-
@
|
250
|
+
def my_root
|
251
|
+
@root
|
195
252
|
end
|
196
253
|
|
254
|
+
|
197
255
|
def delete(uri)
|
198
|
-
|
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.
|
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
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
68
|
-
SearchResults.new(@@ml_http.
|
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(
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
config['logger']['file']
|
89
|
+
else
|
90
|
+
STDERR
|
91
|
+
end
|
92
92
|
log_level = case config['logger']['level']
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
rotation
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
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
|
-
#
|
111
|
+
#puts res.body
|
81
112
|
res.body
|
82
113
|
else
|
83
|
-
|
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[/<
|
34
|
-
value[/<\/
|
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[/<
|
45
|
-
value[/<\/
|
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
|
-
|
29
|
-
end
|
27
|
+
#def index
|
28
|
+
# Integer(@node.xpath("./@index").to_s)
|
29
|
+
#end
|
30
30
|
|
31
31
|
def uri
|
32
|
-
@node.xpath("
|
32
|
+
@node.xpath("./corona:result/corona:uri").text.to_s
|
33
33
|
end
|
34
34
|
|
35
|
-
def path
|
36
|
-
|
37
|
-
end
|
35
|
+
#def path
|
36
|
+
# @node.xpath("./@path").to_s
|
37
|
+
#end
|
38
38
|
|
39
|
-
def score
|
40
|
-
|
41
|
-
end
|
39
|
+
#def score
|
40
|
+
# Float(@node.xpath("./@score").to_s)
|
41
|
+
#end
|
42
42
|
|
43
43
|
def confidence
|
44
|
-
Float(@node.xpath("
|
44
|
+
Float(@node.xpath("./corona:result/corona:confidence").text.to_s)
|
45
45
|
end
|
46
46
|
|
47
|
-
def fitness
|
48
|
-
|
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("./
|
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("./
|
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("./
|
62
|
-
root = full_path.match(
|
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("./
|
67
|
+
SearchMatch.new(@node.xpath("./corona:result/corona:snippet/span")[index])
|
68
68
|
end
|
69
69
|
|
70
70
|
def length
|
71
|
-
@node.xpath("./
|
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("/
|
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
|
50
|
-
@results_document.xpath("/
|
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
|
54
|
-
@results_document.xpath("/
|
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
|
66
|
-
|
40
|
+
def page_length
|
41
|
+
total - start + 1
|
67
42
|
end
|
68
43
|
|
69
44
|
def each(&block)
|
70
|
-
nodeset = @results_document.xpath("/
|
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("/
|
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("/
|
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("/
|
58
|
+
@results_document.xpath("/corona:response/corona:results/corona:result").length
|
84
59
|
end
|
85
60
|
|
86
61
|
end
|
data/lib/active_document.rb
CHANGED
@@ -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/
|
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
|
-
|
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:
|
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.
|
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
|