Skip to content

Commit 1a78fbe

Browse files
akoclaude
andcommitted
feat(catalog): include associations in the objects index (schema v3)
The unified `objects` view unioned every cataloged document type except associations, which lived only in the standalone associations table — so consumers treating `objects` as a complete index silently missed them. Union the associations table into `objects` (ObjectType ASSOCIATION) and bump the catalog schema to v3 so existing caches rebuild. describe auto-detect now resolves associations through the single objects lookup (fast path) instead of a separate query — a cross-module association resolves in ~3.7s via the catalog vs ~8.5s via the live-scan fallback. Adds a catalog test that associations surface in the objects view. Image collections, JavaScript actions, data transformers, and agent-editor docs have no catalog tables yet, so they remain out of the index (tracked in #658). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f991643 commit 1a78fbe

4 files changed

Lines changed: 42 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1212
- **Marketplace search caching** — the first `mxcli marketplace search` fetches the full catalog listing once and caches it under `~/.mxcli/marketplace-catalog-<profile>.json` (24h TTL, mode 0600); subsequent searches (any keyword) are served from the cache instantly. `--refresh` bypasses the cache and re-fetches. An interactive progress line ("Searching marketplace… N items scanned") shows during a fresh scan
1313
- **`describe` auto-detects the document type** — the type is now optional for a qualified name: `mxcli describe MyModule.Customer` resolves the type itself (entity, microflow, page, snippet, enumeration, constant, java action, nanoflow, workflow, association incl. cross-module, …). Resolution prefers the catalog cache (O(1) lookup, no overhead vs. the explicit form) and falls back to a live project scan when the catalog is absent. An ambiguous name (e.g. an entity and a microflow sharing a name) is reported with its candidates. The explicit `describe <type> <name>` form is unchanged, and is still required for the forms that have no single qualified name (module, settings, navigation, module role)
1414

15+
### Changed
16+
17+
- **Catalog `objects` index includes associations** — the unified `objects` view now unions the `associations` table (`ObjectType = ASSOCIATION`), so it is a complete index for the cataloged document types and consumers no longer need a separate associations query. Catalog schema bumped to v3; cached `.mxcli/catalog.db` files rebuild automatically on the next `refresh catalog`. (Image collections, JavaScript actions, data transformers, and agent-editor docs are not yet cataloged at all, so they remain out of the index — tracked in #658.)
18+
1519
### Fixed
1620

1721
- **Marketplace search now scans the whole catalog** — the Content API has no server-side search and caps `limit` at 100 per page, so `marketplace search` previously only filtered the first 100 items and silently missed matches further in (e.g. External Database Connector `219862`, Mendix Business Events `202649`). It now paginates via `offset`, fetching pages **concurrently** (first page alone so a common early match stays a single request; then bounded-parallel batches), and stops at `--limit` matches or end-of-catalog. Measured ~3m45s → ~44s on a slow link for a deep match; combined with the new cache, repeat searches are instant

cmd/mxcli/cmd_describe.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ var objectTypeToDescribe = map[string]string{
287287
"MODULE": "module",
288288
"ENTITY": "entity",
289289
"EXTERNAL_ENTITY": "entity",
290+
"ASSOCIATION": "association",
290291
"MICROFLOW": "microflow",
291292
"NANOFLOW": "nanoflow",
292293
"PAGE": "page",
@@ -389,18 +390,15 @@ func resolveViaCatalog(projectPath, name string) []string {
389390

390391
q := "'" + strings.ReplaceAll(name, "'", "''") + "'"
391392
var out []string
392-
// Documents (the objects union view).
393+
// The objects view is a complete index (associations included as of catalog
394+
// schema v3), so a single query covers every auto-detectable type.
393395
if res, err := cat.Query("SELECT DISTINCT ObjectType FROM objects WHERE QualifiedName = " + q); err == nil {
394396
for _, row := range res.Rows {
395397
if len(row) > 0 {
396398
out = append(out, objectTypeToDescribe[fmt.Sprintf("%v", row[0])])
397399
}
398400
}
399401
}
400-
// Associations are not in the objects view; check their table directly.
401-
if res, err := cat.Query("SELECT 1 FROM associations WHERE QualifiedName = " + q + " LIMIT 1"); err == nil && res.Count > 0 {
402-
out = append(out, "association")
403-
}
404402
return out
405403
}
406404

mdl/catalog/catalog_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,36 @@ func TestQueryWithData(t *testing.T) {
148148
}
149149
}
150150

151+
// TestObjectsView_IncludesAssociations verifies that associations are part of
152+
// the unified objects index (catalog schema v3). They were previously only in
153+
// the standalone associations table, forcing consumers to query it separately.
154+
func TestObjectsView_IncludesAssociations(t *testing.T) {
155+
cat, err := New()
156+
if err != nil {
157+
t.Fatalf("Failed to create catalog: %v", err)
158+
}
159+
defer cat.Close()
160+
161+
_, err = cat.CatalogDB().Exec(
162+
"INSERT INTO associations_data (Id, Name, QualifiedName, ModuleName) VALUES (?, ?, ?, ?)",
163+
"assoc-1", "Order_Customer", "Sales.Order_Customer", "Sales",
164+
)
165+
if err != nil {
166+
t.Fatalf("Failed to insert association: %v", err)
167+
}
168+
169+
result, err := cat.Query("SELECT ObjectType FROM objects WHERE QualifiedName = 'Sales.Order_Customer'")
170+
if err != nil {
171+
t.Fatalf("Query failed: %v", err)
172+
}
173+
if result.Count != 1 {
174+
t.Fatalf("Expected 1 row in objects view, got %d", result.Count)
175+
}
176+
if got := result.Rows[0][0]; got != "ASSOCIATION" {
177+
t.Errorf("ObjectType = %v, want ASSOCIATION", got)
178+
}
179+
}
180+
151181
func TestQueryError(t *testing.T) {
152182
cat, err := New()
153183
if err != nil {

mdl/catalog/tables.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ package catalog
1212
// SnapshotSource / SourceId / SourceBranch / SourceRevision columns
1313
// from every row (issue #576).
1414
// 1 — initial flat schema with denormalized snapshot columns on every row.
15-
const CatalogSchemaVersion = "2"
15+
const CatalogSchemaVersion = "3"
1616

1717
// MetaSchemaVersion is the catalog_meta key that records the schema version
1818
// the cache was built against.
@@ -771,6 +771,10 @@ func (c *Catalog) createTables() error {
771771
ProjectId, ProjectName, SnapshotId, SnapshotDate, SnapshotSource
772772
FROM entities
773773
UNION ALL
774+
SELECT Id, 'ASSOCIATION' as ObjectType, Name, QualifiedName, ModuleName, '' as Folder, Description,
775+
ProjectId, ProjectName, SnapshotId, SnapshotDate, SnapshotSource
776+
FROM associations
777+
UNION ALL
774778
SELECT Id, 'MICROFLOW' as ObjectType, Name, QualifiedName, ModuleName, Folder, Description,
775779
ProjectId, ProjectName, SnapshotId, SnapshotDate, SnapshotSource
776780
FROM microflows WHERE MicroflowType = 'MICROFLOW'

0 commit comments

Comments
 (0)