expressir 2.1.30 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +98 -0
- data/.github/workflows/links.yml +100 -0
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.github/workflows/validate_schemas.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +244 -39
- data/Gemfile +2 -1
- data/README.adoc +621 -54
- data/docs/Gemfile +12 -0
- data/docs/_config.yml +141 -0
- data/docs/_guides/changes/changes-format.adoc +778 -0
- data/docs/_guides/changes/importing-eengine.adoc +898 -0
- data/docs/_guides/changes/index.adoc +396 -0
- data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
- data/docs/_guides/changes/validating-changes.adoc +681 -0
- data/docs/_guides/cli/benchmark-performance.adoc +834 -0
- data/docs/_guides/cli/coverage-analysis.adoc +921 -0
- data/docs/_guides/cli/format-schemas.adoc +547 -0
- data/docs/_guides/cli/index.adoc +8 -0
- data/docs/_guides/cli/managing-changes.adoc +927 -0
- data/docs/_guides/cli/validate-ascii.adoc +645 -0
- data/docs/_guides/cli/validate-schemas.adoc +534 -0
- data/docs/_guides/index.adoc +165 -0
- data/docs/_guides/ler/creating-packages.adoc +664 -0
- data/docs/_guides/ler/index.adoc +305 -0
- data/docs/_guides/ler/loading-packages.adoc +707 -0
- data/docs/_guides/ler/package-formats.adoc +748 -0
- data/docs/_guides/ler/querying-packages.adoc +826 -0
- data/docs/_guides/ler/validating-packages.adoc +750 -0
- data/docs/_guides/liquid/basic-templates.adoc +813 -0
- data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
- data/docs/_guides/liquid/drops-reference.adoc +829 -0
- data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
- data/docs/_guides/liquid/index.adoc +468 -0
- data/docs/_guides/manifests/creating-manifests.adoc +483 -0
- data/docs/_guides/manifests/index.adoc +307 -0
- data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
- data/docs/_guides/manifests/validating-manifests.adoc +713 -0
- data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
- data/docs/_guides/ruby-api/index.adoc +257 -0
- data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
- data/docs/_guides/ruby-api/search-engine.adoc +609 -0
- data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
- data/docs/_pages/data-model.adoc +665 -0
- data/docs/_pages/express-language.adoc +506 -0
- data/docs/_pages/getting-started.adoc +414 -0
- data/docs/_pages/index.adoc +116 -0
- data/docs/_pages/introduction.adoc +256 -0
- data/docs/_pages/ler-packages.adoc +837 -0
- data/docs/_pages/parsers.adoc +683 -0
- data/docs/_pages/schema-manifests.adoc +431 -0
- data/docs/_references/index.adoc +228 -0
- data/docs/_tutorials/creating-ler-package.adoc +735 -0
- data/docs/_tutorials/documentation-coverage.adoc +795 -0
- data/docs/_tutorials/index.adoc +221 -0
- data/docs/_tutorials/liquid-templates.adoc +806 -0
- data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
- data/docs/_tutorials/querying-schemas.adoc +751 -0
- data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
- data/docs/index.adoc +242 -0
- data/docs/lychee.toml +84 -0
- data/examples/demo_ler_usage.sh +86 -0
- data/examples/ler/README.md +111 -0
- data/examples/ler/simple_example.ler +0 -0
- data/examples/ler/simple_schema.exp +33 -0
- data/examples/ler_build.rb +75 -0
- data/examples/ler_cli.rb +79 -0
- data/examples/ler_demo_complete.rb +276 -0
- data/examples/ler_query.rb +91 -0
- data/examples/ler_query_examples.rb +305 -0
- data/examples/ler_stats.rb +81 -0
- data/examples/phase3_demo.rb +159 -0
- data/examples/query_demo_simple.rb +131 -0
- data/expressir.gemspec +2 -0
- data/lib/expressir/cli.rb +12 -4
- data/lib/expressir/commands/manifest.rb +427 -0
- data/lib/expressir/commands/package.rb +1274 -0
- data/lib/expressir/commands/validate.rb +70 -37
- data/lib/expressir/commands/validate_ascii.rb +607 -0
- data/lib/expressir/commands/validate_load.rb +88 -0
- data/lib/expressir/express/formatter.rb +5 -1
- data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
- data/lib/expressir/express/parser.rb +33 -0
- data/lib/expressir/manifest/resolver.rb +213 -0
- data/lib/expressir/manifest/validator.rb +195 -0
- data/lib/expressir/model/declarations/entity.rb +6 -0
- data/lib/expressir/model/dependency_resolver.rb +270 -0
- data/lib/expressir/model/indexes/entity_index.rb +103 -0
- data/lib/expressir/model/indexes/reference_index.rb +148 -0
- data/lib/expressir/model/indexes/type_index.rb +149 -0
- data/lib/expressir/model/interface_validator.rb +384 -0
- data/lib/expressir/model/repository.rb +400 -5
- data/lib/expressir/model/repository_validator.rb +295 -0
- data/lib/expressir/model/search_engine.rb +525 -0
- data/lib/expressir/model.rb +4 -94
- data/lib/expressir/package/builder.rb +200 -0
- data/lib/expressir/package/metadata.rb +81 -0
- data/lib/expressir/package/reader.rb +165 -0
- data/lib/expressir/schema_manifest.rb +11 -1
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +15 -2
- metadata +114 -4
- data/docs/benchmarking.adoc +0 -107
- 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
|