expressir 2.1.29 → 2.1.31

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +98 -0
  3. data/.github/workflows/links.yml +100 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.github/workflows/validate_schemas.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +209 -55
  10. data/Gemfile +2 -1
  11. data/README.adoc +650 -83
  12. data/docs/Gemfile +12 -0
  13. data/docs/_config.yml +141 -0
  14. data/docs/_guides/changes/changes-format.adoc +778 -0
  15. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  16. data/docs/_guides/changes/index.adoc +396 -0
  17. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  18. data/docs/_guides/changes/validating-changes.adoc +681 -0
  19. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  20. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  21. data/docs/_guides/cli/format-schemas.adoc +547 -0
  22. data/docs/_guides/cli/index.adoc +8 -0
  23. data/docs/_guides/cli/managing-changes.adoc +927 -0
  24. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  25. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  26. data/docs/_guides/index.adoc +165 -0
  27. data/docs/_guides/ler/creating-packages.adoc +664 -0
  28. data/docs/_guides/ler/index.adoc +305 -0
  29. data/docs/_guides/ler/loading-packages.adoc +707 -0
  30. data/docs/_guides/ler/package-formats.adoc +748 -0
  31. data/docs/_guides/ler/querying-packages.adoc +826 -0
  32. data/docs/_guides/ler/validating-packages.adoc +750 -0
  33. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  34. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  35. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  36. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  37. data/docs/_guides/liquid/index.adoc +468 -0
  38. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  39. data/docs/_guides/manifests/index.adoc +307 -0
  40. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  41. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  42. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  43. data/docs/_guides/ruby-api/index.adoc +257 -0
  44. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  45. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  46. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  47. data/docs/_pages/data-model.adoc +665 -0
  48. data/docs/_pages/express-language.adoc +506 -0
  49. data/docs/_pages/getting-started.adoc +414 -0
  50. data/docs/_pages/index.adoc +116 -0
  51. data/docs/_pages/introduction.adoc +256 -0
  52. data/docs/_pages/ler-packages.adoc +837 -0
  53. data/docs/_pages/parsers.adoc +683 -0
  54. data/docs/_pages/schema-manifests.adoc +431 -0
  55. data/docs/_references/index.adoc +228 -0
  56. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  57. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  58. data/docs/_tutorials/index.adoc +221 -0
  59. data/docs/_tutorials/liquid-templates.adoc +806 -0
  60. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  61. data/docs/_tutorials/querying-schemas.adoc +751 -0
  62. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  63. data/docs/index.adoc +242 -0
  64. data/docs/lychee.toml +84 -0
  65. data/examples/demo_ler_usage.sh +86 -0
  66. data/examples/ler/README.md +111 -0
  67. data/examples/ler/simple_example.ler +0 -0
  68. data/examples/ler/simple_schema.exp +33 -0
  69. data/examples/ler_build.rb +75 -0
  70. data/examples/ler_cli.rb +79 -0
  71. data/examples/ler_demo_complete.rb +276 -0
  72. data/examples/ler_query.rb +91 -0
  73. data/examples/ler_query_examples.rb +305 -0
  74. data/examples/ler_stats.rb +81 -0
  75. data/examples/phase3_demo.rb +159 -0
  76. data/examples/query_demo_simple.rb +131 -0
  77. data/expressir.gemspec +2 -0
  78. data/lib/expressir/changes/schema_change.rb +32 -22
  79. data/lib/expressir/changes/{edition_change.rb → version_change.rb} +3 -3
  80. data/lib/expressir/cli.rb +12 -4
  81. data/lib/expressir/commands/changes_import_eengine.rb +2 -2
  82. data/lib/expressir/commands/changes_validate.rb +1 -1
  83. data/lib/expressir/commands/manifest.rb +427 -0
  84. data/lib/expressir/commands/package.rb +1274 -0
  85. data/lib/expressir/commands/validate.rb +70 -37
  86. data/lib/expressir/commands/validate_ascii.rb +607 -0
  87. data/lib/expressir/commands/validate_load.rb +88 -0
  88. data/lib/expressir/express/formatter.rb +5 -1
  89. data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
  90. data/lib/expressir/express/parser.rb +33 -0
  91. data/lib/expressir/manifest/resolver.rb +213 -0
  92. data/lib/expressir/manifest/validator.rb +195 -0
  93. data/lib/expressir/model/declarations/entity.rb +6 -0
  94. data/lib/expressir/model/dependency_resolver.rb +270 -0
  95. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  96. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  97. data/lib/expressir/model/indexes/type_index.rb +149 -0
  98. data/lib/expressir/model/interface_validator.rb +384 -0
  99. data/lib/expressir/model/repository.rb +400 -5
  100. data/lib/expressir/model/repository_validator.rb +295 -0
  101. data/lib/expressir/model/search_engine.rb +525 -0
  102. data/lib/expressir/model.rb +4 -94
  103. data/lib/expressir/package/builder.rb +200 -0
  104. data/lib/expressir/package/metadata.rb +81 -0
  105. data/lib/expressir/package/reader.rb +165 -0
  106. data/lib/expressir/schema_manifest.rb +11 -1
  107. data/lib/expressir/version.rb +1 -1
  108. data/lib/expressir.rb +16 -3
  109. metadata +115 -5
  110. data/docs/benchmarking.adoc +0 -107
  111. data/docs/liquid_drops.adoc +0 -1547
@@ -0,0 +1,609 @@
1
+ ---
2
+ title: Search Engine
3
+ parent: Ruby API
4
+ grand_parent: Guides
5
+ nav_order: 3
6
+ ---
7
+
8
+ = Search Engine
9
+
10
+ == Purpose
11
+
12
+ The SearchEngine provides powerful querying and filtering capabilities for
13
+ EXPRESS elements within a repository. It supports pattern matching, wildcards,
14
+ regular expressions, and type filtering to locate specific declarations across
15
+ schemas.
16
+
17
+ == References
18
+
19
+ * link:working-with-repository.html[Working with Repository] - Repository basics
20
+ * link:../../tutorials/querying-schemas.html[Tutorial: Querying Schemas]
21
+
22
+ == Concepts
23
+
24
+ search_engine:: Component for querying EXPRESS elements in a repository
25
+ pattern_matching:: Finding elements using wildcards or regular expressions
26
+ type_filtering:: Limiting search results by element type
27
+ qualified_name:: Full path to an element (e.g., "schema.entity.attribute")
28
+ relevance_ranking:: Scoring search results by match quality
29
+
30
+ == Initialization
31
+
32
+ Create a SearchEngine with a repository:
33
+
34
+ [source,ruby]
35
+ ----
36
+ require "expressir"
37
+
38
+ # Parse schemas
39
+ repository = Expressir::Express::Parser.from_file("schema.exp")
40
+
41
+ # Create search engine
42
+ engine = Expressir::Model::SearchEngine.new(repository)
43
+ ----
44
+
45
+ The engine automatically builds indexes on first use.
46
+
47
+ == Element types
48
+
49
+ The SearchEngine can query these EXPRESS element types:
50
+
51
+ Schema-level elements::
52
+ * `schema` - Schema definitions
53
+ * `entity` - Entity definitions
54
+ * `type` - Type definitions (with category filtering)
55
+ * `function` - Function definitions
56
+ * `procedure` - Procedure definitions
57
+ * `rule` - Rule definitions
58
+ * `constant` - Constant definitions
59
+ * `interface` - Interface definitions
60
+
61
+ Nested elements::
62
+ * `attribute` - Entity attributes
63
+ * `derived_attribute` - Derived attributes
64
+ * `inverse_attribute` - Inverse attributes
65
+ * `parameter` - Function/procedure parameters
66
+ * `variable` - Local variables
67
+ * `where_rule` - Where rules
68
+ * `unique_rule` - Unique rules
69
+ * `enumeration_item` - Enumeration items
70
+
71
+ == Listing elements
72
+
73
+ === List by type
74
+
75
+ Get all elements of a specific type:
76
+
77
+ [source,ruby]
78
+ ----
79
+ # List all entities
80
+ entities = engine.list(type: "entity")
81
+
82
+ entities.each do |entity|
83
+ puts "Entity: #{entity[:path]}"
84
+ puts " ID: #{entity[:id]}"
85
+ puts " Schema: #{entity[:schema]}"
86
+ end
87
+ ----
88
+
89
+ === Filter by schema
90
+
91
+ Limit results to a specific schema:
92
+
93
+ [source,ruby]
94
+ ----
95
+ # List entities only in action_schema
96
+ entities = engine.list(
97
+ type: "entity",
98
+ schema: "action_schema"
99
+ )
100
+ ----
101
+
102
+ === Filter types by category
103
+
104
+ For type elements, filter by category:
105
+
106
+ [source,ruby]
107
+ ----
108
+ # List only SELECT types
109
+ select_types = engine.list(
110
+ type: "type",
111
+ category: "select"
112
+ )
113
+
114
+ # List only ENUMERATION types
115
+ enum_types = engine.list(
116
+ type: "type",
117
+ category: "enumeration"
118
+ )
119
+ ----
120
+
121
+ Valid type categories:
122
+
123
+ * `select` - SELECT types
124
+ * `enumeration` - ENUMERATION types
125
+ * `aggregate` - AGGREGATE types
126
+ * `defined` - Defined types (e.g., INTEGER, STRING)
127
+
128
+ == Pattern searching
129
+
130
+ === Simple patterns
131
+
132
+ Search with substring matching:
133
+
134
+ [source,ruby]
135
+ ----
136
+ # Find entities containing "action"
137
+ results = engine.search(pattern: "action", type: "entity")
138
+
139
+ results.each do |result|
140
+ puts "Found: #{result[:path]}"
141
+ end
142
+ ----
143
+
144
+ === Wildcard patterns
145
+
146
+ Use `*` for wildcard matching:
147
+
148
+ [source,ruby]
149
+ ----
150
+ # Find entities starting with "action"
151
+ results = engine.search(pattern: "action*", type: "entity")
152
+
153
+ # Find entities ending with "item"
154
+ results = engine.search(pattern: "*item", type: "entity")
155
+
156
+ # Find entities containing "item" anywhere
157
+ results = engine.search(pattern: "*item*", type: "entity")
158
+ ----
159
+
160
+ === Qualified path patterns
161
+
162
+ Search with schema qualification:
163
+
164
+ [source,ruby]
165
+ ----
166
+ # Find in specific schema
167
+ results = engine.search(
168
+ pattern: "action_schema.action*",
169
+ type: "entity"
170
+ )
171
+
172
+ # Wildcard schema name
173
+ results = engine.search(
174
+ pattern: "*_schema.action",
175
+ type: "entity"
176
+ )
177
+ ----
178
+
179
+ === Regular expression patterns
180
+
181
+ Use regex for complex pattern matching:
182
+
183
+ [source,ruby]
184
+ ----
185
+ # Find entities matching regex
186
+ results = engine.search(
187
+ pattern: "action_(item|event|directive)",
188
+ type: "entity",
189
+ regex: true
190
+ )
191
+
192
+ # Case-insensitive regex
193
+ results = engine.search(
194
+ pattern: "(?i)action.*item",
195
+ type: "entity",
196
+ regex: true
197
+ )
198
+ ----
199
+
200
+ === Exact matching
201
+
202
+ Match exact element names only:
203
+
204
+ [source,ruby]
205
+ ----
206
+ results = engine.search(
207
+ pattern: "action",
208
+ type: "entity",
209
+ exact: true
210
+ )
211
+
212
+ # Only finds entities named exactly "action"
213
+ ----
214
+
215
+ == Search options
216
+
217
+ === Case sensitivity
218
+
219
+ Control case-sensitive matching:
220
+
221
+ [source,ruby]
222
+ ----
223
+ # Case-insensitive (default)
224
+ results = engine.search(
225
+ pattern: "ACTION",
226
+ type: "entity",
227
+ case_sensitive: false
228
+ )
229
+
230
+ # Case-sensitive
231
+ results = engine.search(
232
+ pattern: "Action",
233
+ type: "entity",
234
+ case_sensitive: true
235
+ )
236
+ ----
237
+
238
+ === Type filtering
239
+
240
+ Search across multiple types or specific types:
241
+
242
+ [source,ruby]
243
+ ----
244
+ # Search all types (if type not specified)
245
+ results = engine.search(pattern: "action*")
246
+
247
+ # Search specific type
248
+ results = engine.search(
249
+ pattern: "action*",
250
+ type: "entity"
251
+ )
252
+
253
+ # Search functions only
254
+ results = engine.search(
255
+ pattern: "validate*",
256
+ type: "function"
257
+ )
258
+ ----
259
+
260
+ === Combining filters
261
+
262
+ Combine multiple search criteria:
263
+
264
+ [source,ruby]
265
+ ----
266
+ results = engine.search(
267
+ pattern: "action*",
268
+ type: "entity",
269
+ schema: "action_schema",
270
+ case_sensitive: false
271
+ )
272
+ ----
273
+
274
+ == Advanced searching
275
+
276
+ === Search with depth filtering
277
+
278
+ Limit results by path depth:
279
+
280
+ [source,ruby]
281
+ ----
282
+ # Depth 1: schema level only
283
+ results = engine.search_with_depth(
284
+ pattern: "action*",
285
+ max_depth: 1
286
+ )
287
+
288
+ # Depth 2: schema.entity level
289
+ results = engine.search_with_depth(
290
+ pattern: "action*",
291
+ max_depth: 2,
292
+ type: "entity"
293
+ )
294
+
295
+ # Depth 3: schema.entity.attribute level
296
+ results = engine.search_with_depth(
297
+ pattern: "name",
298
+ max_depth: 3,
299
+ type: "attribute"
300
+ )
301
+ ----
302
+
303
+ Path depth levels:
304
+
305
+ * 1 = Schema level (e.g., `action_schema`)
306
+ * 2 = Schema.entity level (e.g., `action_schema.action`)
307
+ * 3 = Schema.entity.attribute level (e.g., `action_schema.action.name`)
308
+
309
+ === Relevance ranking
310
+
311
+ Get ranked search results by relevance:
312
+
313
+ [source,ruby]
314
+ ----
315
+ results = engine.search_ranked(
316
+ pattern: "action",
317
+ type: "entity",
318
+ boost_exact: 10, # Boost for exact matches
319
+ boost_prefix: 5 # Boost for prefix matches
320
+ )
321
+
322
+ # Results include relevance_score
323
+ results.each do |result|
324
+ puts "#{result[:path]} (score: #{result[:relevance_score]})"
325
+ end
326
+ ----
327
+
328
+ Relevance scoring factors:
329
+
330
+ * Exact match: Highest score
331
+ * Prefix match: Medium score
332
+ * Shorter paths: Higher score
333
+ * Schema-level results: Higher score
334
+
335
+ === Combined advanced search
336
+
337
+ Use all advanced features together:
338
+
339
+ [source,ruby]
340
+ ----
341
+ results = engine.search_advanced(
342
+ pattern: "action",
343
+ max_depth: 2,
344
+ ranked: true,
345
+ type: "entity",
346
+ schema: "action_schema"
347
+ )
348
+
349
+ # Results are filtered by depth and ranked by relevance
350
+ ----
351
+
352
+ == Counting elements
353
+
354
+ Get element counts without retrieving items:
355
+
356
+ [source,ruby]
357
+ ----
358
+ # Count all entities
359
+ count = engine.count(type: "entity")
360
+ puts "Total entities: #{count}"
361
+
362
+ # Count in specific schema
363
+ count = engine.count(
364
+ type: "entity",
365
+ schema: "action_schema"
366
+ )
367
+
368
+ # Count types by category
369
+ count = engine.count(
370
+ type: "type",
371
+ category: "select"
372
+ )
373
+ ----
374
+
375
+ == Working with results
376
+
377
+ === Result structure
378
+
379
+ Search results are hashes containing:
380
+
381
+ [source,ruby]
382
+ ----
383
+ result = {
384
+ id: "action", # Element identifier
385
+ type: "entity", # Element type
386
+ path: "action_schema.action", # Qualified path
387
+ schema: "action_schema", # Parent schema
388
+ category: "select" # (for types only)
389
+ }
390
+ ----
391
+
392
+ === Processing results
393
+
394
+ [source,ruby]
395
+ ----
396
+ results = engine.search(pattern: "action*", type: "entity")
397
+
398
+ # Extract IDs
399
+ ids = results.map { |r| r[:id] }
400
+
401
+ # Group by schema
402
+ by_schema = results.group_by { |r| r[:schema] }
403
+
404
+ # Filter results
405
+ filtered = results.select { |r| r[:path].include?("item") }
406
+
407
+ # Sort results
408
+ sorted = results.sort_by { |r| r[:id] }
409
+ ----
410
+
411
+ === Accessing full objects
412
+
413
+ Search returns summary hashes. To get full model objects:
414
+
415
+ [source,ruby]
416
+ ----
417
+ results = engine.search(pattern: "action", type: "entity")
418
+
419
+ # Get full entity object via repository
420
+ result = results.first
421
+ entity = engine.repository.find_entity(qualified_name: result[:path])
422
+
423
+ if entity
424
+ puts "Entity class: #{entity.class.name}"
425
+ puts "Attributes: #{entity.attributes&.size}"
426
+ end
427
+ ----
428
+
429
+ == Performance optimization
430
+
431
+ === Use specific types
432
+
433
+ Narrow searches to specific types for better performance:
434
+
435
+ [source,ruby]
436
+ ----
437
+ # Slower - searches all types
438
+ results = engine.search(pattern: "action*")
439
+
440
+ # Faster - searches only entities
441
+ results = engine.search(pattern: "action*", type: "entity")
442
+ ----
443
+
444
+ === Use schema filtering
445
+
446
+ Limit searches to specific schemas:
447
+
448
+ [source,ruby]
449
+ ----
450
+ # Faster when you know the schema
451
+ results = engine.search(
452
+ pattern: "action*",
453
+ type: "entity",
454
+ schema: "action_schema"
455
+ )
456
+ ----
457
+
458
+ === Count before retrieving
459
+
460
+ Use count to check result size before fetching:
461
+
462
+ [source,ruby]
463
+ ----
464
+ count = engine.count(pattern: "action*", type: "entity")
465
+
466
+ if count > 100
467
+ puts "Too many results, refine your search"
468
+ else
469
+ results = engine.search(pattern: "action*", type: "entity")
470
+ end
471
+ ----
472
+
473
+ === Reuse engine instances
474
+
475
+ Create one engine and reuse it for multiple searches:
476
+
477
+ [source,ruby]
478
+ ----
479
+ engine = Expressir::Model::SearchEngine.new(repository)
480
+
481
+ # Indexes are built once and reused
482
+ results1 = engine.search(pattern: "action*", type: "entity")
483
+ results2 = engine.search(pattern: "approval*", type: "entity")
484
+ results3 = engine.search(pattern: "date*", type: "type")
485
+ ----
486
+
487
+ == Common search patterns
488
+
489
+ === Find all items in a schema
490
+
491
+ [source,ruby]
492
+ ----
493
+ # All entities in a schema
494
+ entities = engine.list(
495
+ type: "entity",
496
+ schema: "action_schema"
497
+ )
498
+
499
+ # All types in a schema
500
+ types = engine.list(
501
+ type: "type",
502
+ schema: "action_schema"
503
+ )
504
+ ----
505
+
506
+ === Find by naming convention
507
+
508
+ [source,ruby]
509
+ ----
510
+ # Find all "validate" functions
511
+ validators = engine.search(
512
+ pattern: "validate*",
513
+ type: "function"
514
+ )
515
+
516
+ # Find all "_label" types
517
+ labels = engine.search(
518
+ pattern: "*_label",
519
+ type: "type"
520
+ )
521
+ ----
522
+
523
+ === Cross-schema search
524
+
525
+ [source,ruby]
526
+ ----
527
+ # Find entities across all schemas
528
+ results = engine.search(
529
+ pattern: "action*",
530
+ type: "entity"
531
+ )
532
+
533
+ # Group by schema
534
+ by_schema = results.group_by { |r| r[:schema] }
535
+ by_schema.each do |schema, items|
536
+ puts "#{schema}: #{items.size} matches"
537
+ end
538
+ ----
539
+
540
+ === Find related elements
541
+
542
+ [source,ruby]
543
+ ----
544
+ # Find entity and its attributes
545
+ entity_results = engine.search(
546
+ pattern: "action_schema.action",
547
+ type: "entity",
548
+ exact: true
549
+ )
550
+
551
+ attr_results = engine.search(
552
+ pattern: "action_schema.action.*",
553
+ type: "attribute"
554
+ )
555
+ ----
556
+
557
+ == Error handling
558
+
559
+ The SearchEngine methods generally don't raise exceptions, but return empty
560
+ results for invalid inputs:
561
+
562
+ [source,ruby]
563
+ ----
564
+ # Invalid type returns empty array
565
+ results = engine.search(pattern: "action", type: "invalid_type")
566
+ # => []
567
+
568
+ # Invalid schema returns empty array
569
+ results = engine.search(
570
+ pattern: "action",
571
+ type: "entity",
572
+ schema: "nonexistent_schema"
573
+ )
574
+ # => []
575
+
576
+ # Invalid regex returns empty array
577
+ results = engine.search(
578
+ pattern: "[invalid(",
579
+ type: "entity",
580
+ regex: true
581
+ )
582
+ # => []
583
+ ----
584
+
585
+ == Next steps
586
+
587
+ * link:formatting-schemas.html[Formatting Schemas] - Output EXPRESS code
588
+ * link:../../tutorials/querying-schemas.html[Tutorial: Querying Schemas]
589
+
590
+ == Summary
591
+
592
+ The SearchEngine provides comprehensive querying capabilities:
593
+
594
+ * List all elements of specific types
595
+ * Search with patterns (wildcards, regex, exact)
596
+ * Filter by schema and type category
597
+ * Advanced features: depth filtering, relevance ranking
598
+ * Efficient counting and performance optimization
599
+ * Works across all EXPRESS element types
600
+
601
+ Key takeaways:
602
+
603
+ * Create one engine instance and reuse it for multiple searches
604
+ * Use specific type filters for better performance
605
+ * Wildcard patterns support flexible matching
606
+ * Regex patterns enable complex searches
607
+ * Results are summary hashes; use repository to get full objects
608
+ * Depth filtering helps find elements at specific levels
609
+ * Relevance ranking sorts results by match quality