gcloud 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +8 -0
  3. data/lib/gcloud.rb +48 -30
  4. data/lib/gcloud/bigquery.rb +4 -6
  5. data/lib/gcloud/bigquery/connection.rb +2 -14
  6. data/lib/gcloud/bigquery/dataset.rb +41 -42
  7. data/lib/gcloud/bigquery/project.rb +50 -46
  8. data/lib/gcloud/bigquery/query_job.rb +7 -8
  9. data/lib/gcloud/bigquery/table.rb +54 -55
  10. data/lib/gcloud/bigquery/table/schema.rb +30 -40
  11. data/lib/gcloud/bigquery/view.rb +10 -11
  12. data/lib/gcloud/credentials.rb +19 -25
  13. data/lib/gcloud/datastore.rb +4 -6
  14. data/lib/gcloud/datastore/dataset.rb +3 -5
  15. data/lib/gcloud/dns.rb +4 -6
  16. data/lib/gcloud/dns/connection.rb +17 -16
  17. data/lib/gcloud/dns/importer.rb +5 -11
  18. data/lib/gcloud/dns/project.rb +11 -12
  19. data/lib/gcloud/dns/zone.rb +52 -92
  20. data/lib/gcloud/dns/zone/transaction.rb +2 -2
  21. data/lib/gcloud/pubsub.rb +4 -6
  22. data/lib/gcloud/pubsub/connection.rb +1 -12
  23. data/lib/gcloud/pubsub/project.rb +30 -36
  24. data/lib/gcloud/pubsub/subscription.rb +18 -26
  25. data/lib/gcloud/pubsub/topic.rb +16 -26
  26. data/lib/gcloud/resource_manager.rb +5 -6
  27. data/lib/gcloud/resource_manager/connection.rb +4 -4
  28. data/lib/gcloud/resource_manager/manager.rb +10 -14
  29. data/lib/gcloud/resource_manager/project.rb +3 -5
  30. data/lib/gcloud/search.rb +295 -0
  31. data/lib/gcloud/search/api_client.rb +144 -0
  32. data/lib/gcloud/search/connection.rb +146 -0
  33. data/lib/gcloud/search/credentials.rb +30 -0
  34. data/lib/gcloud/search/document.rb +301 -0
  35. data/lib/gcloud/search/document/list.rb +85 -0
  36. data/lib/gcloud/search/errors.rb +67 -0
  37. data/lib/gcloud/search/field_value.rb +164 -0
  38. data/lib/gcloud/search/field_values.rb +263 -0
  39. data/lib/gcloud/search/fields.rb +267 -0
  40. data/lib/gcloud/search/index.rb +613 -0
  41. data/lib/gcloud/search/index/list.rb +90 -0
  42. data/lib/gcloud/search/project.rb +197 -0
  43. data/lib/gcloud/search/result.rb +169 -0
  44. data/lib/gcloud/search/result/list.rb +95 -0
  45. data/lib/gcloud/storage.rb +4 -6
  46. data/lib/gcloud/storage/bucket.rb +55 -43
  47. data/lib/gcloud/storage/bucket/cors.rb +5 -7
  48. data/lib/gcloud/storage/file.rb +35 -30
  49. data/lib/gcloud/storage/file/acl.rb +12 -16
  50. data/lib/gcloud/storage/project.rb +56 -22
  51. data/lib/gcloud/version.rb +1 -1
  52. metadata +20 -3
@@ -0,0 +1,267 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "gcloud/search/field_values"
17
+ require "gcloud/search/field_value"
18
+
19
+ module Gcloud
20
+ module Search
21
+ ##
22
+ # = Fields
23
+ #
24
+ # Fields is the object that provides access to a document's fields.
25
+ #
26
+ # Each field has a name (String) and a list of values. Each field name
27
+ # consists of only ASCII characters, must be unique within the document and
28
+ # is case sensitive. A field name must start with a letter and can contain
29
+ # letters, digits, or underscore, with a maximum of 500 characters.
30
+ #
31
+ # A field can have multiple values with same or different types; however, it
32
+ # cannot have multiple datetime (DateTime) or number (Float) values. (See
33
+ # FieldValues and FieldValue)
34
+ #
35
+ # require "gcloud"
36
+ #
37
+ # gcloud = Gcloud.new
38
+ # search = gcloud.search
39
+ # index = search.index "products"
40
+ #
41
+ # document = index.document "product-sku-000001"
42
+ # puts "The document #{document.doc_id} has the following fields:"
43
+ # document.names.each do |name|
44
+ # puts "* #{name}:"
45
+ # document[name].each do |value|
46
+ # puts " * #{value} (#{value.type})"
47
+ # end
48
+ # end
49
+ #
50
+ # For more information see {Documents and
51
+ # fields}[https://cloud.google.com/search/documents_indexes].
52
+ #
53
+ class Fields
54
+ include Enumerable
55
+
56
+ ##
57
+ # Create a new empty fields object.
58
+ def initialize #:nodoc:
59
+ @hash = {}
60
+ end
61
+
62
+ ##
63
+ # Retrieve the field values associated to a field name.
64
+ #
65
+ # === Parameters
66
+ #
67
+ # +name+::
68
+ # The name of the field. New values will be configured with this name.
69
+ # (+String+)
70
+ #
71
+ # === Returns
72
+ #
73
+ # FieldValues
74
+ #
75
+ # === Example
76
+ #
77
+ # require "gcloud"
78
+ #
79
+ # gcloud = Gcloud.new
80
+ # search = gcloud.search
81
+ # index = search.index "products"
82
+ #
83
+ # document = index.document "product-sku-000001"
84
+ # puts "The document description is:"
85
+ # document.fields["description"].each do |value|
86
+ # puts "* #{value} (#{value.type}) [#{value.lang}]"
87
+ # end
88
+ #
89
+ def [] name
90
+ @hash[name] ||= FieldValues.new name
91
+ end
92
+
93
+ # rubocop:disable Metrics/LineLength
94
+ # Disabled because there are links in the docs that are long.
95
+
96
+ ##
97
+ # Add a new value. If the field name does not exist it will be added. If
98
+ # the field value is a DateTime or Numeric, or the type is set to
99
+ # +:datetime+ or +:number+, then the added value will replace any existing
100
+ # values of the same type (since there can be only one).
101
+ #
102
+ # === Parameters
103
+ #
104
+ # +name+::
105
+ # The name of the field. (+String+)
106
+ # +value+::
107
+ # The value to add to the field. (+String+ or +Datetime+ or +Float+)
108
+ # +type+::
109
+ # The type of the field value. An attempt is made to set the correct
110
+ # type when this option is missing, although it must be provided for
111
+ # +:geo+ values. A field can have multiple values with same or different
112
+ # types; however, it cannot have multiple +:datetime+ or +:number+
113
+ # values. (+Symbol+)
114
+ #
115
+ # The following values are supported:
116
+ # * +:default+ - The value is a string. The format will be automatically
117
+ # detected. This is the default value for strings.
118
+ # * +:text+ - The value is a string with maximum length 1024**2
119
+ # characters.
120
+ # * +:html+ - The value is an HTML-formatted string with maximum length
121
+ # 1024**2 characters.
122
+ # * +:atom+ - The value is a string with maximum length 500 characters.
123
+ # * +:geo+ - The value is a point on earth described by latitude and
124
+ # longitude coordinates, represented in string with any of the listed
125
+ # {ways of writing
126
+ # coordinates}[http://en.wikipedia.org/wiki/Geographic_coordinate_conversion].
127
+ # * +:datetime+ - The value is a +DateTime+.
128
+ # * +:number+ - The value is a +Numeric+ between -2,147,483,647 and
129
+ # 2,147,483,647. The value will be stored as a double precision
130
+ # floating point value in Cloud Search.
131
+ # +lang+::
132
+ # The language of a string value. Must be a valid {ISO 639-1
133
+ # code}[https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes].
134
+ # (+String+)
135
+ #
136
+ # === Example
137
+ #
138
+ # require "gcloud"
139
+ #
140
+ # gcloud = Gcloud.new
141
+ # search = gcloud.search
142
+ # index = search.index "products"
143
+ #
144
+ # document = index.document "product-sku-000001"
145
+ # document.fields.add "sku", "product-sku-000001", type: :atom
146
+ # document.fields.add "description", "The best T-shirt ever.",
147
+ # type: :text, lang: "en"
148
+ # document.fields.add "description", "<p>The best T-shirt ever.</p>",
149
+ # type: :html, lang: "en"
150
+ # document.fields.add "price", 24.95
151
+ #
152
+ def add name, value, type: nil, lang: nil
153
+ @hash[name] ||= FieldValues.new name
154
+ @hash[name].add value, type: type, lang: lang
155
+ end
156
+
157
+ # rubocop:enable Metrics/LineLength
158
+
159
+ ##
160
+ # Deletes a field and all values.
161
+ #
162
+ # === Parameters
163
+ #
164
+ # +name+::
165
+ # The name of the field. (+String+)
166
+ #
167
+ # === Example
168
+ #
169
+ # require "gcloud"
170
+ #
171
+ # gcloud = Gcloud.new
172
+ # search = gcloud.search
173
+ # index = search.index "products"
174
+ #
175
+ # document = index.document "product-sku-000001"
176
+ # document.fields.delete "description"
177
+ #
178
+ def delete name, &block
179
+ @hash.delete name, &block
180
+ end
181
+
182
+ ##
183
+ # Calls block once for each field, passing the field name and values pair
184
+ # as parameters. If no block is given an enumerator is returned instead.
185
+ #
186
+ # === Example
187
+ #
188
+ # require "gcloud"
189
+ #
190
+ # gcloud = Gcloud.new
191
+ # search = gcloud.search
192
+ # index = search.index "products"
193
+ #
194
+ # document = index.document "product-sku-000001"
195
+ # puts "The document #{document.doc_id} has the following fields:"
196
+ # document.fields.each do |name, values|
197
+ # puts "* #{name}:"
198
+ # values.each do |value|
199
+ # puts " * #{value} (#{value.type})"
200
+ # end
201
+ # end
202
+ #
203
+ def each &block
204
+ # Only yield fields that have values.
205
+ fields_with_values.each(&block)
206
+ end
207
+
208
+ ##
209
+ # Returns a new array populated with all the field names.
210
+ #
211
+ # require "gcloud"
212
+ #
213
+ # gcloud = Gcloud.new
214
+ # search = gcloud.search
215
+ # index = search.index "products"
216
+ #
217
+ # document = index.document "product-sku-000001"
218
+ # puts "The document #{document.doc_id} has the following fields:"
219
+ # document.fields.names.each do |name|
220
+ # puts "* #{name}:"
221
+ # end
222
+ #
223
+ def names
224
+ # Only return fields that have values.
225
+ fields_with_values.keys
226
+ end
227
+
228
+ ##
229
+ # Create a new Fields instance from a raw Hash.
230
+ def self.from_raw raw #:nodoc:
231
+ hsh = {}
232
+ raw.each do |k, v|
233
+ hsh[k] = FieldValues.from_raw k, v["values"]
234
+ end unless raw.nil?
235
+ fields = new
236
+ fields.instance_variable_set "@hash", hsh
237
+ fields
238
+ end
239
+
240
+ ##
241
+ # Create a raw Hash object containing all the field names and values.
242
+ def to_raw #:nodoc:
243
+ hsh = {}
244
+ @hash.each do |k, v|
245
+ hsh[k] = v.to_raw unless v.empty?
246
+ end
247
+ hsh
248
+ end
249
+
250
+ protected
251
+
252
+ ##
253
+ # Find all the fields that have values. This is needed because a field is
254
+ # required to have at least one value.
255
+ #
256
+ # Users can remove all values, and the empty FieldValues object will
257
+ # remain in the internal hash. This is the same as not having that field.
258
+ #
259
+ # Users can also reference the field by name before adding a value. So we
260
+ # have multiple valid use cases which add an empty FieldValues object to
261
+ # the hash.
262
+ def fields_with_values #:nodoc:
263
+ @hash.select { |_name, values| values.any? }
264
+ end
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,613 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "gcloud/search/document"
17
+ require "gcloud/search/index/list"
18
+ require "gcloud/search/result"
19
+
20
+ module Gcloud
21
+ module Search
22
+ ##
23
+ # = Index
24
+ #
25
+ # An index manages Document instances for retrieval. Indexes cannot be
26
+ # created, updated, or deleted directly on the server: They are derived from
27
+ # the documents that reference them. You can manage groups of documents by
28
+ # putting them into separate indexes.
29
+ #
30
+ # With an index, you can retrieve documents with #find and #documents;
31
+ # manage them with #document, #save, and #remove; and perform searches over
32
+ # their fields with #search.
33
+ #
34
+ # require "gcloud"
35
+ #
36
+ # gcloud = Gcloud.new
37
+ # search = gcloud.search
38
+ # index = search.index "books"
39
+ #
40
+ # results = index.search "dark stormy"
41
+ # results.each do |result|
42
+ # puts result.doc_id
43
+ # end
44
+ #
45
+ # For more information, see {Documents and
46
+ # Indexes}[https://cloud.google.com/search/documents_indexes].
47
+ #
48
+ class Index
49
+ ##
50
+ # The Connection object.
51
+ attr_accessor :connection #:nodoc:
52
+
53
+ ##
54
+ # The raw data object.
55
+ attr_accessor :raw #:nodoc:
56
+
57
+ ##
58
+ # Creates a new Index instance.
59
+ #
60
+ def initialize #:nodoc:
61
+ @connection = nil
62
+ @raw = nil
63
+ end
64
+
65
+ ##
66
+ # The index identifier. May be defined by the server or by the client.
67
+ # Must be unique within the project. It cannot be an empty string. It must
68
+ # contain only visible, printable ASCII characters (ASCII codes 33 through
69
+ # 126 inclusive) and be no longer than 100 characters. It cannot begin
70
+ # with an exclamation point (<code>!</code>), and it cannot begin and end
71
+ # with double underscores (<code>__</code>).
72
+ def index_id
73
+ @raw["indexId"]
74
+ end
75
+
76
+ ##
77
+ # The names of fields in which TEXT values are stored. See {Index schemas
78
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
79
+ def text_fields
80
+ return @raw["indexedField"]["textFields"] if @raw["indexedField"]
81
+ []
82
+ end
83
+
84
+ ##
85
+ # The names of fields in which HTML values are stored. See {Index schemas
86
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
87
+ def html_fields
88
+ return @raw["indexedField"]["htmlFields"] if @raw["indexedField"]
89
+ []
90
+ end
91
+
92
+ ##
93
+ # The names of fields in which ATOM values are stored. See {Index schemas
94
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
95
+ def atom_fields
96
+ return @raw["indexedField"]["atomFields"] if @raw["indexedField"]
97
+ []
98
+ end
99
+
100
+ ##
101
+ # The names of fields in which DATE values are stored. See {Index schemas
102
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
103
+ def datetime_fields
104
+ return @raw["indexedField"]["dateFields"] if @raw["indexedField"]
105
+ []
106
+ end
107
+
108
+ ##
109
+ # The names of fields in which NUMBER values are stored. See {Indexschemas
110
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
111
+ def number_fields
112
+ return @raw["indexedField"]["numberFields"] if @raw["indexedField"]
113
+ []
114
+ end
115
+
116
+ ##
117
+ # The names of fields in which GEO values are stored. See {Index
118
+ # }[https://cloud.google.com/search/documents_indexes#index_schemas].
119
+ def geo_fields
120
+ return @raw["indexedField"]["geoFields"] if @raw["indexedField"]
121
+ []
122
+ end
123
+
124
+ ##
125
+ # The names of all the fields that are stored on the index.
126
+ def field_names
127
+ (text_fields + html_fields + atom_fields + datetime_fields +
128
+ number_fields + geo_fields).uniq
129
+ end
130
+
131
+ ##
132
+ # The field value types that are stored on the field name.
133
+ def field_types_for name
134
+ {
135
+ text: text_fields.include?(name),
136
+ html: html_fields.include?(name),
137
+ atom: atom_fields.include?(name),
138
+ datetime: datetime_fields.include?(name),
139
+ number: number_fields.include?(name),
140
+ geo: geo_fields.include?(name)
141
+ }.delete_if { |_k, v| !v }.keys
142
+ end
143
+
144
+ ##
145
+ # Retrieves an existing document by id.
146
+ #
147
+ # === Parameters
148
+ #
149
+ # +doc_id+::
150
+ # The id of a document or a Document instance. (+String+ or Document)
151
+ #
152
+ # === Returns
153
+ #
154
+ # Gcloud::Search::Document or +nil+ if the document does not exist
155
+ #
156
+ # === Example
157
+ #
158
+ # require "gcloud"
159
+ #
160
+ # gcloud = Gcloud.new
161
+ # search = gcloud.search
162
+ # index = search.index "products"
163
+ #
164
+ # document = index.find "product-sku-000001"
165
+ # puts document.doc_id
166
+ #
167
+ def find doc_id
168
+ # Get the id if passes a Document object
169
+ doc_id = doc_id.doc_id if doc_id.respond_to? :doc_id
170
+ ensure_connection!
171
+ resp = connection.get_doc index_id, doc_id
172
+ return Document.from_hash(JSON.parse(resp.body)) if resp.success?
173
+ return nil if resp.status == 404
174
+ fail ApiError.from_response(resp)
175
+ rescue JSON::ParserError
176
+ raise ApiError.from_response(resp)
177
+ end
178
+ alias_method :get, :find
179
+
180
+ ##
181
+ # Helper for creating a new Document instance. The returned instance is
182
+ # local: It is either not yet saved to the service (see #save), or if it
183
+ # has been given the id of an existing document, it is not yet populated
184
+ # with the document's data (see #find).
185
+ #
186
+ # === Parameters
187
+ #
188
+ # +doc_id+::
189
+ # The unique identifier of the new document. This is optional. When the
190
+ # document is saved, this value must contain only visible, printable
191
+ # ASCII characters (ASCII codes 33 through 126 inclusive) and be no
192
+ # longer than 500 characters. It cannot begin with an exclamation point
193
+ # (<code>!</code>), and it cannot begin and end with double underscores
194
+ # (<code>__</code>). (+String+)
195
+ # +rank+::
196
+ # The rank of the new document. This is optional. A positive integer
197
+ # which determines the default ordering of documents returned from a
198
+ # search. It is a bad idea to assign the same rank to many documents,
199
+ # and the same rank should never be assigned to more than 10,000
200
+ # documents. By default (when it is not specified or set to 0), it is
201
+ # set at the time the document is saved to the number of seconds since
202
+ # January 1, 2011. The rank can be used in the +expressions+, +order+,
203
+ # and +fields+ options in #search, where it should referenced as
204
+ # +rank+. (+Integer+)
205
+ #
206
+ # === Returns
207
+ #
208
+ # Gcloud::Search::Document
209
+ #
210
+ # === Example
211
+ #
212
+ # require "gcloud"
213
+ #
214
+ # gcloud = Gcloud.new
215
+ # search = gcloud.search
216
+ # index = search.index "products"
217
+ #
218
+ # document = index.document "product-sku-000001"
219
+ # document.doc_id #=> nil
220
+ # document.rank #=> nil
221
+ #
222
+ # To check if an index already contains a document with the same id, pass
223
+ # the instance to #find:
224
+ #
225
+ # require "gcloud"
226
+ #
227
+ # gcloud = Gcloud.new
228
+ # search = gcloud.search
229
+ # index = search.index "products"
230
+ #
231
+ # document = index.document "product-sku-000001"
232
+ # document = index.find document # returns nil if not present
233
+ #
234
+ def document doc_id = nil, rank = nil
235
+ Document.new.tap do |d|
236
+ d.doc_id = doc_id
237
+ d.rank = rank
238
+ end
239
+ end
240
+
241
+ ##
242
+ # Retrieves the list of documents belonging to the index.
243
+ #
244
+ # === Parameters
245
+ #
246
+ # +token+::
247
+ # A previously-returned page token representing part of the larger set
248
+ # of results to view. (+String+)
249
+ # +max+::
250
+ # Maximum number of documents to return. The default is +100+.
251
+ # (+Integer+)
252
+ #
253
+ # === Returns
254
+ #
255
+ # Array of Gcloud::Search::Document (See Gcloud::Search::Document::List)
256
+ #
257
+ # === Examples
258
+ #
259
+ # require "gcloud"
260
+ #
261
+ # gcloud = Gcloud.new
262
+ # search = gcloud.search
263
+ # index = search.index "products"
264
+ #
265
+ # documents = index.documents
266
+ # documents.each do |index|
267
+ # puts index.index_id
268
+ # end
269
+ #
270
+ # If you have a significant number of documents, you may need to paginate
271
+ # through them: (See Gcloud::Search::Document::List)
272
+ #
273
+ # require "gcloud"
274
+ #
275
+ # gcloud = Gcloud.new
276
+ # search = gcloud.search
277
+ # index = search.index "products"
278
+ #
279
+ # documents = index.documents
280
+ # loop do
281
+ # documents.each do |index|
282
+ # puts index.index_id
283
+ # end
284
+ # break unless documents.next?
285
+ # documents = documents.next
286
+ # end
287
+ #
288
+ def documents token: nil, max: nil, view: nil
289
+ ensure_connection!
290
+ options = { token: token, max: max, view: view }
291
+ resp = connection.list_docs index_id, options
292
+ return Document::List.from_response(resp, self) if resp.success?
293
+ fail ApiError.from_response(resp)
294
+ end
295
+
296
+ ##
297
+ # Saves a new or existing document to the index. If the document instance
298
+ # is new and has been given an id (see #document), it will replace an
299
+ # existing document in the index that has the same unique id.
300
+ #
301
+ # === Parameters
302
+ #
303
+ # +document+::
304
+ # A Document instance, either new (see #document) or existing (see
305
+ # #find).
306
+ #
307
+ # === Returns
308
+ #
309
+ # Gcloud::Search::Document
310
+ #
311
+ # === Example
312
+ #
313
+ # require "gcloud"
314
+ #
315
+ # gcloud = Gcloud.new
316
+ # search = gcloud.search
317
+ # index = search.index "products"
318
+ #
319
+ # document = index.document "product-sku-000001"
320
+ # document.doc_id #=> nil
321
+ # document.rank #=> nil
322
+ #
323
+ # document = index.save document
324
+ # document.doc_id #=> "-2486020449015432113"
325
+ # document.rank #=> 154223228
326
+ #
327
+ def save document
328
+ ensure_connection!
329
+ resp = connection.create_doc index_id, document.to_hash
330
+ if resp.success?
331
+ raw = document.instance_variable_get "@raw"
332
+ raw.merge! JSON.parse(resp.body)
333
+ return document
334
+ end
335
+ fail ApiError.from_response(resp)
336
+ rescue JSON::ParserError
337
+ raise ApiError.from_response(resp)
338
+ end
339
+
340
+ ##
341
+ # Permanently deletes the document from the index.
342
+ #
343
+ # === Parameters
344
+ #
345
+ # +doc_id+::
346
+ # The id of the document. (+String+)
347
+ #
348
+ # === Returns
349
+ #
350
+ # +true+ if successful
351
+ #
352
+ # === Example
353
+ #
354
+ # require "gcloud"
355
+ #
356
+ # gcloud = Gcloud.new
357
+ # search = gcloud.search
358
+ # index = search.index "products"
359
+ #
360
+ # index.remove "product-sku-000001"
361
+ #
362
+ def remove doc_id
363
+ # Get the id if passes a Document object
364
+ doc_id = doc_id.doc_id if doc_id.respond_to? :doc_id
365
+ ensure_connection!
366
+ resp = connection.delete_doc index_id, doc_id
367
+ return true if resp.success?
368
+ fail ApiError.from_response(resp)
369
+ end
370
+
371
+ ##
372
+ # Permanently deletes the index by deleting its documents. (Indexes cannot
373
+ # be created, updated, or deleted directly on the server: They are derived
374
+ # from the documents that reference them.)
375
+ #
376
+ # === Parameters
377
+ #
378
+ # +force+::
379
+ # If +true+, ensures the deletion of the index by first deleting all
380
+ # documents. If +false+ and the index contains documents, the request
381
+ # will fail. Default is +false+. (+Boolean+)
382
+ #
383
+ # === Examples
384
+ #
385
+ # require "gcloud"
386
+ #
387
+ # gcloud = Gcloud.new
388
+ # search = gcloud.search
389
+ # index = search.index "books"
390
+ #
391
+ # An index containing documents can be forcefully deleted with the +force+
392
+ # option:
393
+ #
394
+ # require "gcloud"
395
+ #
396
+ # gcloud = Gcloud.new
397
+ # search = gcloud.search
398
+ # index = search.index "books"
399
+ # index.delete force: true
400
+ #
401
+ def delete force: false
402
+ ensure_connection!
403
+ docs_to_be_removed = documents view: "ID_ONLY"
404
+ return if docs_to_be_removed.empty?
405
+ unless force
406
+ fail "Unable to delete because documents exist. Use force option."
407
+ end
408
+ while docs_to_be_removed
409
+ docs_to_be_removed.each { |d| remove d }
410
+ if docs_to_be_removed.next?
411
+ docs_to_be_removed = documents token: docs_to_be_removed.token,
412
+ view: "ID_ONLY"
413
+ else
414
+ docs_to_be_removed = nil
415
+ end
416
+ end
417
+ end
418
+
419
+ ##
420
+ # New Index from a raw data object.
421
+ def self.from_raw raw, conn #:nodoc:
422
+ new.tap do |f|
423
+ f.raw = raw
424
+ f.connection = conn
425
+ end
426
+ end
427
+
428
+ # rubocop:disable Metrics/LineLength
429
+ # Disabled because there are links in the docs that are long.
430
+
431
+ ##
432
+ # Runs a search against the documents in the index using the provided
433
+ # query. For more information see the REST API documentation for
434
+ # {indexes.search}[https://cloud.google.com/search/reference/rest/v1/projects/indexes/search].
435
+ #
436
+ # === Parameters
437
+ #
438
+ # +query+::
439
+ # The query string in search query syntax. If the query is +nil+ or
440
+ # empty, all documents are returned. For more information see {Query
441
+ # Strings}[https://cloud.google.com/search/query]. (+String+)
442
+ # +expressions+::
443
+ # Customized expressions used in +order+ or +fields+. The expression can
444
+ # contain fields in Document, the built-in fields ( +rank+, the document
445
+ # +rank+, and +score+ if scoring is enabled) and fields defined in
446
+ # +expressions+. All field expressions expressed as a +Hash+ with the
447
+ # keys as the +name+ and the values as the +expression+. The expression
448
+ # value can be a combination of supported functions encoded in the
449
+ # string. Expressions involving number fields can use the arithmetical
450
+ # operators (+, -, *, /) and the built-in numeric functions (+max+,
451
+ # +min+, +pow+, +count+, +log+, +abs+). Expressions involving geopoint
452
+ # fields can use the +geopoint+ and +distance+ functions. Expressions
453
+ # for text and html fields can use the +snippet+ function. (+Hash+)
454
+ # +matched_count_accuracy+::
455
+ # Minimum accuracy requirement for Result::List#matched_count. If
456
+ # specified, +matched_count+ will be accurate to at least that number.
457
+ # For example, when set to 100, any <code>matched_count <= 100</code> is
458
+ # accurate. This option may add considerable latency/expense. By default
459
+ # (when it is not specified or set to 0), the accuracy is the same as
460
+ # +max+. (+Integer+)
461
+ # +offset+::
462
+ # Used to advance pagination to an arbitrary result, independent of the
463
+ # previous results. Offsets are an inefficient alternative to using
464
+ # +token+. (Both cannot be both set.) The default is 0.
465
+ # (+Integer+)
466
+ # +order+::
467
+ # A comma-separated list of fields for sorting on the search result,
468
+ # including fields from Document, the built-in fields (+rank+ and
469
+ # +score+), and fields defined in expressions. The default sorting
470
+ # order is ascending. To specify descending order for a field, a suffix
471
+ # <code>" desc"</code> should be appended to the field name. For
472
+ # example: <code>orderBy="foo desc,bar"</code>. The default value for
473
+ # text sort is the empty string, and the default value for numeric sort
474
+ # is 0. If not specified, the search results are automatically sorted by
475
+ # descending +rank+. Sorting by ascending +rank+ is not allowed.
476
+ # (+String+)
477
+ # +fields+::
478
+ # The fields to return in the Search::Result objects. These can be
479
+ # fields from Document, the built-in fields +rank+ and +score+, and
480
+ # fields defined in expressions. The default is to return all fields.
481
+ # (+String+ or +Array+ of +String+)
482
+ # +scorer+::
483
+ # The scoring function to invoke on a search result for this query. If
484
+ # scorer is not set, scoring is disabled and +score+ is 0 for all
485
+ # documents in the search result. To enable document relevancy score
486
+ # based on term frequency, set +scorer+ to +:generic+.
487
+ # (+String+ or +Symbol+)
488
+ # +scorer_size+::
489
+ # Maximum number of top retrieved results to score. It is valid only
490
+ # when +scorer+ is set. The default is 100. (+Integer+)
491
+ # +token+::
492
+ # A previously-returned page token representing part of the larger set
493
+ # of results to view. (+String+)
494
+ # +max+::
495
+ # Maximum number of results to return per page. (+Integer+)
496
+ #
497
+ # === Returns
498
+ #
499
+ # Array of Gcloud::Search::Result (See Gcloud::Search::Result::List)
500
+ #
501
+ # === Examples
502
+ #
503
+ # require "gcloud"
504
+ #
505
+ # gcloud = Gcloud.new
506
+ # search = gcloud.search
507
+ # index = search.index "books"
508
+ #
509
+ # results = index.search "dark stormy"
510
+ # results.each do |result|
511
+ # puts result.doc_id
512
+ # end
513
+ #
514
+ # If you have a significant number of search results, you may need to
515
+ # paginate through them: (See Gcloud::Search::Result::List)
516
+ #
517
+ # require "gcloud"
518
+ #
519
+ # gcloud = Gcloud.new
520
+ # search = gcloud.search
521
+ # index = search.index "books"
522
+ #
523
+ # results = index.results
524
+ # loop do
525
+ # results.each do |result|
526
+ # puts result.doc_id
527
+ # end
528
+ # break unless results.next?
529
+ # results = results.next
530
+ # end
531
+ #
532
+ # By default, Result objects are sorted by document rank. For more information
533
+ # see the {REST API documentation for Document.rank}[https://cloud.google.com/search/reference/rest/v1/projects/indexes/documents#resource_representation.google.cloudsearch.v1.Document.rank].
534
+ #
535
+ # You can specify how to sort results with the +order+ option. In the example
536
+ # below, the <code>-</code> character before +avg_review+ means that results
537
+ # will be sorted in ascending order by +published+ and then in descending
538
+ # order by +avg_review+.
539
+ #
540
+ # require "gcloud"
541
+ #
542
+ # gcloud = Gcloud.new
543
+ # search = gcloud.search
544
+ # index = search.index "books"
545
+ #
546
+ # results = index.search "dark stormy", order: "published, avg_review desc"
547
+ # documents = index.search query # API call
548
+ #
549
+ # You can add computed fields with the +expressions+ option, and limit the
550
+ # fields that are returned with the +fields+ option:
551
+ #
552
+ # require "gcloud"
553
+ #
554
+ # gcloud = Gcloud.new
555
+ # search = gcloud.search
556
+ # index = search.index "products"
557
+ #
558
+ # results = index.search "cotton T-shirt",
559
+ # expressions: { total_price: "(price + tax)" },
560
+ # fields: ["name", "total_price", "highlight"]
561
+ #
562
+ # Just as in documents, Result data is accessible via Fields methods:
563
+ #
564
+ # require "gcloud"
565
+ #
566
+ # gcloud = Gcloud.new
567
+ # search = gcloud.search
568
+ # index = search.index "products"
569
+ # document = index.find "product-sku-000001"
570
+ # results = index.search "cotton T-shirt"
571
+ # values = results[0]["description"]
572
+ #
573
+ # values[0] #=> "100% organic cotton ruby gem T-shirt"
574
+ # values[0].type #=> :text
575
+ # values[0].lang #=> "en"
576
+ # values[1] #=> "<p>100% organic cotton ruby gem T-shirt</p>"
577
+ # values[1].type #=> :html
578
+ # values[1].lang #=> "en"
579
+ #
580
+ def search query, expressions: nil, matched_count_accuracy: nil,
581
+ offset: nil, order: nil, fields: nil, scorer: nil,
582
+ scorer_size: nil, token: nil, max: nil
583
+ ensure_connection!
584
+ options = { expressions: format_expressions(expressions),
585
+ matched_count_accuracy: matched_count_accuracy,
586
+ offset: offset, order: order, fields: fields,
587
+ scorer: scorer, scorer_size: scorer_size, token: token,
588
+ max: max }
589
+ resp = connection.search index_id, query, options
590
+ if resp.success?
591
+ Result::List.from_response resp, self, query, options
592
+ else
593
+ fail ApiError.from_response(resp)
594
+ end
595
+ end
596
+
597
+ # rubocop:enable Metrics/LineLength
598
+
599
+ protected
600
+
601
+ ##
602
+ # Raise an error unless an active connection is available.
603
+ def ensure_connection!
604
+ fail "Must have active connection" unless connection
605
+ end
606
+
607
+ def format_expressions expressions
608
+ return nil if expressions.nil?
609
+ expressions.to_h.map { |k, v| { name: k, expression: v } }
610
+ end
611
+ end
612
+ end
613
+ end