Conversation
…le values Properties with maxCount <= 1 (e.g. bestFriend) were always wrapped in ResultRow[] arrays after SPARQL result mapping. Now maxCount is propagated through the IR pipeline (DesugaredPropertyStep → IRTraversePattern → NestedGroup) and used in result mapping to unwrap single-value traversals to a single ResultRow or null. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Adds a selectBestFriendOnly fixture and an inline snapshot golden test that asserts the bestFriend traverse pattern carries maxCount: 1 from PropertyShape through desugar/lower to the final IRSelectQuery. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
- Changeset: patch bump for @_linked/core documenting the behavioral change - Report: docs/reports/012-fix-single-value-property-result.md with full architecture, file inventory, test coverage, and known gap https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
…fjieZ Fix single-value property traversals returning arrays
Update 3 Fuseki tests that expected bestFriend to be ResultRow[] — now it's a single ResultRow since bestFriend has maxCount: 1. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
1. selectFriends: assert p1.friends is an array of 2 entity references (p2 and p3), not just "defined and not null". This may expose a bug in flat multi-value projection (currently takes first binding only). 2. doubleNestedSubSelect: validate full nested structure — friends[0].bestFriend.name === 'Jinx' (3-level nesting). 3. nestedObjectProperty: assert exact count (1, not >=1), verify friends array contents, and check bestFriend is unwrapped single ResultRow with id containing 'p3'. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Tests now assert actual values instead of just checking existence: - selectNestedFriendsName: verify 2-level nesting structure and friend names - selectDeepNested: assert empty result (chain is impossible with test data) - selectMultiplePaths: verify name, bestFriend unwrap, friends array - nestedObjectPropertySingle: match nestedObjectProperty assertions - subSelectAllProperties: verify friend count and property values - subSelectAllPropertiesSingle: verify bestFriend unwrap with all properties - nestedQueries2: verify friends array, firstPet ref, bestFriend unwrap - subSelectArray: verify friend count, names, and hobby values - selectShapeSetAs/selectShapeAs: verify guardDogLevel values - countNestedFriends/countLabel: verify actual count values - preloadBestFriend: verify bestFriend unwrap with preloaded name Two tests will fail due to flat multi-value projection bug: - selectFriends (already flagged in prior commit) - selectMultiplePaths (friends flat field takes first binding only) https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Properties without maxCount constraints (e.g. `friends`) were returning a single entity reference instead of an array when selected via flat projections like `Person.select(p => p.friends)`. Root cause: `mapFlatRows` deduplicated by root ID and took only the first binding. `mapNestedRows` used `groupBindings[0]` for root-level flat fields. Fix: - Add `maxCount?: number` to `IRPropertyExpression` (propagated from `DesugaredPropertyStep.maxCount` in `IRProjection.ts`) - Add `maxCount?: number` to `FieldDescriptor` in result mapping - Refactor `mapFlatRows` to group bindings by root ID, then: - Single-value fields (maxCount <= 1): take first binding (unchanged) - Multi-value fields (no maxCount): collect all distinct values into arrays - Update `mapNestedRows` to use same logic for root-level flat fields - Add 5 new unit tests covering collection, dedup, absent, mixed, and multi-value-in-nested-mode scenarios https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
- Consolidate duplicated extraction logic: populateFields now delegates
to extractFieldValue instead of inlining the same URI/literal handling
- Simplify extractFieldValue: merge redundant URI branches (both returned
the same {id: val.value})
- Add clarifying comments on FieldDescriptor.maxCount, extractFieldValue
URI handling, and multi-value collection semantics
- Write report 013 (flat multi-value projection fix)
- Update report 012 with follow-on reference
- Remove plan/ideation docs (013)
- Add changeset for flat multi-value fix (patch)
https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Multi-value literal properties (e.g. `nickNames: string[]`) were silently dropped because `populateFlatFields` filtered collected values with `typeof extracted === 'object' && 'id' in extracted`, which rejects string/number/boolean/Date values. Fix: - Remove the object/id guard — collect all non-null values - Add `string[] | number[] | boolean[] | Date[]` to `ResultFieldValue` type to support literal arrays - Add 4 new unit tests: literal string collection, mixed URI+literal, absent (empty array), and deduplication https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
…ngeset - Merge reports 012 + 013 into single report 012 covering all maxCount result mapping fixes (single-value, multi-value URI, multi-value literal) - Remove known limitation about literal fields (now fixed) - Remove plan/ideation docs for 014 (flat multi-value literal fix) - Merge two changesets into one unified changeset https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
…fjieZ Fix maxCount-aware result mapping for single-value and multi-value properties
1. nestedObjectProperty/nestedObjectPropertySingle: bestFriend is OPTIONAL within the friends traversal (not INNER JOIN), so both p1 and p2 qualify. Fixed assertions to expect 2 rows with null bestFriend for friends that lack one. 2. Aggregate expressions (countFriends, countNestedFriends, countLabel, customResultNumFriends): non-property expressions like aggregate_expr had no maxCount, so populateFlatFields treated them as multi-value and wrapped scalar results in arrays. Fixed by defaulting all non-property_expr expressions to maxCount: 1 in buildNestingDescriptor. 3. countNestedFriends/countLabel: relaxed assertions that assumed specific count values without Fuseki verification — now check structural correctness (numeric type, row count >= 2). https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Root cause: GROUP BY queries with traversals (e.g. friends.friends.size()) produce both a nested group (empty friends array from aggregated-away ?a1) AND the aggregate count as flat fields. The old Object.keys().find(k !== 'id') hit the empty friends array first, returning [] instead of the count. Fix: add findCountValue() helper that searches for the first numeric-valued key, skipping non-numeric fields like the empty nested group array. Restore strict assertions: p1 count=2, p2 count=0. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Fix count aggregation tests and aggregate wrapping bug
Root cause: irToAlgebra renames aggregate aliases that collide with traversal aliases (e.g. a1 → a1_agg) and updates resultMap, but not the projection. buildNestingDescriptor then fails to find the projection entry for the renamed alias, silently dropping the aggregate field. Fix: when a resultMap entry has no matching projection (renamed by irToAlgebra), create a flat field descriptor using the resultMap alias directly as the SPARQL variable name, with maxCount: 1. Adds unit test simulating the exact alias rename scenario. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Fix aggregate count mapping when alias collides with traversal
- Add ci.yml: runs build+test on PRs targeting main/dev (previously tests only ran after merge, allowing broken code through) - Bump changeset from patch to minor: result shape changes are breaking for existing consumers https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Tests now run on PRs via ci.yml. The post-merge publish workflow only needs to build before publishing — testing is redundant since the code was already tested before merge. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Add PR CI workflow and bump changeset to minor
npm install -g npm@^11.5.1 fails on Node 22.22.2 with MODULE_NOT_FOUND for promise-retry. Node 22 ships with npm 10.x which works fine for changeset publish. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Fix publish workflow: remove broken npm global upgrade
Node 22.22.2 (latest on GitHub runners) breaks npm install -g npm@^11.5.1 with MODULE_NOT_FOUND. Fix: pin Node to 22.16.0 LTS across all workflows. Restore npm 11 upgrade in release/dev-release jobs (needed for publish). https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Pin Node to 22.16.0 LTS and restore npm 11 for publish
Both release and dev-release jobs already do their own checkout/install/build. The standalone build job was just wasting CI minutes as a redundant gate. https://claude.ai/code/session_017mqanCkMvA1VU8MVD7hkA1
Remove redundant standalone build job from publish workflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.