opensearch-sugar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.agents/skills/diataxis/SKILL.md +142 -0
  3. data/.agents/skills/diataxis/references/examples.md +420 -0
  4. data/.agents/skills/diataxis/references/explanation-template.md +96 -0
  5. data/.agents/skills/diataxis/references/framework.md +400 -0
  6. data/.agents/skills/diataxis/references/how-to-guide-template.md +105 -0
  7. data/.agents/skills/diataxis/references/reference-template.md +110 -0
  8. data/.agents/skills/diataxis/references/tutorial-template.md +101 -0
  9. data/.agents/skills/diataxis/scripts/generate_index.py +139 -0
  10. data/.rspec +3 -0
  11. data/.standard.yml +3 -0
  12. data/AGENTS.md +120 -0
  13. data/CHANGELOG.md +5 -0
  14. data/Dockerfile.opensearch +4 -0
  15. data/Increase_Coverage.md +311 -0
  16. data/README.md +143 -0
  17. data/Rakefile +27 -0
  18. data/Steepfile +23 -0
  19. data/adrs/ADR-000-template.md +87 -0
  20. data/adrs/ADR-001-simpledelegator-for-client.md +138 -0
  21. data/adrs/ADR-002-facade-pattern-for-index.md +126 -0
  22. data/adrs/ADR-003-repository-pattern-for-models.md +148 -0
  23. data/adrs/ADR-004-integration-tests-no-mocking.md +91 -0
  24. data/adrs/ADR-005-exceptions-over-result-objects.md +107 -0
  25. data/adrs/ADR-006-ssl-on-by-default.md +95 -0
  26. data/adrs/ADR-007-selective-sugar-surface.md +118 -0
  27. data/adrs/ADR-008-integration-test-design.md +178 -0
  28. data/compose.yml +2 -0
  29. data/compose_opensearch.yml +31 -0
  30. data/docs/HOWTO.md +844 -0
  31. data/docs/REFERENCE.md +725 -0
  32. data/docs/TUTORIAL.md +327 -0
  33. data/docs/alias-api-design-notes.md +119 -0
  34. data/lib/opensearch/sugar/client.rb +300 -0
  35. data/lib/opensearch/sugar/index/include/utilities.rb +6 -0
  36. data/lib/opensearch/sugar/index.rb +339 -0
  37. data/lib/opensearch/sugar/models.rb +209 -0
  38. data/lib/opensearch/sugar/version.rb +8 -0
  39. data/lib/opensearch/sugar.rb +61 -0
  40. data/old_docs/DELEGATED_METHODS_ANALYSIS.md +361 -0
  41. data/old_docs/EXPLANATION.md +685 -0
  42. data/old_docs/README.md +155 -0
  43. data/old_docs/docs/CLI-PROPOSAL.md +257 -0
  44. data/old_docs/docs/HOWTO.md +798 -0
  45. data/old_docs/docs/REFERENCE.md +901 -0
  46. data/old_docs/docs/TUTORIAL.md +493 -0
  47. data/sig/opensearch/sugar.rbs +162 -0
  48. metadata +240 -0
@@ -0,0 +1,901 @@
1
+ # API Reference
2
+
3
+ *(Documentation written by GitHub Copilot, powered by Claude Sonnet 4.5)*
4
+
5
+ Complete technical reference for all classes, methods, and interfaces in OpenSearch::Sugar.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Module: OpenSearch::Sugar](#module-opensearchsugar)
10
+ - [Class: OpenSearch::Sugar::Client](#class-opensearchsugarclient)
11
+ - [Class: OpenSearch::Sugar::Index](#class-opensearchsugarindex)
12
+ - [Class: OpenSearch::Sugar::Models](#class-opensearchsugarmodels)
13
+ - [Error Classes](#error-classes)
14
+
15
+ ---
16
+
17
+ ## Module: OpenSearch::Sugar
18
+
19
+ The main namespace module for the OpenSearch::Sugar gem.
20
+
21
+ ### Module Methods
22
+
23
+ #### `OpenSearch::Sugar.client(**kwargs)`
24
+
25
+ Creates a new OpenSearch::Sugar::Client instance.
26
+
27
+ **Parameters:**
28
+ - `**kwargs` (Hash) - Connection parameters passed to Client.new
29
+
30
+ **Returns:**
31
+ - (OpenSearch::Sugar::Client) - A new client instance
32
+
33
+ **Example:**
34
+ ```ruby
35
+ client = OpenSearch::Sugar.client(
36
+ host: 'https://localhost:9200',
37
+ user: 'admin',
38
+ password: 'admin'
39
+ )
40
+ ```
41
+
42
+ #### `OpenSearch::Sugar.new(**kwargs)`
43
+
44
+ Alias for `OpenSearch::Sugar.client`. Creates a new client instance.
45
+
46
+ **Parameters:**
47
+ - `**kwargs` (Hash) - Connection parameters
48
+
49
+ **Returns:**
50
+ - (OpenSearch::Sugar::Client) - A new client instance
51
+
52
+ **Example:**
53
+ ```ruby
54
+ client = OpenSearch::Sugar.new
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Class: OpenSearch::Sugar::Client
60
+
61
+ The main client class that wraps the OpenSearch::Client. Inherits from SimpleDelegator, providing access to all OpenSearch::Client methods while adding convenient sugar methods.
62
+
63
+ **Inherits:** SimpleDelegator
64
+
65
+ **See Also:**
66
+ - [OpenSearch Ruby Client Documentation](https://opensearch.org/docs/latest/clients/ruby/)
67
+ - [OpenSearch API Reference](https://opensearch.org/docs/latest/api-reference/)
68
+
69
+ ### Class Methods
70
+
71
+ #### `.raw_client(*args, **kwargs)`
72
+
73
+ Creates a new raw OpenSearch::Client instance without the sugar wrapper.
74
+
75
+ **Parameters:**
76
+ - `*args` (Array) - Positional arguments for OpenSearch::Client
77
+ - `**kwargs` (Hash) - Keyword arguments for OpenSearch::Client
78
+
79
+ **Returns:**
80
+ - (OpenSearch::Client) - A raw OpenSearch client instance
81
+
82
+ **Example:**
83
+ ```ruby
84
+ raw = OpenSearch::Sugar::Client.raw_client(
85
+ host: 'https://localhost:9200'
86
+ )
87
+ ```
88
+
89
+ #### `.new(**kwargs)`
90
+
91
+ Creates a new OpenSearch::Sugar::Client instance.
92
+
93
+ **Parameters:**
94
+ - `host` (String) - OpenSearch host URL (default: `ENV['OPENSEARCH_URL']` or `ENV['OPENSEARCH_HOST']` or `'https://localhost:9000'`)
95
+ - `user` (String) - Username (default: `ENV['OPENSEARCH_USER']` or `'admin'`)
96
+ - `password` (String) - Password (default: `ENV['OPENSEARCH_PASSWORD']` or `ENV['OPENSEARCH_INITIAL_ADMIN_PASSWORD']`)
97
+ - `retry_on_failure` (Integer) - Number of retries (default: 5)
98
+ - `request_timeout` (Integer) - Request timeout in seconds (default: 5)
99
+ - `log` (Boolean) - Enable logging (default: true)
100
+ - `trace` (Boolean) - Enable trace logging (default: false)
101
+ - `transport_options` (Hash) - HTTP transport options including SSL settings
102
+ - `**kwargs` (Hash) - Additional options passed to OpenSearch::Client
103
+
104
+ **Returns:**
105
+ - (OpenSearch::Sugar::Client) - A new client instance
106
+
107
+ **Example:**
108
+ ```ruby
109
+ client = OpenSearch::Sugar::Client.new(
110
+ host: 'https://search.example.com:9200',
111
+ user: 'myuser',
112
+ password: 'secret',
113
+ retry_on_failure: 3,
114
+ request_timeout: 10,
115
+ log: false,
116
+ transport_options: {
117
+ ssl: { verify: true }
118
+ }
119
+ )
120
+ ```
121
+
122
+ ### Instance Attributes
123
+
124
+ #### `#raw_client`
125
+
126
+ **Type:** OpenSearch::Client (readonly)
127
+
128
+ **Description:** The underlying raw OpenSearch::Client instance.
129
+
130
+ **Example:**
131
+ ```ruby
132
+ raw = client.raw_client
133
+ response = raw.cluster.health
134
+ ```
135
+
136
+ #### `#models`
137
+
138
+ **Type:** OpenSearch::Sugar::Models (readonly)
139
+
140
+ **Description:** The models interface for managing ML models.
141
+
142
+ **Example:**
143
+ ```ruby
144
+ models = client.models
145
+ models.list
146
+ ```
147
+
148
+ ### Instance Methods
149
+
150
+ #### `#default_args`
151
+
152
+ Returns the default connection arguments used by the client.
153
+
154
+ **Returns:**
155
+ - (Hash) - Default connection parameters
156
+
157
+ **Example:**
158
+ ```ruby
159
+ defaults = client.default_args
160
+ puts defaults[:host]
161
+ # => "https://localhost:9000"
162
+ ```
163
+
164
+ #### `#set_log_level(logger: 'logger._root', level: 'warn')`
165
+
166
+ Sets the logging level for OpenSearch cluster loggers.
167
+
168
+ **Parameters:**
169
+ - `logger` (String) - Logger name (default: `'logger._root'`)
170
+ - `level` (String) - Log level: `'trace'`, `'debug'`, `'info'`, `'warn'`, `'error'`, `'fatal'`
171
+
172
+ **Returns:**
173
+ - (Hash) - OpenSearch response
174
+
175
+ **See:** [OpenSearch Logging Documentation](https://opensearch.org/docs/latest/install-and-configure/configuring-opensearch/logs/)
176
+
177
+ **Example:**
178
+ ```ruby
179
+ client.set_log_level(logger: 'logger._root', level: 'warn')
180
+ client.set_log_level(logger: 'index.search.slowlog', level: 'debug')
181
+ ```
182
+
183
+ #### `#has_index?(name)`
184
+
185
+ Checks if an index exists in OpenSearch.
186
+
187
+ **Parameters:**
188
+ - `name` (String) - The index name to check
189
+
190
+ **Returns:**
191
+ - (Boolean) - `true` if index exists, `false` otherwise
192
+
193
+ **Example:**
194
+ ```ruby
195
+ if client.has_index?('my_index')
196
+ puts "Index exists"
197
+ end
198
+ ```
199
+
200
+ #### `#index_names`
201
+
202
+ Returns a list of all index names in the cluster.
203
+
204
+ **Returns:**
205
+ - (Array<String>) - Array of index names
206
+
207
+ **Example:**
208
+ ```ruby
209
+ names = client.index_names
210
+ # => ["index1", "index2", "index3"]
211
+ ```
212
+
213
+ #### `#[](index_name)`
214
+
215
+ Opens an existing index by name.
216
+
217
+ **Parameters:**
218
+ - `index_name` (String) - The name of the index to open
219
+
220
+ **Returns:**
221
+ - (OpenSearch::Sugar::Index) - The index instance
222
+
223
+ **Raises:**
224
+ - (ArgumentError) - If index does not exist
225
+
226
+ **Example:**
227
+ ```ruby
228
+ index = client['my_index']
229
+ ```
230
+
231
+ #### `#open_or_create_index(index_name)`
232
+
233
+ Opens an existing index or creates it if it doesn't exist.
234
+
235
+ **Parameters:**
236
+ - `index_name` (String) - The index name
237
+
238
+ **Returns:**
239
+ - (OpenSearch::Sugar::Index) - The index instance
240
+
241
+ **Example:**
242
+ ```ruby
243
+ index = client.open_or_create_index('my_index')
244
+ ```
245
+
246
+ #### `#delete_index!(index_name)`
247
+
248
+ Deletes the named index and all its data.
249
+
250
+ **Parameters:**
251
+ - `index_name` (String) - The name of the index to delete
252
+
253
+ **Returns:**
254
+ - (Hash) - OpenSearch acknowledgement response
255
+
256
+ **Warning:** This operation is irreversible!
257
+
258
+ **Example:**
259
+ ```ruby
260
+ client.delete_index!('my_index')
261
+ ```
262
+
263
+ #### `#update_settings(settings, index_name)`
264
+
265
+ Updates settings for an index. Automatically closes and reopens the index.
266
+
267
+ **Parameters:**
268
+ - `settings` (Hash) - The settings hash (with or without a top-level `:settings` key)
269
+ - `index_name` (String) - The name of the index to update
270
+
271
+ **Returns:**
272
+ - (Hash) - The raw OpenSearch response from reopening the index
273
+
274
+ **Example:**
275
+ ```ruby
276
+ client.update_settings(
277
+ {
278
+ settings: {
279
+ analysis: {
280
+ analyzer: {
281
+ my_analyzer: {
282
+ type: 'standard',
283
+ stopwords: ['the', 'a']
284
+ }
285
+ }
286
+ }
287
+ }
288
+ },
289
+ 'my_index'
290
+ )
291
+ ```
292
+
293
+ #### `#update_mappings(mappings, index_name)`
294
+
295
+ Updates mappings for an index. Automatically closes and reopens the index.
296
+
297
+ **Parameters:**
298
+ - `mappings` (Hash) - The mappings hash (with or without a top-level `:mappings` key)
299
+ - `index_name` (String) - The name of the index to update
300
+
301
+ **Returns:**
302
+ - (Hash) - The raw OpenSearch response from reopening the index
303
+
304
+ **Example:**
305
+ ```ruby
306
+ result = client.update_mappings(
307
+ {
308
+ mappings: {
309
+ properties: {
310
+ title: { type: 'text' },
311
+ created_at: { type: 'date' }
312
+ }
313
+ }
314
+ },
315
+ 'my_index'
316
+ )
317
+ ```
318
+
319
+ ### Delegated Methods
320
+
321
+ Because `Client` inherits from `SimpleDelegator` and wraps `OpenSearch::Client`, all OpenSearch client methods are available:
322
+
323
+ **Index Operations:**
324
+ - `indices.create`, `indices.delete`, `indices.exists?`, `indices.refresh`
325
+ - `indices.open`, `indices.close`, `indices.get_settings`, `indices.put_settings`
326
+ - `indices.get_mapping`, `indices.put_mapping`, `indices.update_aliases`
327
+
328
+ **Document Operations:**
329
+ - `index`, `get`, `update`, `delete`, `bulk`
330
+ - `search`, `count`, `delete_by_query`, `update_by_query`
331
+
332
+ **Cluster Operations:**
333
+ - `cluster.health`, `cluster.state`, `cluster.stats`, `cluster.get_settings`
334
+
335
+ **Analysis:**
336
+ - `indices.analyze`
337
+
338
+ See the [OpenSearch Ruby Client](https://github.com/opensearch-project/opensearch-ruby) and [OpenSearch API documentation](https://opensearch.org/docs/latest/api-reference/) for complete method listings.
339
+
340
+ ---
341
+
342
+ ## Class: OpenSearch::Sugar::Index
343
+
344
+ Represents an OpenSearch index with convenient methods for index operations.
345
+
346
+ **See Also:**
347
+ - [OpenSearch Index APIs](https://opensearch.org/docs/latest/api-reference/index-apis/)
348
+
349
+ ### Class Methods
350
+
351
+ #### `.open(client:, name:)`
352
+
353
+ Opens an existing index.
354
+
355
+ **Parameters:**
356
+ - `client` (OpenSearch::Sugar::Client) - The client instance
357
+ - `name` (String) - The index name
358
+
359
+ **Returns:**
360
+ - (OpenSearch::Sugar::Index) - The index instance
361
+
362
+ **Raises:**
363
+ - (ArgumentError) - If the index does not exist
364
+
365
+ **Example:**
366
+ ```ruby
367
+ index = OpenSearch::Sugar::Index.open(
368
+ client: client,
369
+ name: 'my_index'
370
+ )
371
+ ```
372
+
373
+ #### `.create(client:, name:, knn: true)`
374
+
375
+ Creates a new index.
376
+
377
+ **Parameters:**
378
+ - `client` (OpenSearch::Sugar::Client) - The client instance
379
+ - `name` (String) - The index name
380
+ - `knn` (Boolean) - Enable k-NN functionality (default: true)
381
+
382
+ **Returns:**
383
+ - (OpenSearch::Sugar::Index) - The newly created index instance
384
+
385
+ **Raises:**
386
+ - (ArgumentError) - If the index already exists
387
+
388
+ **Example:**
389
+ ```ruby
390
+ index = OpenSearch::Sugar::Index.create(
391
+ client: client,
392
+ name: 'my_index',
393
+ knn: true
394
+ )
395
+ ```
396
+
397
+ ### Instance Attributes
398
+
399
+ #### `#client`
400
+
401
+ **Type:** OpenSearch::Sugar::Client
402
+
403
+ **Description:** The client instance this index belongs to.
404
+
405
+ #### `#name`
406
+
407
+ **Type:** String
408
+
409
+ **Description:** The name of this index.
410
+
411
+ ### Instance Methods
412
+
413
+ #### `#settings`
414
+
415
+ Gets the current settings for this index.
416
+
417
+ **Returns:**
418
+ - (Hash) - The index settings
419
+
420
+ **Example:**
421
+ ```ruby
422
+ settings = index.settings
423
+ analyzer = settings.dig('my_index', 'settings', 'index', 'analysis', 'analyzer')
424
+ ```
425
+
426
+ #### `#update_settings(settings)`
427
+
428
+ Updates the settings for this index.
429
+
430
+ **Parameters:**
431
+ - `settings` (Hash) - The settings to apply
432
+
433
+ **Returns:**
434
+ - (Hash) - The raw OpenSearch response from reopening the index
435
+
436
+ **Example:**
437
+ ```ruby
438
+ index.update_settings(
439
+ settings: {
440
+ number_of_replicas: 2,
441
+ refresh_interval: '5s'
442
+ }
443
+ )
444
+ ```
445
+
446
+ #### `#mappings`
447
+
448
+ Gets the current mappings for this index.
449
+
450
+ **Returns:**
451
+ - (Hash) - The index mappings
452
+
453
+ **Example:**
454
+ ```ruby
455
+ mappings = index.mappings
456
+ properties = mappings.dig('my_index', 'mappings', 'properties')
457
+ ```
458
+
459
+ #### `#update_mappings(mappings)`
460
+
461
+ Updates the mappings for this index.
462
+
463
+ **Parameters:**
464
+ - `mappings` (Hash) - The mappings to apply
465
+
466
+ **Returns:**
467
+ - (Hash) - The raw OpenSearch response from reopening the index
468
+
469
+ **Example:**
470
+ ```ruby
471
+ index.update_mappings(
472
+ mappings: {
473
+ properties: {
474
+ title: { type: 'text' },
475
+ tags: { type: 'keyword' }
476
+ }
477
+ }
478
+ )
479
+ ```
480
+
481
+ #### `#count`
482
+
483
+ Returns the number of documents in this index.
484
+
485
+ **Returns:**
486
+ - (Integer) - Document count
487
+
488
+ **Example:**
489
+ ```ruby
490
+ count = index.count
491
+ puts "Total documents: #{count}"
492
+ ```
493
+
494
+ #### `#delete!`
495
+
496
+ Permanently deletes this index and all its data.
497
+
498
+ **Returns:**
499
+ - (Hash) - OpenSearch response
500
+
501
+ **Warning:** This operation is irreversible!
502
+
503
+ **Example:**
504
+ ```ruby
505
+ index.delete!
506
+ ```
507
+
508
+ #### `#refresh`
509
+
510
+ Forces an index refresh, making all recently indexed documents immediately visible to searches.
511
+ Useful after bulk indexing operations when you need immediate search visibility.
512
+
513
+ **Returns:**
514
+ - (Hash) - OpenSearch response
515
+
516
+ **Example:**
517
+ ```ruby
518
+ index.index_document({title: "hello"}, "1")
519
+ index.refresh
520
+ puts index.count # => 1
521
+ ```
522
+
523
+ #### `#clear!`
524
+
525
+ Deletes all documents from this index using delete_by_query.
526
+
527
+ **Returns:**
528
+ - (Integer) - Number of documents deleted
529
+
530
+ **Example:**
531
+ ```ruby
532
+ deleted = index.clear!
533
+ puts "Deleted #{deleted} documents"
534
+ ```
535
+
536
+ #### `#delete_by_id(id)`
537
+
538
+ Deletes a single document by ID.
539
+
540
+ **Parameters:**
541
+ - `id` (String) - The document ID to delete
542
+
543
+ **Returns:**
544
+ - (Hash) - OpenSearch response
545
+
546
+ **Raises:**
547
+ - (ArgumentError) - If ID is nil or empty
548
+
549
+ **Example:**
550
+ ```ruby
551
+ index.delete_by_id('doc123')
552
+ ```
553
+
554
+ #### `#aliases`
555
+
556
+ Returns the list of aliases for this index.
557
+
558
+ **Returns:**
559
+ - (Array<String>) - Array of alias names
560
+
561
+ **Example:**
562
+ ```ruby
563
+ aliases = index.aliases
564
+ # => ["alias1", "alias2"]
565
+ ```
566
+
567
+ #### `#create_alias(alias_name)`
568
+
569
+ Creates an alias for this index.
570
+
571
+ **Parameters:**
572
+ - `alias_name` (String) - The alias name to create
573
+
574
+ **Returns:**
575
+ - (Array<String>) - Updated list of all aliases
576
+
577
+ **Example:**
578
+ ```ruby
579
+ index.create_alias('my_alias')
580
+ ```
581
+
582
+ #### `#all_available_analyzers` / `#analyzers`
583
+
584
+ Returns all analyzers available to this index (both index-level and cluster-level).
585
+
586
+ **Returns:**
587
+ - (Array<String>) - Array of analyzer names
588
+
589
+ **Example:**
590
+ ```ruby
591
+ analyzers = index.analyzers
592
+ # => ["my_custom_analyzer", "book_title_analyzer"]
593
+ ```
594
+
595
+ **Note:** Only analyzers explicitly defined in index settings or cluster persistent settings are returned.
596
+ Built-in OpenSearch analyzers (e.g., `standard`, `simple`) are not included.
597
+
598
+ #### `#analyze_text(analyzer:, text:)`
599
+
600
+ Analyzes text using a specified analyzer and returns the resulting tokens.
601
+
602
+ **Parameters:**
603
+ - `analyzer` (String) - The analyzer name to use
604
+ - `text` (String) - The text to analyze
605
+
606
+ **Returns:**
607
+ - (Array<String, Array<String>>) - Array of tokens; if multiple tokens exist at the same position, they're grouped in a sub-array
608
+
609
+ **Raises:**
610
+ - (ArgumentError) - If the analyzer doesn't exist in the index
611
+
612
+ **See:** [OpenSearch Analyze API](https://opensearch.org/docs/latest/api-reference/analyze-apis/)
613
+
614
+ **Example:**
615
+ ```ruby
616
+ tokens = index.analyze_text(
617
+ analyzer: 'my_custom_analyzer',
618
+ text: 'The quick brown fox'
619
+ )
620
+ # => ["quick", "brown", "fox"]
621
+ ```
622
+
623
+ **Note:** The analyzer must be explicitly defined in the index settings.
624
+ Built-in analyzers such as `standard` are not listed in index settings and will raise `ArgumentError`.
625
+
626
+ #### `#analyze_text_field(field:, text:)`
627
+
628
+ Analyzes text using the analyzer configured for a specific field.
629
+
630
+ **Parameters:**
631
+ - `field` (String) - The field name whose analyzer to use
632
+ - `text` (String) - The text to analyze
633
+
634
+ **Returns:**
635
+ - (Array<String, Array<String>>) - Array of tokens
636
+
637
+ **Raises:**
638
+ - (ArgumentError) - If the field doesn't exist or has no analyzer
639
+
640
+ **Example:**
641
+ ```ruby
642
+ tokens = index.analyze_text_field(
643
+ field: 'title',
644
+ text: 'My Document Title'
645
+ )
646
+ ```
647
+
648
+ ---
649
+
650
+ ## Class: OpenSearch::Sugar::Models
651
+
652
+ Manages machine learning models in OpenSearch.
653
+
654
+ **See Also:**
655
+ - [OpenSearch ML Commons Plugin](https://opensearch.org/docs/latest/ml-commons-plugin/)
656
+
657
+ ### Nested Classes
658
+
659
+ #### `ML_INFO`
660
+
661
+ A Struct containing model information.
662
+
663
+ **Fields:**
664
+ - `name` (String) - Model name
665
+ - `version` (String) - Model version
666
+ - `id` (String) - Internal model ID
667
+
668
+ ### Instance Methods
669
+
670
+ #### `#initialize(os)`
671
+
672
+ Creates a new Models instance.
673
+
674
+ **Parameters:**
675
+ - `os` (OpenSearch::Sugar::Client) - The client instance
676
+
677
+ **Note:** Typically accessed via `client.models`, not instantiated directly.
678
+
679
+ #### `#register(name:, version:, format: 'TORCH_SCRIPT')` / `#deploy(...)`
680
+
681
+ Registers and deploys a machine learning model. Waits for deployment to complete.
682
+
683
+ **Parameters:**
684
+ - `name` (String) - Full model name (e.g., `'huggingface/sentence-transformers/all-MiniLM-L12-v2'`)
685
+ - `version` (String) - Model version
686
+ - `format` (String) - Model format (default: `'TORCH_SCRIPT'`)
687
+
688
+ **Returns:**
689
+ - (ML_INFO) - Model information struct, or existing model if already registered
690
+
691
+ **Example:**
692
+ ```ruby
693
+ model = client.models.register(
694
+ name: 'huggingface/sentence-transformers/all-MiniLM-L12-v2',
695
+ version: '1.0.1',
696
+ format: 'TORCH_SCRIPT'
697
+ )
698
+
699
+ puts "Deployed: #{model.name} v#{model.version}"
700
+ ```
701
+
702
+ #### `#list`
703
+
704
+ Returns a list of all registered ML models.
705
+
706
+ **Returns:**
707
+ - (Array<ML_INFO>) - Array of model information structs
708
+
709
+ **Example:**
710
+ ```ruby
711
+ models = client.models.list
712
+ models.each do |model|
713
+ puts "#{model.name} v#{model.version} (#{model.id})"
714
+ end
715
+ ```
716
+
717
+ #### `#[](id_or_fullname_or_nickname)`
718
+
719
+ Finds a model by ID, exact name, or partial name match.
720
+
721
+ **Parameters:**
722
+ - `id_or_fullname_or_nickname` (String) - Model identifier
723
+
724
+ **Returns:**
725
+ - (ML_INFO, nil) - Model information or nil if not found
726
+
727
+ **Behavior:**
728
+ 1. Tries exact name match first
729
+ 2. Then tries ID match
730
+ 3. Finally tries case-insensitive regex match on name, returning latest version
731
+
732
+ **Example:**
733
+ ```ruby
734
+ # By exact name
735
+ model = client.models['huggingface/sentence-transformers/all-MiniLM-L12-v2']
736
+
737
+ # By ID
738
+ model = client.models['abc123']
739
+
740
+ # By nickname (case-insensitive, partial match)
741
+ model = client.models['minilm'] # Finds latest MiniLM model
742
+ ```
743
+
744
+ #### `#raw_list`
745
+
746
+ Returns the raw OpenSearch response for model listing.
747
+
748
+ **Returns:**
749
+ - (Hash) - Raw OpenSearch response
750
+
751
+ **Example:**
752
+ ```ruby
753
+ raw = client.models.raw_list
754
+ hits = raw['hits']['hits']
755
+ ```
756
+
757
+ #### `#undeploy!(name_or_id)`
758
+
759
+ Undeploys a model by name or ID.
760
+
761
+ **Parameters:**
762
+ - `name_or_id` (String) - Model name or ID
763
+
764
+ **Returns:**
765
+ - (Hash) - OpenSearch response
766
+
767
+ **Example:**
768
+ ```ruby
769
+ client.models.undeploy!('all-MiniLM-L12-v2')
770
+ ```
771
+
772
+ #### `#delete!(name_or_id)`
773
+
774
+ Deletes a model by name or ID. Automatically undeploys first.
775
+
776
+ **Parameters:**
777
+ - `name_or_id` (String) - Model name or ID
778
+
779
+ **Returns:**
780
+ - (Hash) - OpenSearch response
781
+
782
+ **Example:**
783
+ ```ruby
784
+ client.models.delete!('all-MiniLM-L12-v2')
785
+ ```
786
+
787
+ #### `#create_pipeline(name:, model:, description:, field_map:)`
788
+
789
+ Creates an ingest pipeline that uses a model to generate embeddings.
790
+
791
+ **Parameters:**
792
+ - `name` (String) - Pipeline name (spaces converted to underscores)
793
+ - `model` (String) - Model name or ID
794
+ - `description` (String) - Pipeline description
795
+ - `field_map` (Hash) - Map of source field to target embedding field
796
+
797
+ **Returns:**
798
+ - (Hash) - OpenSearch response
799
+
800
+ **Example:**
801
+ ```ruby
802
+ client.models.create_pipeline(
803
+ name: 'text_embedding_pipeline',
804
+ model: 'all-MiniLM-L12-v2',
805
+ description: 'Generate text embeddings',
806
+ field_map: {
807
+ 'content' => 'content_embedding',
808
+ 'title' => 'title_embedding'
809
+ }
810
+ )
811
+
812
+ # Use the pipeline when indexing
813
+ client.index(
814
+ index: 'my_index',
815
+ pipeline: 'text_embedding_pipeline',
816
+ body: {
817
+ title: 'My Document',
818
+ content: 'Document text here'
819
+ }
820
+ )
821
+ ```
822
+
823
+ ---
824
+
825
+ ## Error Classes
826
+
827
+ ### `OpenSearch::Sugar::Error`
828
+
829
+ Base error class for all OpenSearch::Sugar errors.
830
+
831
+ **Inherits:** StandardError
832
+
833
+ **Example:**
834
+ ```ruby
835
+ begin
836
+ # Operation that might fail
837
+ rescue OpenSearch::Sugar::Error => e
838
+ puts "OpenSearch::Sugar error: #{e.message}"
839
+ end
840
+ ```
841
+
842
+ ### ArgumentError
843
+
844
+ Ruby's standard ArgumentError is raised for invalid arguments.
845
+
846
+ **Common cases:**
847
+ - Index doesn't exist when calling `Index.open`
848
+ - Index already exists when calling `Index.create`
849
+ - Analyzer doesn't exist in `analyze_text`
850
+ - Field doesn't exist in `analyze_text_field`
851
+ - Document ID is nil/empty in `delete_by_id`
852
+
853
+ **Example:**
854
+ ```ruby
855
+ begin
856
+ index = OpenSearch::Sugar::Index.open(client: client, name: 'nonexistent')
857
+ rescue ArgumentError => e
858
+ puts "Error: #{e.message}"
859
+ # => "Index nonexistent not found"
860
+ end
861
+ ```
862
+
863
+ ### OpenSearch::Transport::Transport::Error
864
+
865
+ Errors from the underlying OpenSearch transport layer.
866
+
867
+ **Common subclasses:**
868
+ - `OpenSearch::Transport::Transport::Errors::NotFound` - Resource not found (404)
869
+ - `OpenSearch::Transport::Transport::Errors::BadRequest` - Invalid request (400)
870
+ - `OpenSearch::Transport::Transport::Errors::Unauthorized` - Authentication failed (401)
871
+ - `OpenSearch::Transport::Transport::Errors::Forbidden` - Permission denied (403)
872
+
873
+ **Example:**
874
+ ```ruby
875
+ begin
876
+ client.search(index: 'nonexistent', body: {query: {match_all: {}}})
877
+ rescue OpenSearch::Transport::Transport::Errors::NotFound => e
878
+ puts "Index not found: #{e.message}"
879
+ rescue OpenSearch::Transport::Transport::Error => e
880
+ puts "Transport error: #{e.message}"
881
+ end
882
+ ```
883
+
884
+ ---
885
+
886
+ ## Type Signatures
887
+
888
+ For type checking with tools like Sorbet or Steep, see the [RBS type signatures](../sig/opensearch/sugar.rbs) in the `sig/` directory.
889
+
890
+ ---
891
+
892
+ ## Additional Resources
893
+
894
+ - [OpenSearch Documentation](https://opensearch.org/docs/latest/)
895
+ - [OpenSearch Ruby Client](https://github.com/opensearch-project/opensearch-ruby)
896
+ - [OpenSearch API Reference](https://opensearch.org/docs/latest/api-reference/)
897
+ - [OpenSearch ML Commons](https://opensearch.org/docs/latest/ml-commons-plugin/)
898
+ - [Tutorial](TUTORIAL.md) - Step-by-step learning guide
899
+ - [How-to Guides](HOWTO.md) - Problem-solving recipes
900
+ - [Explanation](EXPLANATION.md) - Understanding concepts
901
+