@@ -6,20 +6,34 @@ import { AbstractQueryProcessor, AbstractQueryProcessorOptions, LinkQueryOptions
66 * It contains both the current and previous state of the row.
77 */
88export interface WatchedQueryRowDifferential < RowType > {
9- current : RowType ;
10- previous : RowType ;
9+ readonly current : RowType ;
10+ readonly previous : RowType ;
1111}
1212
1313/**
1414 * Represents the result of a watched query that has been differentiated.
1515 * {@link WatchedQueryState.data} is of the {@link WatchedQueryDifferential} form when using the {@link IncrementalWatchMode.DIFFERENTIAL} mode.
1616 */
1717export interface WatchedQueryDifferential < RowType > {
18- added : RowType [ ] ;
19- all : RowType [ ] ;
20- removed : RowType [ ] ;
21- updated : WatchedQueryRowDifferential < RowType > [ ] ;
22- unchanged : RowType [ ] ;
18+ added : ReadonlyArray < RowType > ;
19+ /**
20+ * The entire current result set.
21+ * Array item object references are preserved between updates if the item is unchanged.
22+ *
23+ * e.g. In the query
24+ * ```sql
25+ * SELECT name, make FROM assets ORDER BY make ASC;
26+ * ```
27+ *
28+ * If a previous result set contains an item (A) `{name: 'pc', make: 'Cool PC'}` and
29+ * an update has been made which adds another item (B) to the result set (the item A is unchanged) - then
30+ * the updated result set will be contain the same object reference,to item A, as the previous resultset.
31+ * This is regardless of the item A's position in the updated result set.
32+ */
33+ all : ReadonlyArray < RowType > ;
34+ removed : ReadonlyArray < RowType > ;
35+ updated : ReadonlyArray < WatchedQueryRowDifferential < RowType > > ;
36+ unchanged : ReadonlyArray < RowType > ;
2337}
2438
2539/**
@@ -98,36 +112,44 @@ export class DifferentialQueryProcessor<RowType>
98112
99113 let hasChanged = false ;
100114 const currentMap = new Map < string , { hash : string ; item : RowType } > ( ) ;
101- current . forEach ( ( item ) => {
102- currentMap . set ( identify ( item ) , {
103- hash : compareBy ( item ) ,
104- item
105- } ) ;
106- } ) ;
107-
108115 const removedTracker = new Set ( previousMap . keys ( ) ) ;
109116
110- const diff : WatchedQueryDifferential < RowType > = {
111- all : current ,
112- added : [ ] ,
113- removed : [ ] ,
114- updated : [ ] ,
115- unchanged : [ ]
117+ // Allow mutating to populate the data temporarily.
118+ const diff = {
119+ all : [ ] as RowType [ ] ,
120+ added : [ ] as RowType [ ] ,
121+ removed : [ ] as RowType [ ] ,
122+ updated : [ ] as WatchedQueryRowDifferential < RowType > [ ] ,
123+ unchanged : [ ] as RowType [ ]
116124 } ;
117125
118- for ( const [ key , { hash, item } ] of currentMap ) {
126+ /**
127+ * Looping over the current result set array is important to preserve
128+ * the ordering of the result set.
129+ * We can replace items in the current array with previous object references if they are equal.
130+ */
131+ for ( const item of current ) {
132+ const key = identify ( item ) ;
133+ const hash = compareBy ( item ) ;
134+ currentMap . set ( key , { hash, item } ) ;
135+
119136 const previousItem = previousMap . get ( key ) ;
120137 if ( ! previousItem ) {
121138 // New item
122139 hasChanged = true ;
123140 diff . added . push ( item ) ;
141+ diff . all . push ( item ) ;
124142 } else {
125143 // Existing item
126144 if ( hash == previousItem . hash ) {
127145 diff . unchanged . push ( item ) ;
146+ // Use the previous object reference
147+ diff . all . push ( previousItem . item ) ;
128148 } else {
129149 hasChanged = true ;
130150 diff . updated . push ( { current : item , previous : previousItem . item } ) ;
151+ // Use the new reference
152+ diff . all . push ( item ) ;
131153 }
132154 }
133155 // The item is present, we don't consider it removed
@@ -175,7 +197,9 @@ export class DifferentialQueryProcessor<RowType>
175197 await this . updateState ( { isFetching : true } ) ;
176198 }
177199
178- const partialStateUpdate : Partial < WatchedQueryState < WatchedQueryDifferential < RowType > > > = { } ;
200+ const partialStateUpdate : Partial < WatchedQueryState < WatchedQueryDifferential < RowType > > > & {
201+ data ?: WatchedQueryDifferential < RowType > ;
202+ } = { } ;
179203
180204 // Always run the query if an underlying table has changed
181205 const result = await watchOptions . query . execute ( {
0 commit comments