contentstack 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql-analysis.yml +68 -68
  3. data/.github/workflows/jira.yml +28 -0
  4. data/.github/workflows/release-gem.yml +31 -0
  5. data/.github/workflows/sast-scan.yml +10 -10
  6. data/.github/workflows/sca-scan.yml +15 -13
  7. data/.github/workflows/secrets-scan.yml +10 -10
  8. data/.gitignore +11 -11
  9. data/.yardopts +6 -6
  10. data/CHANGELOG.md +108 -78
  11. data/CODEOWNERS +1 -1
  12. data/CODE_OF_CONDUCT.md +73 -73
  13. data/Gemfile +2 -2
  14. data/Gemfile.lock +29 -24
  15. data/LICENSE.txt +21 -21
  16. data/README.md +197 -197
  17. data/SECURITY.md +27 -27
  18. data/contentstack.gemspec +29 -29
  19. data/lib/contentstack/api.rb +191 -212
  20. data/lib/contentstack/asset.rb +68 -68
  21. data/lib/contentstack/asset_collection.rb +27 -27
  22. data/lib/contentstack/client.rb +91 -91
  23. data/lib/contentstack/content_type.rb +53 -53
  24. data/lib/contentstack/entry.rb +221 -221
  25. data/lib/contentstack/entry_collection.rb +44 -44
  26. data/lib/contentstack/error.rb +6 -6
  27. data/lib/contentstack/query.rb +653 -653
  28. data/lib/contentstack/region.rb +5 -5
  29. data/lib/contentstack/sync_result.rb +29 -29
  30. data/lib/contentstack/version.rb +2 -2
  31. data/lib/contentstack.rb +31 -31
  32. data/lib/util.rb +110 -110
  33. data/rakefile.rb +3 -3
  34. data/spec/asset_collection_spec.rb +15 -15
  35. data/spec/asset_spec.rb +47 -47
  36. data/spec/content_type_spec.rb +80 -80
  37. data/spec/contentstack_spec.rb +38 -38
  38. data/spec/entry_collection_spec.rb +41 -41
  39. data/spec/entry_spec.rb +101 -101
  40. data/spec/fixtures/asset.json +1 -1
  41. data/spec/fixtures/asset_collection.json +1 -1
  42. data/spec/fixtures/category_content_type.json +1 -1
  43. data/spec/fixtures/category_entry.json +1 -1
  44. data/spec/fixtures/category_entry_collection.json +1 -1
  45. data/spec/fixtures/category_entry_collection_without_count.json +1 -1
  46. data/spec/fixtures/content_types.json +1 -1
  47. data/spec/fixtures/product_entry.json +1 -1
  48. data/spec/fixtures/product_entry_collection.json +1 -1
  49. data/spec/fixtures/sync_init.json +2974 -2974
  50. data/spec/query_spec.rb +205 -205
  51. data/spec/spec_helper.rb +180 -180
  52. data/spec/sync_spec.rb +26 -26
  53. metadata +9 -7
@@ -1,654 +1,654 @@
1
- require 'contentstack/entry_collection'
2
- require 'util'
3
-
4
- module Contentstack
5
- # A class that defines a query that is used to query for Entry instance.
6
- class Query
7
- using Utility
8
- # @!attribute [r] query
9
- # Attribute which has all the information about the query which will be executed against Contentstack API
10
-
11
- # @!attribute [r] content_type
12
- # Denotes which `content_type` should the query be executed for
13
-
14
- attr_reader :query, :content_type
15
-
16
- # Initialize the Query instance
17
- # @param [String] content_type
18
- #
19
- # Example:
20
- # @query = @stack.content_type('blog').query
21
- # @entries = @query.where('author', 'John Doe').fetch
22
- #
23
- # @return [Contentstack::Query]
24
- def initialize(content_type)
25
- @content_type = content_type
26
- @query = {
27
- query: "{}",
28
- include_count: false,
29
- skip: 0,
30
- count: 10,
31
- desc: 'created_at'
32
- }
33
- end
34
-
35
- # Add a custom query against specified key.
36
- # @param [String] field_uid
37
- # @param [String/Number/Boolean/Hash] value
38
- #
39
- # Example:
40
- # @query = @stack.content_type('blog').query
41
- # @query.add_query('author', "Jane Doe")
42
- #
43
- # @return [Contentstack::Query]
44
- def add_query(field_uid, value)
45
- add_query_hash({:"#{field_uid}" => value})
46
- end
47
-
48
- # Remove provided query key from custom query if exist.
49
- # @param [String] field_uid
50
- #
51
- # Example:
52
- # @query = @stack.content_type('blog').query
53
- # @query.remove_query('author')
54
- #
55
- # @return [Contentstack::Query]
56
- def remove_query(field_uid)
57
- q = ActiveSupport::JSON.decode(@query[:query])
58
- q.delete(field_uid)
59
- @query[:query] = ActiveSupport::JSON.encode(q)
60
- self
61
- end
62
-
63
- # Add a constraint to fetch all entries that contains given value against specified key.
64
- # @param [Hash] query_hash
65
- #
66
- # Example:
67
- # @query = @stack.content_type('blog').query
68
- # @query.where({:author => "Jane Doe"})
69
- #
70
- # @return [Contentstack::Query]
71
- def where(query_hash)
72
- add_query_hash(query_hash)
73
- end
74
-
75
- # Add a regular expression constraint for finding string values that match the provided regular expression. This may be slow for large data sets.
76
- # @param [String] field_uid The key to be constrained.
77
- # @param [String] pattern The regular expression pattern to match.
78
- # @param [String] options Regex options
79
- #
80
- # Example:
81
- # @query = @stack.content_type('product').query
82
- # @query.regex('title', '.*Mobile.*', 'i') # Search without case sensitivity
83
- #
84
- # @return [Contentstack::Query]
85
- def regex(field_uid, pattern, options="")
86
- hash = {
87
- "#{field_uid}" => {
88
- "$regex": pattern
89
- }
90
- }
91
-
92
- hash["#{field_uid}"]["$options"] = options if !options.empty? || !options.nil?
93
-
94
- add_query_hash(hash)
95
- end
96
-
97
- # Add a constraint that requires, a specified key exists in response.
98
- # @param [String] field_uid The key to be constrained.
99
- #
100
- # Example:
101
- # @query = @stack.content_type('product').query
102
- # @query.exists?('product_image') # only fetch products which have a `product_image`
103
- #
104
- # @return [Contentstack::Query]
105
- def exists?(field_uid)
106
- add_query_hash({:"#{field_uid}" => {"$exists" => true}})
107
- end
108
-
109
- # Add a constraint that requires, a specified key does not exists in response.
110
- # @param [String] field_uid The key to be constrained.
111
- #
112
- # Example:
113
- # @query = @stack.content_type('product').query
114
- # @query.not_exists?('product_image') # only fetch products which do not have a `product_image`
115
- #
116
- # @return [Contentstack::Query]
117
- def not_exists?(field_uid)
118
- add_query_hash({:"#{field_uid}" => {"$exists" => false}})
119
- self
120
- end
121
-
122
- # Combines all the queries together using AND operator.
123
- #
124
- # @param [Array] queries Array of instances of the Query class
125
- #
126
- # Each query should be an instance of the Contentstack::Query class, and belong to the same `content_type`
127
- # Example:
128
- # @query1 = @stack.content_type('category').query
129
- # @query1.where('title', 'Electronics')
130
- #
131
- # @query2 = @stack.content_type('category').query
132
- # @query2.regex('description', '.*Electronics.*')
133
- #
134
- # query_array = [@query1, @query2]
135
- #
136
- # @query = @stack.content_type('category').query
137
- # @query.and(query_array)
138
- #
139
- # @return [Contentstack::Query]
140
- def and(queries)
141
- add_query_hash({"$and" => concat_queries(queries)})
142
- self
143
- end
144
-
145
- # Combines all the queries together using OR operator.
146
- #
147
- # @param [Array] queries Array of instances of the Query class
148
- #
149
- # Each query should be an instance of the Contentstack::Query class, and belong to the same `content_type`
150
- # Example:
151
- # @query1 = @stack.content_type('category').query
152
- # @query1.where('title', 'Electronics')
153
- #
154
- # @query2 = @stack.content_type('category').query
155
- # @query2.where('title', 'Apparel')
156
- #
157
- # query_array = [@query1, @query2]
158
- #
159
- # @query = @stack.content_type('category').query
160
- # @query.or(query_array)
161
- #
162
- # @return [Contentstack::Query]
163
- def or(queries)
164
- add_query_hash({"$or" => concat_queries(queries)})
165
- self
166
- end
167
-
168
- # Add a constraint to the query that requires a particular key entry to be less than the provided value.
169
- #
170
- # @param [String] field_uid UID of the field for which query should be executed
171
- #
172
- # @param [String/Number] value Value that provides an upper bound
173
- #
174
- # Example
175
- # @query = @stack.content_type('product').query
176
- # @query.less_than('price', '100')
177
- #
178
- # @return [Contentstack::Query]
179
- def less_than(field_uid, value)
180
- add_query_hash({:"#{field_uid}" => {"$lt" => value}})
181
- self
182
- end
183
-
184
- # Add a constraint to the query that requires a particular key entry to be less than or equal to the provided value.
185
- #
186
- # @param [String] field_uid UID of the field for which query should be executed
187
- #
188
- # @param [String/Number] value Value that provides an upper bound
189
- #
190
- # Example
191
- # @query = @stack.content_type('product').query
192
- # @query.less_than_or_equal('price', '100')
193
- #
194
- # @return [Contentstack::Query]
195
- def less_than_or_equal(field_uid, value)
196
- add_query_hash({:"#{field_uid}" => {"$lte" => value}})
197
- self
198
- end
199
-
200
- # Add a constraint to the query that requires a particular key entry to be greater than the provided value.
201
- #
202
- # @param [String] field_uid UID of the field for which query should be executed
203
- #
204
- # @param [String/Number] value Value that provides a lower bound
205
- #
206
- # Example
207
- # @query = @stack.content_type('product').query
208
- # @query.greater_than('price', '100')
209
- #
210
- # @return [Contentstack::Query]
211
- def greater_than(field_uid, value)
212
- add_query_hash({:"#{field_uid}" => {"$gt" => value}})
213
- self
214
- end
215
-
216
- # Add a constraint to the query that requires a particular key entry to be greater than or equal to the provided value.
217
- #
218
- # @param [String] field_uid UID of the field for which query should be executed
219
- #
220
- # @param [String/Number] value Value that provides a lower bound
221
- #
222
- # Example
223
- # @query = @stack.content_type('product').query
224
- # @query.greater_than_or_equal('price', '100')
225
- #
226
- # @return [Contentstack::Query]
227
- def greater_than_or_equal(field_uid, value)
228
- add_query_hash({:"#{field_uid}" => {"$gte" => value}})
229
- self
230
- end
231
-
232
- # Add a constraint to the query that requires a particular key's entry to be not equal to the provided value.
233
- #
234
- # @param [String] field_uid UID of the field for which query should be executed
235
- # @param [String] value The object that must not be equaled.
236
- #
237
- # Example
238
- # @query = @stack.content_type('product').query
239
- # @query.not_equal_to('price', '100')
240
- #
241
- # @return [Contentstack::Query]
242
- def not_equal_to(field_uid, value)
243
- add_query_hash({:"#{field_uid}" => {"$ne" => value}})
244
- self
245
- end
246
-
247
- # Add a constraint to the query that requires a particular key's entry to be contained in the provided array.
248
- #
249
- # @param [String] field_uid UID of the field for which query should be executed
250
- # @param [String] values The possible values for the key's object
251
- #
252
- # Example 1 - Array Equals Operator Within Group
253
- # @query = @stack.content_type('category').query
254
- # @query.contained_in("title", ["Electronics", "Apparel"])
255
- #
256
- # Example 2 - Array Equals Operator Within Modular Blocks
257
- # @query = @stack.content_type('category').query
258
- # @query.contained_in("additional_info.deals.deal_name", ["Christmas Deal", "Summer Deal"])
259
- #
260
- # @return [Contentstack::Query]
261
- def contained_in(field_uid, values)
262
- add_query_hash({:"#{field_uid}" => {"$in" => values}})
263
- self
264
- end
265
-
266
- # Add a constraint to the query that requires a particular key entry's value not be contained in the provided array.
267
- #
268
- # @param [String] field_uid UID of the field for which query should be executed
269
- # @param [String] values The possible values for the key's object
270
- #
271
- # Example 1 - Array Not-equals Operator Within Group
272
- # @query = @stack.content_type('category').query
273
- # @query.not_contained_in("title", ["Electronics", "Apparel"])
274
- #
275
- # Example 2 - Array Not-equals Operator Within Modular Blocks
276
- # @query = @stack.content_type('category').query
277
- # @query.not_contained_in("additional_info.deals.deal_name", ["Christmas Deal", "Summer Deal"])
278
- #
279
- # @return [Contentstack::Query]
280
- def not_contained_in(field_uid, values)
281
- add_query_hash({:"#{field_uid}" => {"$nin" => values}})
282
- self
283
- end
284
-
285
- # The number of objects to skip before returning any.
286
- #
287
- # @param [Number] count of objects to skip from resulset.
288
- #
289
- # Example
290
- # @query = @stack.content_type('category').query
291
- # @query.skip(50)
292
- #
293
- # @return [Contentstack::Query]
294
- def skip(count)
295
- @query[:skip] = count
296
- self
297
- end
298
-
299
- # This method provides only the entries matching the specified value.
300
- # @deprecated since version 0.5.0
301
- # @param [String] text value used to match or compare
302
- #
303
- # Example
304
- # @query = @stack.content_type('product').query
305
- # @query.search("This is an awesome product")
306
- #
307
- # @return [Contentstack::Query]
308
- def search(text)
309
- @query[:typeahead] = text
310
- self
311
- end
312
-
313
- # A limit on the number of objects to return.
314
- #
315
- # @param [Number] count of objects to limit in resulset.
316
- #
317
- # Example
318
- # @query = @stack.content_type('category').query
319
- # @query.limit(50)
320
- #
321
- # @return [Contentstack::Query]
322
- def limit(count=10)
323
- @query[:limit] = count
324
- self
325
- end
326
-
327
- # Retrieve only count of entries in result.
328
- #
329
- # Example
330
- # @query = @stack.content_type('category').query
331
- # @query.count
332
- #
333
- # @return [Integer]
334
- def count
335
- include_count
336
- fetch.count
337
- end
338
-
339
- # Retrieve count and data of objects in result.
340
- #
341
- # Example
342
- # @query = @stack.content_type('category').query
343
- # @query.include_count
344
- #
345
- # @return [Contentstack::Query]
346
- def include_count(flag=true)
347
- @query[:include_count] = flag
348
- self
349
- end
350
-
351
- # Sort the results in ascending order with the given key.
352
- # Sort the returned entries in ascending order of the provided key.
353
- #
354
- # @param [String] field_uid The key to order by
355
- #
356
- # Example
357
- # @query = @stack.content_type('category').query
358
- # @query.ascending
359
- #
360
- # @return [Contentstack::Query]
361
- def ascending(field_uid)
362
- @query.delete(:desc)
363
- @query[:asc] = field_uid
364
- self
365
- end
366
-
367
- # Sort the results in descending order with the given key.
368
- # Sort the returned entries in descending order of the provided key.
369
- #
370
- # @param [String] field_uid The key to order by
371
- #
372
- # Example
373
- # @query = @stack.content_type('category').query
374
- # @query.descending
375
- #
376
- # @return [Contentstack::Query]
377
- def descending(field_uid)
378
- @query.delete(:asc)
379
- @query[:desc] = field_uid
380
- self
381
- end
382
-
383
- # Get entries from the specified locale.
384
- #
385
- # @param [String] code The locale code of the entry
386
- #
387
- # Example
388
- # Change language method
389
- # @query = @stack.content_type('category').query
390
- # @query.locale('en-us')
391
- #
392
- # @return [Contentstack::Query]
393
- def locale(code)
394
- @query[:locale] = code
395
- self
396
- end
397
-
398
- # Specifies an array of 'only' keys in BASE object that would be 'included' in the response.
399
- #
400
- # @param [Array] fields Array of the 'only' reference keys to be included in response.
401
- # @param [Array] fields_with_base Can be used to denote 'only' fields of the reference class
402
- #
403
- # Example
404
- # # Include only title and description field in response
405
- # @query = @stack.content_type('category').query
406
- # @query.only(['title', 'description'])
407
- #
408
- # # Query product and include only the title and description from category reference
409
- # @query = @stack.content_type('product').query
410
- # @query.include_reference('category')
411
- # .only('category', ['title', 'description'])
412
- #
413
- # @return [Contentstack::Query]
414
- def only(fields, fields_with_base=nil)
415
- q = {}
416
- if [Array, String].include?(fields_with_base.class)
417
- fields_with_base = [fields_with_base] if fields_with_base.class == String
418
- q[fields.to_sym] = fields_with_base
419
- else
420
- fields = [fields] if fields.class == String
421
- q = {BASE: fields}
422
- end
423
-
424
- @query[:only] = q
425
- self
426
- end
427
-
428
- # Specifies list of field uids that would be 'excluded' from the response.
429
- #
430
- # @param [Array] fields Array of field uid which get 'excluded' from the response.
431
- # @param [Array] fields_with_base Can be used to denote 'except' fields of the reference class
432
- #
433
- # Example
434
- # # Exclude 'description' field in response
435
- # @query = @stack.content_type('category').query
436
- # @query.except(['description'])
437
- #
438
- # # Query product and exclude the 'description' from category reference
439
- # @query = @stack.content_type('product').query
440
- # @query.include_reference('category')
441
- # .except('category', ['description'])
442
- #
443
- # @return [Contentstack::Query]
444
- def except(fields, fields_with_base=nil)
445
- q = {}
446
- if [Array, String].include?(fields_with_base.class)
447
- fields_with_base = [fields_with_base] if fields_with_base.class == String
448
- q[fields.to_sym] = fields_with_base
449
- else
450
- fields = [fields] if fields.class == String
451
- q = {BASE: fields}
452
- end
453
-
454
- @query[:except] = q
455
- self
456
- end
457
-
458
- # Add a constraint that requires a particular reference key details.
459
- #
460
- # @param [String/Array] reference_field_uids Pass string or array of reference fields that must be included in the response
461
- #
462
- # Example
463
- #
464
- # # Include reference of 'category'
465
- # @query = @stack.content_type('product').query
466
- # @query.include_reference('category')
467
- #
468
- # # Include reference of 'category' and 'reviews'
469
- # @query = @stack.content_type('product').query
470
- # @query.include_reference(['category', 'reviews'])
471
- #
472
- # @return [Contentstack::Query]
473
- def include_reference(reference_field_uids)
474
- self.include(reference_field_uids)
475
- end
476
-
477
- # Include schemas of all returned objects along with objects themselves.
478
- #
479
- # Example
480
- #
481
- # @query = @stack.content_type('product').query
482
- # @query.include_schema
483
- #
484
- # @return [Contentstack::Query]
485
- def include_schema(flag=true)
486
- @query[:include_schema] = flag
487
- self
488
- end
489
-
490
- # Include object owner's profile in the objects data.
491
- #
492
- # Example
493
- #
494
- # @query = @stack.content_type('product').query
495
- # @query.include_owner
496
- #
497
- # @return [Contentstack::Query]
498
- def include_owner(flag=true)
499
- @query[:include_owner] = flag
500
- self
501
- end
502
-
503
- # Include object's content_type in response
504
- #
505
- # Example
506
- #
507
- # @query = @stack.content_type('product').query
508
- # @query.include_content_type
509
- #
510
- # @return [Contentstack::Query]
511
- def include_content_type(flag=true)
512
- @query[:include_content_type] = flag
513
- self
514
- end
515
-
516
-
517
- # Include the fallback locale publish content, if specified locale content is not publish.
518
- #
519
- # Example
520
- #
521
- # @query = @stack.content_type('product').query
522
- # @query.include_fallback
523
- #
524
- # @return [Contentstack::Query]
525
- def include_fallback(flag=true)
526
- @query[:include_fallback] = flag
527
- self
528
- end
529
-
530
- # Include the branch for publish content.
531
- #
532
- # Example
533
- #
534
- # @query = @stack.content_type('product').query
535
- # @query.include_branch
536
- #
537
- # @return [Contentstack::Entry]
538
- def include_branch(flag=true)
539
- @query[:include_branch] = flag
540
- self
541
- end
542
-
543
- # Include Embedded Objects (Entries and Assets) along with entry/entries details.
544
- #
545
- # Example
546
- #
547
- # @query = @stack.content_type('product').query
548
- # @query.include_embedded_items
549
- #
550
- # @return [Contentstack::Query]
551
- def include_embedded_items()
552
- @query[:include_embedded_items] = ['BASE']
553
- self
554
- end
555
-
556
- # Include objects in 'Draft' mode in response
557
- #
558
- # Example
559
- #
560
- # @query = @stack.content_type('product').query
561
- # @query.include_draft
562
- #
563
- # @return [Contentstack::Query]
564
- def include_draft(flag=true)
565
- @query[:include_draft] = flag
566
- self
567
- end
568
-
569
-
570
- #
571
- # @return [Contentstack::Query]
572
- def include(field_uids)
573
- field_uids = [field_uids] if field_uids.class == String
574
- @query[:include] ||= []
575
- @query[:include] = @query[:include] | field_uids
576
- self
577
- end
578
-
579
- # Include tags with which to search entries.
580
- #
581
- # @param [Array] tags_array Array of tags using which search must be performed
582
- #
583
- # Example
584
- #
585
- # @query = @stack.content_type('product').query
586
- # @query.tags(["tag1", "tag2"])
587
- #
588
- # @return [Contentstack::Query]
589
- def tags(tags_array)
590
- @query[:tags] = tags_array
591
- self
592
- end
593
-
594
-
595
- # Execute query
596
- #
597
- # Example
598
- #
599
- # @query = @stack.content_type('product').query
600
- # @query.tags(["tag1", "tag2"])
601
- # .fetch
602
- #
603
- # @return [Contentstack::EntryCollection]
604
- def fetch
605
- entries = API.fetch_entries(@content_type, @query)
606
- EntryCollection.new(entries, @content_type)
607
- end
608
-
609
- # Execute a Query and get the single matching object
610
- #
611
- # Example
612
- #
613
- # @query = @stack.content_type('product').query
614
- # @query.tags(["tag1", "tag2"])
615
- # .find_one
616
- #
617
- # @return [Contentstack::Entry]
618
- def find_one
619
- limit 1
620
- fetch.first
621
- end
622
-
623
- alias_method :find, :fetch
624
- alias_method :in, :contained_in
625
- alias_method :not_in, :not_contained_in
626
-
627
- private
628
- def add_query_hash(query_hash)
629
- q = ActiveSupport::JSON.decode(@query[:query])
630
- q.merge!(query_hash)
631
- @query[:query] = ActiveSupport::JSON.encode(q)
632
- self
633
- end
634
-
635
- def concat_queries(queries)
636
- this_queries = []
637
- this_query = ActiveSupport::JSON.decode(@query[:query])
638
- if this_query.keys.length > 0
639
- this_queries = [this_query]
640
- end
641
-
642
- if queries.class == Array
643
- queries.map do |query_object|
644
- if query_object.class == Contentstack::Query && query_object.content_type == @content_type
645
- q = ActiveSupport::JSON.decode(query_object.query[:query])
646
- this_queries.push(q.symbolize_keys)
647
- end
648
- end
649
- end
650
-
651
- this_queries
652
- end
653
- end
1
+ require 'contentstack/entry_collection'
2
+ require 'util'
3
+
4
+ module Contentstack
5
+ # A class that defines a query that is used to query for Entry instance.
6
+ class Query
7
+ using Utility
8
+ # @!attribute [r] query
9
+ # Attribute which has all the information about the query which will be executed against Contentstack API
10
+
11
+ # @!attribute [r] content_type
12
+ # Denotes which `content_type` should the query be executed for
13
+
14
+ attr_reader :query, :content_type
15
+
16
+ # Initialize the Query instance
17
+ # @param [String] content_type
18
+ #
19
+ # Example:
20
+ # @query = @stack.content_type('blog').query
21
+ # @entries = @query.where('author', 'John Doe').fetch
22
+ #
23
+ # @return [Contentstack::Query]
24
+ def initialize(content_type)
25
+ @content_type = content_type
26
+ @query = {
27
+ query: "{}",
28
+ include_count: false,
29
+ skip: 0,
30
+ count: 10,
31
+ desc: 'created_at'
32
+ }
33
+ end
34
+
35
+ # Add a custom query against specified key.
36
+ # @param [String] field_uid
37
+ # @param [String/Number/Boolean/Hash] value
38
+ #
39
+ # Example:
40
+ # @query = @stack.content_type('blog').query
41
+ # @query.add_query('author', "Jane Doe")
42
+ #
43
+ # @return [Contentstack::Query]
44
+ def add_query(field_uid, value)
45
+ add_query_hash({:"#{field_uid}" => value})
46
+ end
47
+
48
+ # Remove provided query key from custom query if exist.
49
+ # @param [String] field_uid
50
+ #
51
+ # Example:
52
+ # @query = @stack.content_type('blog').query
53
+ # @query.remove_query('author')
54
+ #
55
+ # @return [Contentstack::Query]
56
+ def remove_query(field_uid)
57
+ q = ActiveSupport::JSON.decode(@query[:query])
58
+ q.delete(field_uid)
59
+ @query[:query] = ActiveSupport::JSON.encode(q)
60
+ self
61
+ end
62
+
63
+ # Add a constraint to fetch all entries that contains given value against specified key.
64
+ # @param [Hash] query_hash
65
+ #
66
+ # Example:
67
+ # @query = @stack.content_type('blog').query
68
+ # @query.where({:author => "Jane Doe"})
69
+ #
70
+ # @return [Contentstack::Query]
71
+ def where(query_hash)
72
+ add_query_hash(query_hash)
73
+ end
74
+
75
+ # Add a regular expression constraint for finding string values that match the provided regular expression. This may be slow for large data sets.
76
+ # @param [String] field_uid The key to be constrained.
77
+ # @param [String] pattern The regular expression pattern to match.
78
+ # @param [String] options Regex options
79
+ #
80
+ # Example:
81
+ # @query = @stack.content_type('product').query
82
+ # @query.regex('title', '.*Mobile.*', 'i') # Search without case sensitivity
83
+ #
84
+ # @return [Contentstack::Query]
85
+ def regex(field_uid, pattern, options="")
86
+ hash = {
87
+ "#{field_uid}" => {
88
+ "$regex": pattern
89
+ }
90
+ }
91
+
92
+ hash["#{field_uid}"]["$options"] = options if !options.empty? || !options.nil?
93
+
94
+ add_query_hash(hash)
95
+ end
96
+
97
+ # Add a constraint that requires, a specified key exists in response.
98
+ # @param [String] field_uid The key to be constrained.
99
+ #
100
+ # Example:
101
+ # @query = @stack.content_type('product').query
102
+ # @query.exists?('product_image') # only fetch products which have a `product_image`
103
+ #
104
+ # @return [Contentstack::Query]
105
+ def exists?(field_uid)
106
+ add_query_hash({:"#{field_uid}" => {"$exists" => true}})
107
+ end
108
+
109
+ # Add a constraint that requires, a specified key does not exists in response.
110
+ # @param [String] field_uid The key to be constrained.
111
+ #
112
+ # Example:
113
+ # @query = @stack.content_type('product').query
114
+ # @query.not_exists?('product_image') # only fetch products which do not have a `product_image`
115
+ #
116
+ # @return [Contentstack::Query]
117
+ def not_exists?(field_uid)
118
+ add_query_hash({:"#{field_uid}" => {"$exists" => false}})
119
+ self
120
+ end
121
+
122
+ # Combines all the queries together using AND operator.
123
+ #
124
+ # @param [Array] queries Array of instances of the Query class
125
+ #
126
+ # Each query should be an instance of the Contentstack::Query class, and belong to the same `content_type`
127
+ # Example:
128
+ # @query1 = @stack.content_type('category').query
129
+ # @query1.where('title', 'Electronics')
130
+ #
131
+ # @query2 = @stack.content_type('category').query
132
+ # @query2.regex('description', '.*Electronics.*')
133
+ #
134
+ # query_array = [@query1, @query2]
135
+ #
136
+ # @query = @stack.content_type('category').query
137
+ # @query.and(query_array)
138
+ #
139
+ # @return [Contentstack::Query]
140
+ def and(queries)
141
+ add_query_hash({"$and" => concat_queries(queries)})
142
+ self
143
+ end
144
+
145
+ # Combines all the queries together using OR operator.
146
+ #
147
+ # @param [Array] queries Array of instances of the Query class
148
+ #
149
+ # Each query should be an instance of the Contentstack::Query class, and belong to the same `content_type`
150
+ # Example:
151
+ # @query1 = @stack.content_type('category').query
152
+ # @query1.where('title', 'Electronics')
153
+ #
154
+ # @query2 = @stack.content_type('category').query
155
+ # @query2.where('title', 'Apparel')
156
+ #
157
+ # query_array = [@query1, @query2]
158
+ #
159
+ # @query = @stack.content_type('category').query
160
+ # @query.or(query_array)
161
+ #
162
+ # @return [Contentstack::Query]
163
+ def or(queries)
164
+ add_query_hash({"$or" => concat_queries(queries)})
165
+ self
166
+ end
167
+
168
+ # Add a constraint to the query that requires a particular key entry to be less than the provided value.
169
+ #
170
+ # @param [String] field_uid UID of the field for which query should be executed
171
+ #
172
+ # @param [String/Number] value Value that provides an upper bound
173
+ #
174
+ # Example
175
+ # @query = @stack.content_type('product').query
176
+ # @query.less_than('price', '100')
177
+ #
178
+ # @return [Contentstack::Query]
179
+ def less_than(field_uid, value)
180
+ add_query_hash({:"#{field_uid}" => {"$lt" => value}})
181
+ self
182
+ end
183
+
184
+ # Add a constraint to the query that requires a particular key entry to be less than or equal to the provided value.
185
+ #
186
+ # @param [String] field_uid UID of the field for which query should be executed
187
+ #
188
+ # @param [String/Number] value Value that provides an upper bound
189
+ #
190
+ # Example
191
+ # @query = @stack.content_type('product').query
192
+ # @query.less_than_or_equal('price', '100')
193
+ #
194
+ # @return [Contentstack::Query]
195
+ def less_than_or_equal(field_uid, value)
196
+ add_query_hash({:"#{field_uid}" => {"$lte" => value}})
197
+ self
198
+ end
199
+
200
+ # Add a constraint to the query that requires a particular key entry to be greater than the provided value.
201
+ #
202
+ # @param [String] field_uid UID of the field for which query should be executed
203
+ #
204
+ # @param [String/Number] value Value that provides a lower bound
205
+ #
206
+ # Example
207
+ # @query = @stack.content_type('product').query
208
+ # @query.greater_than('price', '100')
209
+ #
210
+ # @return [Contentstack::Query]
211
+ def greater_than(field_uid, value)
212
+ add_query_hash({:"#{field_uid}" => {"$gt" => value}})
213
+ self
214
+ end
215
+
216
+ # Add a constraint to the query that requires a particular key entry to be greater than or equal to the provided value.
217
+ #
218
+ # @param [String] field_uid UID of the field for which query should be executed
219
+ #
220
+ # @param [String/Number] value Value that provides a lower bound
221
+ #
222
+ # Example
223
+ # @query = @stack.content_type('product').query
224
+ # @query.greater_than_or_equal('price', '100')
225
+ #
226
+ # @return [Contentstack::Query]
227
+ def greater_than_or_equal(field_uid, value)
228
+ add_query_hash({:"#{field_uid}" => {"$gte" => value}})
229
+ self
230
+ end
231
+
232
+ # Add a constraint to the query that requires a particular key's entry to be not equal to the provided value.
233
+ #
234
+ # @param [String] field_uid UID of the field for which query should be executed
235
+ # @param [String] value The object that must not be equaled.
236
+ #
237
+ # Example
238
+ # @query = @stack.content_type('product').query
239
+ # @query.not_equal_to('price', '100')
240
+ #
241
+ # @return [Contentstack::Query]
242
+ def not_equal_to(field_uid, value)
243
+ add_query_hash({:"#{field_uid}" => {"$ne" => value}})
244
+ self
245
+ end
246
+
247
+ # Add a constraint to the query that requires a particular key's entry to be contained in the provided array.
248
+ #
249
+ # @param [String] field_uid UID of the field for which query should be executed
250
+ # @param [String] values The possible values for the key's object
251
+ #
252
+ # Example 1 - Array Equals Operator Within Group
253
+ # @query = @stack.content_type('category').query
254
+ # @query.contained_in("title", ["Electronics", "Apparel"])
255
+ #
256
+ # Example 2 - Array Equals Operator Within Modular Blocks
257
+ # @query = @stack.content_type('category').query
258
+ # @query.contained_in("additional_info.deals.deal_name", ["Christmas Deal", "Summer Deal"])
259
+ #
260
+ # @return [Contentstack::Query]
261
+ def contained_in(field_uid, values)
262
+ add_query_hash({:"#{field_uid}" => {"$in" => values}})
263
+ self
264
+ end
265
+
266
+ # Add a constraint to the query that requires a particular key entry's value not be contained in the provided array.
267
+ #
268
+ # @param [String] field_uid UID of the field for which query should be executed
269
+ # @param [String] values The possible values for the key's object
270
+ #
271
+ # Example 1 - Array Not-equals Operator Within Group
272
+ # @query = @stack.content_type('category').query
273
+ # @query.not_contained_in("title", ["Electronics", "Apparel"])
274
+ #
275
+ # Example 2 - Array Not-equals Operator Within Modular Blocks
276
+ # @query = @stack.content_type('category').query
277
+ # @query.not_contained_in("additional_info.deals.deal_name", ["Christmas Deal", "Summer Deal"])
278
+ #
279
+ # @return [Contentstack::Query]
280
+ def not_contained_in(field_uid, values)
281
+ add_query_hash({:"#{field_uid}" => {"$nin" => values}})
282
+ self
283
+ end
284
+
285
+ # The number of objects to skip before returning any.
286
+ #
287
+ # @param [Number] count of objects to skip from resulset.
288
+ #
289
+ # Example
290
+ # @query = @stack.content_type('category').query
291
+ # @query.skip(50)
292
+ #
293
+ # @return [Contentstack::Query]
294
+ def skip(count)
295
+ @query[:skip] = count
296
+ self
297
+ end
298
+
299
+ # This method provides only the entries matching the specified value.
300
+ # @deprecated since version 0.5.0
301
+ # @param [String] text value used to match or compare
302
+ #
303
+ # Example
304
+ # @query = @stack.content_type('product').query
305
+ # @query.search("This is an awesome product")
306
+ #
307
+ # @return [Contentstack::Query]
308
+ def search(text)
309
+ @query[:typeahead] = text
310
+ self
311
+ end
312
+
313
+ # A limit on the number of objects to return.
314
+ #
315
+ # @param [Number] count of objects to limit in resulset.
316
+ #
317
+ # Example
318
+ # @query = @stack.content_type('category').query
319
+ # @query.limit(50)
320
+ #
321
+ # @return [Contentstack::Query]
322
+ def limit(count=10)
323
+ @query[:limit] = count
324
+ self
325
+ end
326
+
327
+ # Retrieve only count of entries in result.
328
+ #
329
+ # Example
330
+ # @query = @stack.content_type('category').query
331
+ # @query.count
332
+ #
333
+ # @return [Integer]
334
+ def count
335
+ include_count
336
+ fetch.count
337
+ end
338
+
339
+ # Retrieve count and data of objects in result.
340
+ #
341
+ # Example
342
+ # @query = @stack.content_type('category').query
343
+ # @query.include_count
344
+ #
345
+ # @return [Contentstack::Query]
346
+ def include_count(flag=true)
347
+ @query[:include_count] = flag
348
+ self
349
+ end
350
+
351
+ # Sort the results in ascending order with the given key.
352
+ # Sort the returned entries in ascending order of the provided key.
353
+ #
354
+ # @param [String] field_uid The key to order by
355
+ #
356
+ # Example
357
+ # @query = @stack.content_type('category').query
358
+ # @query.ascending
359
+ #
360
+ # @return [Contentstack::Query]
361
+ def ascending(field_uid)
362
+ @query.delete(:desc)
363
+ @query[:asc] = field_uid
364
+ self
365
+ end
366
+
367
+ # Sort the results in descending order with the given key.
368
+ # Sort the returned entries in descending order of the provided key.
369
+ #
370
+ # @param [String] field_uid The key to order by
371
+ #
372
+ # Example
373
+ # @query = @stack.content_type('category').query
374
+ # @query.descending
375
+ #
376
+ # @return [Contentstack::Query]
377
+ def descending(field_uid)
378
+ @query.delete(:asc)
379
+ @query[:desc] = field_uid
380
+ self
381
+ end
382
+
383
+ # Get entries from the specified locale.
384
+ #
385
+ # @param [String] code The locale code of the entry
386
+ #
387
+ # Example
388
+ # Change language method
389
+ # @query = @stack.content_type('category').query
390
+ # @query.locale('en-us')
391
+ #
392
+ # @return [Contentstack::Query]
393
+ def locale(code)
394
+ @query[:locale] = code
395
+ self
396
+ end
397
+
398
+ # Specifies an array of 'only' keys in BASE object that would be 'included' in the response.
399
+ #
400
+ # @param [Array] fields Array of the 'only' reference keys to be included in response.
401
+ # @param [Array] fields_with_base Can be used to denote 'only' fields of the reference class
402
+ #
403
+ # Example
404
+ # # Include only title and description field in response
405
+ # @query = @stack.content_type('category').query
406
+ # @query.only(['title', 'description'])
407
+ #
408
+ # # Query product and include only the title and description from category reference
409
+ # @query = @stack.content_type('product').query
410
+ # @query.include_reference('category')
411
+ # .only('category', ['title', 'description'])
412
+ #
413
+ # @return [Contentstack::Query]
414
+ def only(fields, fields_with_base=nil)
415
+ q = {}
416
+ if [Array, String].include?(fields_with_base.class)
417
+ fields_with_base = [fields_with_base] if fields_with_base.class == String
418
+ q[fields.to_sym] = fields_with_base
419
+ else
420
+ fields = [fields] if fields.class == String
421
+ q = {BASE: fields}
422
+ end
423
+
424
+ @query[:only] = q
425
+ self
426
+ end
427
+
428
+ # Specifies list of field uids that would be 'excluded' from the response.
429
+ #
430
+ # @param [Array] fields Array of field uid which get 'excluded' from the response.
431
+ # @param [Array] fields_with_base Can be used to denote 'except' fields of the reference class
432
+ #
433
+ # Example
434
+ # # Exclude 'description' field in response
435
+ # @query = @stack.content_type('category').query
436
+ # @query.except(['description'])
437
+ #
438
+ # # Query product and exclude the 'description' from category reference
439
+ # @query = @stack.content_type('product').query
440
+ # @query.include_reference('category')
441
+ # .except('category', ['description'])
442
+ #
443
+ # @return [Contentstack::Query]
444
+ def except(fields, fields_with_base=nil)
445
+ q = {}
446
+ if [Array, String].include?(fields_with_base.class)
447
+ fields_with_base = [fields_with_base] if fields_with_base.class == String
448
+ q[fields.to_sym] = fields_with_base
449
+ else
450
+ fields = [fields] if fields.class == String
451
+ q = {BASE: fields}
452
+ end
453
+
454
+ @query[:except] = q
455
+ self
456
+ end
457
+
458
+ # Add a constraint that requires a particular reference key details.
459
+ #
460
+ # @param [String/Array] reference_field_uids Pass string or array of reference fields that must be included in the response
461
+ #
462
+ # Example
463
+ #
464
+ # # Include reference of 'category'
465
+ # @query = @stack.content_type('product').query
466
+ # @query.include_reference('category')
467
+ #
468
+ # # Include reference of 'category' and 'reviews'
469
+ # @query = @stack.content_type('product').query
470
+ # @query.include_reference(['category', 'reviews'])
471
+ #
472
+ # @return [Contentstack::Query]
473
+ def include_reference(reference_field_uids)
474
+ self.include(reference_field_uids)
475
+ end
476
+
477
+ # Include schemas of all returned objects along with objects themselves.
478
+ #
479
+ # Example
480
+ #
481
+ # @query = @stack.content_type('product').query
482
+ # @query.include_schema
483
+ #
484
+ # @return [Contentstack::Query]
485
+ def include_schema(flag=true)
486
+ @query[:include_schema] = flag
487
+ self
488
+ end
489
+
490
+ # Include object owner's profile in the objects data.
491
+ #
492
+ # Example
493
+ #
494
+ # @query = @stack.content_type('product').query
495
+ # @query.include_owner
496
+ #
497
+ # @return [Contentstack::Query]
498
+ def include_owner(flag=true)
499
+ @query[:include_owner] = flag
500
+ self
501
+ end
502
+
503
+ # Include object's content_type in response
504
+ #
505
+ # Example
506
+ #
507
+ # @query = @stack.content_type('product').query
508
+ # @query.include_content_type
509
+ #
510
+ # @return [Contentstack::Query]
511
+ def include_content_type(flag=true)
512
+ @query[:include_content_type] = flag
513
+ self
514
+ end
515
+
516
+
517
+ # Include the fallback locale publish content, if specified locale content is not publish.
518
+ #
519
+ # Example
520
+ #
521
+ # @query = @stack.content_type('product').query
522
+ # @query.include_fallback
523
+ #
524
+ # @return [Contentstack::Query]
525
+ def include_fallback(flag=true)
526
+ @query[:include_fallback] = flag
527
+ self
528
+ end
529
+
530
+ # Include the branch for publish content.
531
+ #
532
+ # Example
533
+ #
534
+ # @query = @stack.content_type('product').query
535
+ # @query.include_branch
536
+ #
537
+ # @return [Contentstack::Entry]
538
+ def include_branch(flag=true)
539
+ @query[:include_branch] = flag
540
+ self
541
+ end
542
+
543
+ # Include Embedded Objects (Entries and Assets) along with entry/entries details.
544
+ #
545
+ # Example
546
+ #
547
+ # @query = @stack.content_type('product').query
548
+ # @query.include_embedded_items
549
+ #
550
+ # @return [Contentstack::Query]
551
+ def include_embedded_items()
552
+ @query[:include_embedded_items] = ['BASE']
553
+ self
554
+ end
555
+
556
+ # Include objects in 'Draft' mode in response
557
+ #
558
+ # Example
559
+ #
560
+ # @query = @stack.content_type('product').query
561
+ # @query.include_draft
562
+ #
563
+ # @return [Contentstack::Query]
564
+ def include_draft(flag=true)
565
+ @query[:include_draft] = flag
566
+ self
567
+ end
568
+
569
+
570
+ #
571
+ # @return [Contentstack::Query]
572
+ def include(field_uids)
573
+ field_uids = [field_uids] if field_uids.class == String
574
+ @query[:include] ||= []
575
+ @query[:include] = @query[:include] | field_uids
576
+ self
577
+ end
578
+
579
+ # Include tags with which to search entries.
580
+ #
581
+ # @param [Array] tags_array Array of tags using which search must be performed
582
+ #
583
+ # Example
584
+ #
585
+ # @query = @stack.content_type('product').query
586
+ # @query.tags(["tag1", "tag2"])
587
+ #
588
+ # @return [Contentstack::Query]
589
+ def tags(tags_array)
590
+ @query[:tags] = tags_array
591
+ self
592
+ end
593
+
594
+
595
+ # Execute query
596
+ #
597
+ # Example
598
+ #
599
+ # @query = @stack.content_type('product').query
600
+ # @query.tags(["tag1", "tag2"])
601
+ # .fetch
602
+ #
603
+ # @return [Contentstack::EntryCollection]
604
+ def fetch
605
+ entries = API.fetch_entries(@content_type, @query)
606
+ EntryCollection.new(entries, @content_type)
607
+ end
608
+
609
+ # Execute a Query and get the single matching object
610
+ #
611
+ # Example
612
+ #
613
+ # @query = @stack.content_type('product').query
614
+ # @query.tags(["tag1", "tag2"])
615
+ # .find_one
616
+ #
617
+ # @return [Contentstack::Entry]
618
+ def find_one
619
+ limit 1
620
+ fetch.first
621
+ end
622
+
623
+ alias_method :find, :fetch
624
+ alias_method :in, :contained_in
625
+ alias_method :not_in, :not_contained_in
626
+
627
+ private
628
+ def add_query_hash(query_hash)
629
+ q = ActiveSupport::JSON.decode(@query[:query])
630
+ q.merge!(query_hash)
631
+ @query[:query] = ActiveSupport::JSON.encode(q)
632
+ self
633
+ end
634
+
635
+ def concat_queries(queries)
636
+ this_queries = []
637
+ this_query = ActiveSupport::JSON.decode(@query[:query])
638
+ if this_query.keys.length > 0
639
+ this_queries = [this_query]
640
+ end
641
+
642
+ if queries.class == Array
643
+ queries.map do |query_object|
644
+ if query_object.class == Contentstack::Query && query_object.content_type == @content_type
645
+ q = ActiveSupport::JSON.decode(query_object.query[:query])
646
+ this_queries.push(q.symbolize_keys)
647
+ end
648
+ end
649
+ end
650
+
651
+ this_queries
652
+ end
653
+ end
654
654
  end