Skip to content

feat(project): add asset_class filter to project search - #137

Open
Alexbeav wants to merge 2 commits into
tumourlove:masterfrom
Alexbeav:feat/project-search-asset-class-filter
Open

feat(project): add asset_class filter to project search#137
Alexbeav wants to merge 2 commits into
tumourlove:masterfrom
Alexbeav:feat/project-search-asset-class-filter

Conversation

@Alexbeav

Copy link
Copy Markdown
Contributor

The problem

Searching a common token in a project with a large third-party content folder buries the target. Real numbers from a live project index, project search "E_" --limit 50:

class rows
UserDefinedEnum (the target) 16
Texture2D 21
NiagaraEmitter 10
StaticMesh 3

Every result row already carries asset_class. There was just no way to ask for it.

The change

asset_class on project search: a bare string ("Blueprint") or an array (["Blueprint","WidgetBlueprint"]), case-insensitive, de-duplicated, capped at 32 entries. Absent means unfiltered, i.e. existing behaviour.

The predicate is applied inside the SQL, not to the returned array. LIMIT is applied by the query, so a post-hoc filter would return fewer rows than asked for — often zero — while matching assets sat below the cut. limit now counts matching rows. Both FTS statements already JOIN assets, so no extra join was needed, and a graph-node text hit inside a Blueprint still survives a Blueprint filter (it is filtered by the class of the asset that owns the node).

Only the placeholder count is interpolated into the SQL; every class name stays bound.

The parameter is type-checked against EJson rather than read through TryGetStringField, which coerces: a numeric 7 would otherwise arrive as the string "7" and become a filter for a class of that name — zero results dressed up as a valid search instead of a -32602. Whitespace-only input is likewise a caller error rather than a silent widening.

A class no asset uses is a successful empty result, not an error.

Offline parity

Mirrored into monolith_query.exe and monolith_offline.py. verify_offline_parity.py has no project.* cases, and SPEC_MonolithIndex asks for the three to be kept in step by hand — so this follows that rather than leaving the CLIs behind.

Args::options in the C++ tool is single-valued, so both CLIs accept a comma-separated list (--asset-class A,B); the Python tool additionally accepts a repeated flag.

The second commit fixes a bug in the first: parse_args normalises hyphens to underscores, so the initial args.opt("asset-class") never matched and the filter silently did nothing. It compiled clean and had no effect — caught only by running the exe against a real index and diffing against the Python tool.

Tests

  • Monolith.ProjectSearch.AssetClassFilter — locks the in-SQL behaviour with a deliberately lopsided fixture (10 noise assets, 3 wanted) that a post-hoc implementation cannot pass by luck. Also covers case-insensitivity, multi-class union and unknown classes.
  • Monolith.ProjectSearch.AssetClassValidation — the -32602 paths.

Verification

  • UE 5.8.1 (CL 56057345) — BuildPlugin, BUILD SUCCESSFUL, 0 errors, 0 warnings
  • UE 5.7.4 (CL 51494982), the stated floor — BUILD SUCCESSFUL, 0 errors, 0 warnings
  • Every changed file lands in the non-unity compile set under Adaptive Build, so it is checked individually rather than inside a unity blob
  • Automation: 9/9 green across Monolith.ProjectSearch.* and Monolith.StructFields.*
  • Manually verified against a real 18 MB ProjectIndex.db, exe and Python agreeing exactly

Note on how it was verified

BuildPlugin on current master fails before reaching MonolithIndex:

MonolithAudioRuntime/Private/MonolithAudioPerceptionStatics.cpp
  error C2065: 'GEngine': undeclared identifier
MonolithAudioRuntime/Private/MonolithAudioRuntimeModule.cpp
  error C2061: syntax error: identifier 'MonolithAudioRuntime'   (IMPLEMENT_MODULE)

#136 touches exactly those two files. This branch was therefore built and tested on a throwaway branch with #136 merged in. It does not depend on #136 functionally and does not include it. Worth noting the release path uses host projects with different unity settings, which is presumably why this configuration is not covered by the existing gate.

Docs

CHANGELOG.md (Unreleased), SPEC_MonolithIndex.md (new Asset-Class Filtering section), Skills/unreal-project-search. README untouched — no rounded threshold crossed.

Searching a common token in a project with a large third-party content
folder buried the target: an E_ search over a real project returned 16
UserDefinedEnum rows under 21 Texture2D, 10 NiagaraEmitter and 3
StaticMesh. Every result row already carried asset_class; there was no
way to ask for it.

The predicate is applied inside the SQL rather than to the returned
array. LIMIT is applied by the query, so a post-hoc filter would return
fewer rows than requested -- frequently zero -- while matching assets sat
below the cut. limit now counts matching rows. Both FTS statements
already JOIN assets, so no extra join was needed, and a graph-node hit
inside a Blueprint still survives a Blueprint filter. Only the
placeholder count is interpolated; class names stay bound.

The parameter is type-checked against EJson rather than read through
TryGetStringField, which coerces: a numeric 7 would arrive as "7" and
become a filter for a class of that name -- zero results dressed up as a
valid search instead of -32602. Whitespace-only input is a caller error,
not a silent widening.

Mirrored into monolith_query.exe and monolith_offline.py, whose search
path verify_offline_parity.py does not gate and which SPEC_MonolithIndex
asks to be kept in step by hand.

Tests: Monolith.ProjectSearch.AssetClassFilter locks the in-SQL
behaviour with a lopsided fixture (10 noise, 3 wanted) that a post-hoc
implementation cannot pass by luck; AssetClassValidation covers -32602.
The first cut looked up args.opt("asset-class"), but parse_args
normalises hyphens to underscores, so the key is asset_class -- the
lookup never matched and the filter silently did nothing. It also was
not in value_options, so the space-separated form would not have
consumed its value even with the right key.

It compiled clean and did nothing at all; caught only by running the exe
against a real index and diffing against the Python tool.

Both --asset-class=A,B and --asset-class A,B now work, and the usage text
advertises the option.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants