@@ -1536,55 +1536,69 @@ public function decode(Document $collection, Document $document, array $selectio
15361536 {
15371537 /** @var array<array<string, mixed>|Document> $allAttributes */
15381538 $ allAttributes = $ collection ->getAttribute ('attributes ' , []);
1539- $ attributes = \array_filter (
1540- $ allAttributes ,
1541- fn (array |Document $ attribute ) => $ attribute ['type ' ] !== ColumnType::Relationship->value
1542- );
15431539
1544- $ relationships = \array_filter (
1545- $ allAttributes ,
1546- fn (array |Document $ attribute ) => $ attribute ['type ' ] === ColumnType::Relationship->value
1547- );
1540+ // Single-pass partition into relationships vs regular attributes.
1541+ // Replaces two array_filter passes that each walked the full list.
1542+ $ attributes = [];
1543+ $ relationships = [];
1544+ $ relationshipType = ColumnType::Relationship->value ;
1545+ foreach ($ allAttributes as $ attribute ) {
1546+ if (($ attribute ['type ' ] ?? '' ) === $ relationshipType ) {
1547+ $ relationships [] = $ attribute ;
1548+ } else {
1549+ $ attributes [] = $ attribute ;
1550+ }
1551+ }
15481552
15491553 $ filteredValue = [];
15501554
1551- foreach ($ relationships as $ relationship ) {
1552- /** @var string $key */
1553- $ key = $ relationship ['$id ' ] ?? '' ;
1554-
1555- if (
1556- \array_key_exists ($ key , (array ) $ document )
1557- || \array_key_exists ($ this ->adapter ->filter ($ key ), (array ) $ document )
1558- ) {
1559- $ value = $ document ->getAttribute ($ key );
1560- $ value ??= $ document ->getAttribute ($ this ->adapter ->filter ($ key ));
1561- $ document ->removeAttribute ($ this ->adapter ->filter ($ key ));
1562- $ document ->setAttribute ($ key , $ value );
1555+ if (! empty ($ relationships )) {
1556+ $ documentArray = (array ) $ document ;
1557+ foreach ($ relationships as $ relationship ) {
1558+ /** @var string $key */
1559+ $ key = $ relationship ['$id ' ] ?? '' ;
1560+ $ filteredKey = $ this ->adapter ->filter ($ key );
1561+
1562+ if (
1563+ \array_key_exists ($ key , $ documentArray )
1564+ || \array_key_exists ($ filteredKey , $ documentArray )
1565+ ) {
1566+ $ value = $ document ->getAttribute ($ key );
1567+ $ value ??= $ document ->getAttribute ($ filteredKey );
1568+ $ document ->removeAttribute ($ filteredKey );
1569+ $ document ->setAttribute ($ key , $ value );
1570+ }
15631571 }
15641572 }
15651573
15661574 foreach ($ this ->getInternalAttributes () as $ attribute ) {
15671575 $ attributes [] = $ attribute ;
15681576 }
15691577
1578+ $ hasSelections = ! empty ($ selections );
1579+ $ selectAll = $ hasSelections && \in_array ('* ' , $ selections , true );
1580+ $ selectionsMap = ($ hasSelections && ! $ selectAll )
1581+ ? \array_fill_keys ($ selections , true )
1582+ : null ;
1583+
15701584 foreach ($ attributes as $ attribute ) {
15711585 /** @var string $key */
15721586 $ key = $ attribute ['$id ' ] ?? '' ;
1573- $ type = $ attribute ['type ' ] ?? '' ;
1587+ if ($ key === '$permissions ' ) {
1588+ continue ;
1589+ }
1590+
15741591 $ array = $ attribute ['array ' ] ?? false ;
15751592 /** @var array<string> $filters */
15761593 $ filters = $ attribute ['filters ' ] ?? [];
15771594 $ value = $ document ->getAttribute ($ key );
15781595
1579- if ($ key === '$permissions ' ) {
1580- continue ;
1581- }
1582-
15831596 if (\is_null ($ value )) {
1584- $ value = $ document ->getAttribute ($ this ->adapter ->filter ($ key ));
1597+ $ filteredKey = $ this ->adapter ->filter ($ key );
1598+ $ value = $ document ->getAttribute ($ filteredKey );
15851599
15861600 if (! \is_null ($ value )) {
1587- $ document ->removeAttribute ($ this -> adapter -> filter ( $ key ) );
1601+ $ document ->removeAttribute ($ filteredKey );
15881602 }
15891603 }
15901604
@@ -1596,46 +1610,52 @@ public function decode(Document $collection, Document $document, array $selectio
15961610 $ value = ($ array ) ? $ value : [$ value ];
15971611 $ value = (is_null ($ value )) ? [] : $ value ;
15981612
1613+ // Reverse filter application without allocating a new array
1614+ // for each document. Walking the array backwards avoids
1615+ // array_reverse() per attribute per document.
1616+ $ filterCount = \count ($ filters );
1617+
15991618 /** @var array<int|string, mixed> $value */
16001619 foreach ($ value as $ index => $ node ) {
1601- foreach ( \array_reverse ( $ filters ) as $ filter ) {
1602- $ node = $ this ->decodeAttribute ($ filter , $ node , $ document , $ key );
1620+ for ( $ i = $ filterCount - 1 ; $ i >= 0 ; $ i -- ) {
1621+ $ node = $ this ->decodeAttribute ($ filters [ $ i ] , $ node , $ document , $ key );
16031622 }
16041623 $ value [$ index ] = $ node ;
16051624 }
16061625
1607- $ filteredValue [$ key ] = ($ array ) ? $ value : $ value [0 ];
1626+ $ resolved = $ array ? $ value : $ value [0 ];
1627+ $ filteredValue [$ key ] = $ resolved ;
16081628
16091629 if (
1610- empty ( $ selections )
1611- || \in_array ( $ key , $ selections )
1612- || \in_array ( ' * ' , $ selections )
1630+ ! $ hasSelections
1631+ || $ selectAll
1632+ || isset ( $ selectionsMap [ $ key ] )
16131633 ) {
1614- $ document ->setAttribute ($ key , ( $ array ) ? $ value : $ value [ 0 ] );
1634+ $ document ->setAttribute ($ key , $ resolved );
16151635 }
16161636 }
16171637
1618- $ hasRelationshipSelections = false ;
1619- if (! empty ( $ selections )) {
1638+ if ( $ hasSelections && ! $ selectAll ) {
1639+ $ hasRelationshipSelections = false ;
16201640 foreach ($ selections as $ selection ) {
16211641 if (\str_contains ($ selection , '. ' )) {
16221642 $ hasRelationshipSelections = true ;
16231643 break ;
16241644 }
16251645 }
1626- }
16271646
1628- if ( $ hasRelationshipSelections && ! empty ( $ selections ) && ! \in_array ( ' * ' , $ selections ) ) {
1629- foreach ($ allAttributes as $ attribute ) {
1630- /** @var string $key */
1631- $ key = $ attribute ['$id ' ] ?? '' ;
1647+ if ( $ hasRelationshipSelections ) {
1648+ foreach ($ allAttributes as $ attribute ) {
1649+ /** @var string $key */
1650+ $ key = $ attribute ['$id ' ] ?? '' ;
16321651
1633- if ($ attribute ['type ' ] === ColumnType::Relationship-> value || $ key === '$permissions ' ) {
1634- continue ;
1635- }
1652+ if (( $ attribute ['type ' ] ?? '' ) === $ relationshipType || $ key === '$permissions ' ) {
1653+ continue ;
1654+ }
16361655
1637- if (! in_array ($ key , $ selections ) && isset ($ filteredValue [$ key ])) {
1638- $ document ->setAttribute ($ key , $ filteredValue [$ key ]);
1656+ if (! isset ($ selectionsMap [$ key ]) && isset ($ filteredValue [$ key ])) {
1657+ $ document ->setAttribute ($ key , $ filteredValue [$ key ]);
1658+ }
16391659 }
16401660 }
16411661 }
0 commit comments