simple_solr_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +349 -0
  5. data/Rakefile +11 -0
  6. data/lib/simple_solr.rb +42 -0
  7. data/lib/simple_solr/client.rb +139 -0
  8. data/lib/simple_solr/client/core_admin.rb +0 -0
  9. data/lib/simple_solr/core.rb +50 -0
  10. data/lib/simple_solr/core/admin.rb +47 -0
  11. data/lib/simple_solr/core/core_data.rb +51 -0
  12. data/lib/simple_solr/core/index.rb +25 -0
  13. data/lib/simple_solr/core/search.rb +21 -0
  14. data/lib/simple_solr/response/document.rb +45 -0
  15. data/lib/simple_solr/response/generic_response.rb +19 -0
  16. data/lib/simple_solr/response/query_response.rb +54 -0
  17. data/lib/simple_solr/schema.rb +261 -0
  18. data/lib/simple_solr/schema/analysis.rb +58 -0
  19. data/lib/simple_solr/schema/copyfield.rb +42 -0
  20. data/lib/simple_solr/schema/dynamic_field.rb +23 -0
  21. data/lib/simple_solr/schema/field.rb +35 -0
  22. data/lib/simple_solr/schema/field_or_type.rb +112 -0
  23. data/lib/simple_solr/schema/field_type.rb +62 -0
  24. data/lib/simple_solr/schema/matcher.rb +16 -0
  25. data/lib/simple_solr/version.rb +3 -0
  26. data/simple_solr_client.gemspec +39 -0
  27. data/solr_sample_core/conf/_schema_analysis_stopwords_english.json +38 -0
  28. data/solr_sample_core/conf/_schema_analysis_synonyms_english.json +11 -0
  29. data/solr_sample_core/conf/admin-extra.html +24 -0
  30. data/solr_sample_core/conf/admin-extra.menu-bottom.html +25 -0
  31. data/solr_sample_core/conf/admin-extra.menu-top.html +25 -0
  32. data/solr_sample_core/conf/clustering/carrot2/kmeans-attributes.xml +19 -0
  33. data/solr_sample_core/conf/clustering/carrot2/lingo-attributes.xml +24 -0
  34. data/solr_sample_core/conf/clustering/carrot2/stc-attributes.xml +19 -0
  35. data/solr_sample_core/conf/currency.xml +67 -0
  36. data/solr_sample_core/conf/elevate.xml +38 -0
  37. data/solr_sample_core/conf/lang/contractions_ca.txt +8 -0
  38. data/solr_sample_core/conf/lang/contractions_fr.txt +15 -0
  39. data/solr_sample_core/conf/lang/contractions_ga.txt +5 -0
  40. data/solr_sample_core/conf/lang/contractions_it.txt +23 -0
  41. data/solr_sample_core/conf/lang/hyphenations_ga.txt +5 -0
  42. data/solr_sample_core/conf/lang/stemdict_nl.txt +6 -0
  43. data/solr_sample_core/conf/lang/stoptags_ja.txt +420 -0
  44. data/solr_sample_core/conf/lang/stopwords_ar.txt +125 -0
  45. data/solr_sample_core/conf/lang/stopwords_bg.txt +193 -0
  46. data/solr_sample_core/conf/lang/stopwords_ca.txt +220 -0
  47. data/solr_sample_core/conf/lang/stopwords_ckb.txt +136 -0
  48. data/solr_sample_core/conf/lang/stopwords_cz.txt +172 -0
  49. data/solr_sample_core/conf/lang/stopwords_da.txt +110 -0
  50. data/solr_sample_core/conf/lang/stopwords_de.txt +294 -0
  51. data/solr_sample_core/conf/lang/stopwords_el.txt +78 -0
  52. data/solr_sample_core/conf/lang/stopwords_en.txt +54 -0
  53. data/solr_sample_core/conf/lang/stopwords_es.txt +356 -0
  54. data/solr_sample_core/conf/lang/stopwords_eu.txt +99 -0
  55. data/solr_sample_core/conf/lang/stopwords_fa.txt +313 -0
  56. data/solr_sample_core/conf/lang/stopwords_fi.txt +97 -0
  57. data/solr_sample_core/conf/lang/stopwords_fr.txt +186 -0
  58. data/solr_sample_core/conf/lang/stopwords_ga.txt +110 -0
  59. data/solr_sample_core/conf/lang/stopwords_gl.txt +161 -0
  60. data/solr_sample_core/conf/lang/stopwords_hi.txt +235 -0
  61. data/solr_sample_core/conf/lang/stopwords_hu.txt +211 -0
  62. data/solr_sample_core/conf/lang/stopwords_hy.txt +46 -0
  63. data/solr_sample_core/conf/lang/stopwords_id.txt +359 -0
  64. data/solr_sample_core/conf/lang/stopwords_it.txt +303 -0
  65. data/solr_sample_core/conf/lang/stopwords_ja.txt +127 -0
  66. data/solr_sample_core/conf/lang/stopwords_lv.txt +172 -0
  67. data/solr_sample_core/conf/lang/stopwords_nl.txt +119 -0
  68. data/solr_sample_core/conf/lang/stopwords_no.txt +194 -0
  69. data/solr_sample_core/conf/lang/stopwords_pt.txt +253 -0
  70. data/solr_sample_core/conf/lang/stopwords_ro.txt +233 -0
  71. data/solr_sample_core/conf/lang/stopwords_ru.txt +243 -0
  72. data/solr_sample_core/conf/lang/stopwords_sv.txt +133 -0
  73. data/solr_sample_core/conf/lang/stopwords_th.txt +119 -0
  74. data/solr_sample_core/conf/lang/stopwords_tr.txt +212 -0
  75. data/solr_sample_core/conf/lang/userdict_ja.txt +29 -0
  76. data/solr_sample_core/conf/mapping-FoldToASCII.txt +3813 -0
  77. data/solr_sample_core/conf/mapping-ISOLatin1Accent.txt +246 -0
  78. data/solr_sample_core/conf/protwords.txt +21 -0
  79. data/solr_sample_core/conf/schema.xml +62 -0
  80. data/solr_sample_core/conf/scripts.conf +24 -0
  81. data/solr_sample_core/conf/solrconfig.xml +1702 -0
  82. data/solr_sample_core/conf/spellings.txt +2 -0
  83. data/solr_sample_core/conf/stopwords.txt +14 -0
  84. data/solr_sample_core/conf/syn.txt +0 -0
  85. data/solr_sample_core/conf/synonyms.txt +29 -0
  86. data/solr_sample_core/conf/token_fixing_charfilter.txt +110 -0
  87. data/solr_sample_core/conf/update-script.js +53 -0
  88. data/solr_sample_core/conf/velocity/README.txt +101 -0
  89. data/solr_sample_core/conf/velocity/VM_global_library.vm +175 -0
  90. data/solr_sample_core/conf/velocity/browse.vm +33 -0
  91. data/solr_sample_core/conf/velocity/cluster.vm +19 -0
  92. data/solr_sample_core/conf/velocity/cluster_results.vm +31 -0
  93. data/solr_sample_core/conf/velocity/debug.vm +28 -0
  94. data/solr_sample_core/conf/velocity/did_you_mean.vm +9 -0
  95. data/solr_sample_core/conf/velocity/error.vm +11 -0
  96. data/solr_sample_core/conf/velocity/facet_fields.vm +23 -0
  97. data/solr_sample_core/conf/velocity/facet_pivot.vm +12 -0
  98. data/solr_sample_core/conf/velocity/facet_queries.vm +12 -0
  99. data/solr_sample_core/conf/velocity/facet_ranges.vm +23 -0
  100. data/solr_sample_core/conf/velocity/facets.vm +10 -0
  101. data/solr_sample_core/conf/velocity/footer.vm +43 -0
  102. data/solr_sample_core/conf/velocity/head.vm +35 -0
  103. data/solr_sample_core/conf/velocity/header.vm +7 -0
  104. data/solr_sample_core/conf/velocity/hit.vm +25 -0
  105. data/solr_sample_core/conf/velocity/hit_grouped.vm +43 -0
  106. data/solr_sample_core/conf/velocity/hit_plain.vm +25 -0
  107. data/solr_sample_core/conf/velocity/join_doc.vm +20 -0
  108. data/solr_sample_core/conf/velocity/jquery.autocomplete.css +48 -0
  109. data/solr_sample_core/conf/velocity/jquery.autocomplete.js +763 -0
  110. data/solr_sample_core/conf/velocity/layout.vm +24 -0
  111. data/solr_sample_core/conf/velocity/main.css +230 -0
  112. data/solr_sample_core/conf/velocity/mime_type_lists.vm +68 -0
  113. data/solr_sample_core/conf/velocity/pagination_bottom.vm +22 -0
  114. data/solr_sample_core/conf/velocity/pagination_top.vm +29 -0
  115. data/solr_sample_core/conf/velocity/product_doc.vm +32 -0
  116. data/solr_sample_core/conf/velocity/query.vm +42 -0
  117. data/solr_sample_core/conf/velocity/query_form.vm +64 -0
  118. data/solr_sample_core/conf/velocity/query_group.vm +43 -0
  119. data/solr_sample_core/conf/velocity/query_spatial.vm +75 -0
  120. data/solr_sample_core/conf/velocity/results_list.vm +22 -0
  121. data/solr_sample_core/conf/velocity/richtext_doc.vm +153 -0
  122. data/solr_sample_core/conf/velocity/suggest.vm +8 -0
  123. data/solr_sample_core/conf/velocity/tabs.vm +50 -0
  124. data/solr_sample_core/conf/xslt/example.xsl +132 -0
  125. data/solr_sample_core/conf/xslt/example_atom.xsl +67 -0
  126. data/solr_sample_core/conf/xslt/example_rss.xsl +66 -0
  127. data/solr_sample_core/conf/xslt/luke.xsl +337 -0
  128. data/solr_sample_core/conf/xslt/updateXml.xsl +70 -0
  129. data/spec/client_basics_spec.rb +26 -0
  130. data/spec/connect_spec.rb +25 -0
  131. data/spec/core_basics.rb +21 -0
  132. data/spec/index_spec.rb +31 -0
  133. data/spec/load_spec.rb +7 -0
  134. data/spec/minitest_helper.rb +36 -0
  135. data/spec/schema_spec.rb +113 -0
  136. metadata +284 -0
File without changes
@@ -0,0 +1,50 @@
1
+ # Pre-define the inheritance so Ruby doesn't complain
2
+ # on import.
3
+ require 'simple_solr/client'
4
+ require 'simple_solr/schema'
5
+ module SimpleSolrClient
6
+ class Core < Client
7
+ end
8
+ end
9
+
10
+
11
+ require 'simple_solr/core/admin'
12
+ require 'simple_solr/core/core_data'
13
+ require 'simple_solr/core/index'
14
+ require 'simple_solr/core/search'
15
+
16
+ class SimpleSolrClient::Core
17
+
18
+
19
+ include SimpleSolrClient::Core::Admin
20
+ include SimpleSolrClient::Core::CoreData
21
+ include SimpleSolrClient::Core::Index
22
+ include SimpleSolrClient::Core::Search
23
+
24
+
25
+ attr_reader :core
26
+ alias_method :name, :core
27
+
28
+ def initialize(url, core)
29
+ super(url)
30
+ @core = core
31
+ end
32
+
33
+
34
+ # Override #url so we're now talking to the core
35
+ def url(*args)
36
+ [@base_url, @core, *args].join('/').chomp('/')
37
+ end
38
+
39
+ # Send JSON to this core's update/json handler
40
+ def update(object_to_post, response_type = nil)
41
+ post_json('update/json', object_to_post, response_type)
42
+ end
43
+
44
+ def schema
45
+ @schema ||= SimpleSolrClient::Schema.new(self)
46
+ end
47
+
48
+ end
49
+
50
+
@@ -0,0 +1,47 @@
1
+ module SimpleSolrClient::Core::Admin
2
+ def ping
3
+ get('admin/ping')
4
+ end
5
+
6
+ # Is the server up (and responding to a ping?)
7
+ # @return [Boolean]
8
+ def up?
9
+ begin
10
+ ping.status == 'OK'
11
+ rescue
12
+ false
13
+ end
14
+ end
15
+
16
+ # Send a commit command
17
+ # @return self
18
+ def commit
19
+ update({'commit' => {}})
20
+ self
21
+ end
22
+
23
+ # Send an optimize command
24
+ # @return self
25
+ def optimize
26
+ update({"optimize" => {}})
27
+ self
28
+ end
29
+
30
+ # Reload the core (for when you've changed the schema, solrconfig, synonyms, etc.)
31
+ # Make sure to mark the schema as dirty!
32
+ # @return self
33
+ def reload
34
+ get('admin/cores', {:force_top_level_url => true, :core => core, :action => 'RELOAD'})
35
+ @schema = nil
36
+ self
37
+ end
38
+
39
+ # Unload the current core and delete all its files
40
+ # @return The Solr response
41
+ def unload
42
+ get('admin/cores', {:force_top_level_url => true, :core => core, :action => 'UNLOAD', :deleteInstanceDir => true})
43
+ end
44
+
45
+
46
+ end
47
+
@@ -0,0 +1,51 @@
1
+ module SimpleSolrClient::Core::CoreData
2
+ attr_reader :raw_solr_hash
3
+
4
+
5
+ # Get the core data for this core
6
+ # This is weird in that while the data is about a specific
7
+ # core, we need to call it at the client_url level; hence
8
+ # all the screwing around with force_top_level_url
9
+ #
10
+ # It would make sense to cache this until, say, a commit or a
11
+ # reload, but the added complexity isn't yet worth it.
12
+ def core_data_hash
13
+ cdata = get('admin/cores', {:force_top_level_url => true})
14
+ cdata['status'][core]
15
+ end
16
+
17
+ def index
18
+ core_data_hash['index']
19
+ end
20
+
21
+ def default?
22
+ core_data_hash['isDefaultCore']
23
+ end
24
+
25
+ def last_modified
26
+ Time.parse index['lastModified']
27
+ end
28
+
29
+ def number_of_documents
30
+ index['numDocs']
31
+ end
32
+
33
+ def data_dir
34
+ core_data_hash['dataDir']
35
+ end
36
+
37
+ def instance_dir
38
+ core_data_hash['instanceDir']
39
+ end
40
+
41
+ def config_file
42
+ File.join(instance_dir, 'conf', core_data_hash['config'])
43
+ end
44
+
45
+ def schema_file
46
+ File.join(instance_dir, 'conf', core_data_hash['schema'])
47
+ end
48
+
49
+ end
50
+
51
+
@@ -0,0 +1,25 @@
1
+ module SimpleSolrClient::Core::Index
2
+ # Add the given hash or array of hashes
3
+ # @return self
4
+ def add_docs(*hash_or_hashes)
5
+ update(hash_or_hashes.flatten)
6
+ self
7
+ end
8
+
9
+ # A raw delete. Your query needs to be legal (e.g., escaped) already
10
+ # @param [String] q The query to identify items to delete
11
+ # @return self
12
+ def delete(q)
13
+ update({:delete => {:query => q}})
14
+ self
15
+ end
16
+
17
+ # Delete all document in the index and immdiately commit
18
+ # @return self
19
+ def clear
20
+ delete('*:*').commit
21
+ self
22
+ end
23
+
24
+
25
+ end
@@ -0,0 +1,21 @@
1
+ require 'simple_solr/response/query_response'
2
+
3
+ module SimpleSolrClient::Core::Search
4
+
5
+ def fv_search(field, value)
6
+ v = value
7
+ v = SimpleSolrClient.lucene_escape Array(value).join(' ') unless v == '*'
8
+ kv = "#{field}:(#{v})"
9
+ get('select', {:q => kv}, SimpleSolrClient::Response::QueryResponse)
10
+ end
11
+
12
+ def all
13
+ fv_search('*', '*')
14
+ end
15
+
16
+ def id(i)
17
+ fv_search('id', i).first
18
+ end
19
+
20
+
21
+ end
@@ -0,0 +1,45 @@
1
+ class SimpleSolrClient::Response::Document
2
+ extend Forwardable
3
+ include Comparable
4
+
5
+ attr_accessor :rank
6
+
7
+ def <=>(other)
8
+ other = other.score if other.respond_to? :score,
9
+ self.score <=> other
10
+ end
11
+
12
+ # @!method [](key)
13
+ # @param [String] key The name of the stored field
14
+ # @return [String, Array<String>] the value(s) of the requested field
15
+ def_delegators :@solr_doc_hash, :[], :keys, :values, :to_s
16
+
17
+ # !@attribute solr_doc_hash
18
+ # @return [Hash] the original, un-munged solr data for this document passed into the initializer
19
+ attr_accessor :solr_doc_hash, :rank
20
+
21
+
22
+ # Create a single document from a Hash representation of the solr return value
23
+ # @param solrdochash [Hash] The ruby-hash representation of a Solr document
24
+ # as returned by a solr query
25
+ def initialize(solrdochash)
26
+ @solr_doc_hash = solrdochash
27
+ end
28
+
29
+
30
+ # The value of the 'id' field of this document
31
+ def id
32
+ @solr_doc_hash['id']
33
+ end
34
+
35
+ # The score of this document on thsi query
36
+ def score
37
+ @solr_doc_hash['score']
38
+ end
39
+
40
+
41
+ def to_h
42
+ @solr_doc_hash.merge({'_rank' => @rank})
43
+ end
44
+
45
+ end
@@ -0,0 +1,19 @@
1
+ require 'forwardable'
2
+
3
+ module SimpleSolrClient
4
+ module Response
5
+ class GenericResponse
6
+ extend Forwardable
7
+ def_delegators :@solr_response, :[]
8
+
9
+ def initialize(solr_response_hash)
10
+ @solr_response = solr_response_hash
11
+ end
12
+
13
+ def status
14
+ @solr_response['status']
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ require 'simple_solr/response/generic_response'
2
+ require 'simple_solr/response/document'
3
+
4
+ class SimpleSolrClient::Response::QueryResponse < SimpleSolrClient::Response::GenericResponse
5
+ extend Forwardable
6
+ include Enumerable
7
+
8
+ attr_reader :num_found, :docs, :first_index, :docs, :params, :page
9
+
10
+ def_delegators :@docs, :each, :count, :size
11
+ def_delegators :@indexed_docs, :[]
12
+
13
+ def initialize(solr_response)
14
+ super
15
+ resp = @solr_response['response']
16
+ @num_found = resp['numFound']
17
+ @first_index = resp['start'] + 1
18
+
19
+ @docs = []
20
+ @indexed_docs = {}
21
+ resp['docs'].each_with_index do |d, i|
22
+ doc_rank = i + @first_index
23
+ doc = SimpleSolrClient::Response::Document.new(d)
24
+ doc.rank = doc_rank
25
+ @docs << doc
26
+ @indexed_docs[doc.id] = doc
27
+ end
28
+ end
29
+
30
+ def last_index
31
+ @first_index + @num_found
32
+ end
33
+
34
+ def rank(id)
35
+ @indexed_docs[id.to_s].rank
36
+ end
37
+
38
+ def score(id)
39
+ @indexed_docs[id.to_s].score
40
+ end
41
+
42
+ # @return [Boolean] True if there are no documents
43
+ def empty?
44
+ @docs.empty?
45
+ end
46
+
47
+
48
+ def each_with_rank
49
+ return self.enum_for(:each_with_rank) unless block_given?
50
+ @docs.each { |x| yield x, x.rank }
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,261 @@
1
+ require 'nokogiri'
2
+
3
+ require 'simple_solr/schema/matcher'
4
+ require 'simple_solr/schema/copyfield'
5
+ require 'simple_solr/schema/field'
6
+ require 'simple_solr/schema/dynamic_field'
7
+ require 'simple_solr/schema/field_type'
8
+
9
+ class SimpleSolrClient::Schema
10
+ # A simplistic representation of a schema
11
+
12
+
13
+ attr_reader :xmldoc
14
+
15
+ def initialize(core)
16
+ @core = core
17
+ @fields = {}
18
+ @dynamic_fields = {}
19
+ @copy_fields = Hash.new { |h, k| h[k] = [] }
20
+ @field_types = {}
21
+ self.load
22
+ end
23
+
24
+
25
+ def fields
26
+ @fields.values.map { |x| x.resolve_type(self) }
27
+ end
28
+
29
+ def field(n)
30
+ @fields[n].resolve_type(self)
31
+ end
32
+
33
+ def dynamic_fields
34
+ @dynamic_fields.values.map { |x| x.resolve_type(self) }
35
+ end
36
+
37
+ def dynamic_field(n)
38
+ @dynamic_fields[n].resolve_type(self)
39
+ end
40
+
41
+ def copy_fields_for(n)
42
+ @copy_fields[n]
43
+ end
44
+
45
+ def copy_fields
46
+ @copy_fields.values.flatten
47
+ end
48
+
49
+ def add_field(f)
50
+ @fields[f.name] = f
51
+ field(f.name)
52
+ end
53
+
54
+ def drop_field(str)
55
+ @fields.delete(str)
56
+ self
57
+ end
58
+
59
+
60
+ def field_types
61
+ @field_types.values
62
+ end
63
+
64
+ def field_type(k)
65
+ @field_types[k]
66
+ end
67
+
68
+
69
+ # When we add dynamic fields, we need to keep them sorted by
70
+ # length of the key, since that's how they match
71
+ def add_dynamic_field(f)
72
+ raise "Dynamic field should be dynamic and have a '*' in it somewhere; '#{f.name}' does not" unless f.name =~ /\*/
73
+ @dynamic_fields[f.name] = f
74
+
75
+ @dynamic_fields = @dynamic_fields.sort { |a, b| b[0].size <=> a[0].size }.to_h
76
+
77
+ end
78
+
79
+ def drop_dynamic_field(str)
80
+ @dynamic_fields.delete(str)
81
+ self
82
+ end
83
+
84
+ def add_copy_field(f)
85
+ cf = @copy_fields[f.source]
86
+ cf << f
87
+ end
88
+
89
+ def drop_copy_field(str)
90
+ @copy_fields.delete(str)
91
+ self
92
+ end
93
+
94
+ def add_field_type(ft)
95
+ ft.core = @core
96
+ @field_types[ft.name] = ft
97
+ end
98
+
99
+ def drop_field_type(str)
100
+ @field_types.delete(str)
101
+ self
102
+ end
103
+
104
+
105
+ # For loading, we get the information about the fields via the API,
106
+ # but grab an XML document for modifying/writing
107
+ def load
108
+ @xmldoc = Nokogiri.XML(@core.raw_get_content('admin/file', {:file => 'schema.xml'})) do |config|
109
+ config.noent
110
+ end
111
+ load_explicit_fields
112
+ load_dynamic_fields
113
+ load_copy_fields
114
+ load_field_types
115
+ end
116
+
117
+
118
+ def load_explicit_fields
119
+ @fields = {}
120
+ @core.get('schema/fields')['fields'].each do |field_hash|
121
+ add_field(Field.new_from_solr_hash(field_hash))
122
+ end
123
+ end
124
+
125
+ def load_dynamic_fields
126
+ @dynamic_fields = {}
127
+ @core.get('schema/dynamicfields')['dynamicFields'].each do |field_hash|
128
+ f = DynamicField.new_from_solr_hash(field_hash)
129
+ if @dynamic_fields[f.name]
130
+ raise "Dynamic field '#{f.name}' defined more than once"
131
+ end
132
+ add_dynamic_field(f)
133
+ end
134
+ end
135
+
136
+ def load_copy_fields
137
+ @copy_fields = Hash.new { |h, k| h[k] = [] }
138
+ @core.get('schema/copyfields')['copyFields'].each do |cfield_hash|
139
+ add_copy_field(CopyField.new(cfield_hash['source'], cfield_hash['dest']))
140
+ end
141
+ end
142
+
143
+ def load_field_types
144
+ @field_types = {}
145
+ @core.get('schema/fieldtypes')['fieldTypes'].each do |fthash|
146
+ ft = FieldType.new_from_solr_hash(fthash)
147
+ type_name = ft.name
148
+ attr = "[@name=\"#{type_name}\"]"
149
+ node = @xmldoc.css("fieldType#{attr}").first || @xmldoc.css("fieldtype#{attr}").first
150
+ unless node
151
+ puts "Failed for type #{type_name}"
152
+ end
153
+ ft.xml = node.to_xml
154
+ add_field_type(ft)
155
+ end
156
+ end
157
+
158
+ def clean_schema_xml
159
+ d = @xmldoc.dup
160
+ d.xpath('//comment()').remove
161
+ d.css('field').remove
162
+ d.css('fieldType').remove
163
+ d.css('fieldtype').remove
164
+ d.css('dynamicField').remove
165
+ d.css('copyField').remove
166
+ d.css('dynamicfield').remove
167
+ d.css('copyfield').remove
168
+ d.css('schema').children.find_all { |x| x.name == 'text' }.each { |x| x.remove }
169
+ d
170
+ end
171
+
172
+ def to_xml
173
+ # Get a clean schema XML document
174
+ d = clean_schema_xml
175
+ s = d.css('schema').first
176
+ [fields, dynamic_fields, copy_fields, field_types].flatten.each do |f|
177
+ s.add_child f.to_xml_node
178
+ end
179
+ d.to_xml
180
+ end
181
+
182
+
183
+ def write
184
+ File.open(@core.schema_file, 'w:utf-8') do |out|
185
+ out.puts self.to_xml
186
+ end
187
+ end
188
+
189
+ def reload
190
+ @core.reload
191
+ end
192
+
193
+
194
+ # Figuring out which fields are actually produced can be hard:
195
+ # * If a non-dynamic field name matches, no dynamic_fields will match
196
+ # * The result of a copyField may match another dynamicField, but the
197
+ # result of *that* will not match more copyFields
198
+ # * dynamicFields are matched longest to shortest
199
+ #
200
+ # Suppose I have the following:
201
+ # dynamic *_ts => string
202
+ # dynamic *_t => string
203
+ # dynamic *_s => string
204
+ # dynamic *_ddd => string
205
+ #
206
+ # copy *_ts => *_t
207
+ # copy *_ts => *_s
208
+ # copy *_s => *_ddd
209
+ #
210
+ # You might expect:
211
+ # name_ts => string
212
+ # name_ts copied to name_t => string
213
+ # name_ts copied to name_s => string
214
+ # name_s copied to name_ddd => string
215
+ #
216
+ # ...giving us name_ts, name_t, name_s, and name_ddd
217
+ #
218
+ # What you'll find is that we don't get name_ddd, since
219
+ # name_s was generated by a wildcard-enabled copyField
220
+ # and that's where things stop.
221
+ #
222
+ # However, if you explicitly add a field called
223
+ # name_s, it *will* get copied to name_ddd.
224
+ #
225
+ # Yeah. It's confusing.
226
+
227
+
228
+ def first_matching_field(str)
229
+ f = fields.find { |x| x.matches str } or first_matching_dfield(str)
230
+ end
231
+
232
+ def first_matching_dfield(str)
233
+ df = dynamic_fields.find { |x| x.matches str }
234
+ if df
235
+ f = Field.new(df.to_h)
236
+ f[:name] = df.dynamic_name str
237
+ end
238
+ f
239
+
240
+ end
241
+
242
+ def resulting_fields(str)
243
+ rv = []
244
+ f = first_matching_field(str)
245
+ rv << f
246
+ copy_fields.each do |cf|
247
+ if cf.matches(f.name)
248
+ dname = cf.dynamic_name(f.name)
249
+ fmf = Field.new(first_matching_field(dname).to_h)
250
+ fmf[:name] = dname
251
+ rv << fmf
252
+ end
253
+ end
254
+ rv.uniq
255
+ end
256
+
257
+ end
258
+
259
+
260
+
261
+