11import type { DocHandle , Patch } from '@automerge/automerge-repo' ;
22import * as Schema from 'effect/Schema' ;
33import { isRelationField } from '../utils/isRelationField.js' ;
4+ import { canonicalize } from '../utils/jsc.js' ;
45import { type DecodedEntitiesCacheEntry , type QueryEntry , decodedEntitiesCache } from './decodedEntitiesCache.js' ;
56import { entityRelationParentsMap } from './entityRelationParentsMap.js' ;
67import { getEntityRelations } from './getEntityRelations.js' ;
@@ -122,15 +123,9 @@ const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) => {
122123 }
123124 }
124125
125- const query = cacheEntry . queries . get ( 'all' ) ;
126- if ( query && decoded ) {
127- const index = query . data . findIndex ( ( entity ) => entity . id === entityId ) ;
128- if ( index !== - 1 ) {
129- query . data [ index ] = decoded ;
130- } else {
131- query . data . push ( decoded ) ;
132- }
133- touchedQueries . add ( [ typeName , 'all' ] ) ;
126+ for ( const [ queryKey , query ] of cacheEntry . queries ) {
127+ touchedQueries . add ( [ typeName , queryKey ] ) ;
128+ query . isInvalidated = true ;
134129 }
135130
136131 entityTypes . add ( typeName ) ;
@@ -155,12 +150,12 @@ const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) => {
155150 entityTypes . add ( affectedTypeName ) ;
156151 cacheEntry . entities . delete ( entityId ) ;
157152
158- for ( const [ , query ] of cacheEntry . queries ) {
153+ for ( const [ queryKey , query ] of cacheEntry . queries ) {
159154 // find the entity in the query and remove it using splice
160155 const index = query . data . findIndex ( ( entity ) => entity . id === entityId ) ;
161156 if ( index !== - 1 ) {
162157 query . data . splice ( index , 1 ) ;
163- touchedQueries . add ( [ affectedTypeName , 'all' ] ) ;
158+ touchedQueries . add ( [ affectedTypeName , queryKey ] ) ;
164159 }
165160 }
166161 }
@@ -228,6 +223,7 @@ const subscribeToDocumentChanges = (handle: DocHandle<DocumentContent>) => {
228223export function findMany < const S extends AnyNoContext > (
229224 handle : DocHandle < DocumentContent > ,
230225 type : S ,
226+ filter ?: Schema . Simplify < Partial < Schema . Schema . Type < S > > > | undefined ,
231227) : { entities : Readonly < Array < Entity < S > > > ; corruptEntityIds : Readonly < Array < string > > } {
232228 const decode = Schema . decodeUnknownSync ( type ) ;
233229 // TODO: what's the right way to get the name of the type?
@@ -246,7 +242,16 @@ export function findMany<const S extends AnyNoContext>(
246242 if ( hasValidTypesProperty ( entity ) && entity [ '@@types@@' ] . includes ( typeName ) ) {
247243 const relations = getEntityRelations ( id , type , doc ) ;
248244 try {
249- filtered . push ( { ...decode ( { ...entity , ...relations , id } ) , type : typeName } ) ;
245+ const decoded = { ...decode ( { ...entity , ...relations , id } ) , type : typeName } ;
246+ if ( filter ) {
247+ for ( const filterEntry in filter ) {
248+ if ( decoded [ filterEntry ] === filter [ filterEntry ] ) {
249+ filtered . push ( decoded ) ;
250+ }
251+ }
252+ } else {
253+ filtered . push ( decoded ) ;
254+ }
250255 } catch ( error ) {
251256 corruptEntityIds . push ( id ) ;
252257 }
@@ -261,11 +266,12 @@ const stableEmptyArray: Array<unknown> = [];
261266export function subscribeToFindMany < const S extends AnyNoContext > (
262267 handle : DocHandle < DocumentContent > ,
263268 type : S ,
269+ filter ?: Schema . Simplify < Partial < Schema . Schema . Type < S > > > | undefined ,
264270) : {
265271 subscribe : ( callback : ( ) => void ) => ( ) => void ;
266272 getEntities : ( ) => Readonly < Array < Entity < S > > > ;
267273} {
268- const queryKey = 'all' ;
274+ const queryKey = filter ? canonicalize ( filter ) : 'all' ;
269275 const decode = Schema . decodeUnknownSync ( type ) ;
270276 // TODO: what's the right way to get the name of the type?
271277 // @ts -expect-error name is defined
@@ -282,24 +288,17 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
282288 return query . data ;
283289 }
284290
285- const { entities } = findMany ( handle , type ) ;
291+ const { entities } = findMany ( handle , type , filter ) ;
292+
286293 for ( const entity of entities ) {
287294 cacheEntry ?. entities . set ( entity . id , entity ) ;
288-
289- if ( ! query ) continue ;
290-
291- const index = query . data . findIndex ( ( e ) => e . id === entity . id ) ;
292- if ( index !== - 1 ) {
293- query . data [ index ] = entity ;
294- } else {
295- query . data . push ( entity ) ;
296- }
297295 }
298296
297+ // must be a new reference to ensure it can be used in React.useMemo
298+ query . data = [ ...entities ] ;
299299 cacheEntry . isInvalidated = false ;
300300 query . isInvalidated = false ;
301- // must be a new reference to ensure it can be used in React.useMemo
302- query . data = [ ...query . data ] ;
301+
303302 return query . data ;
304303 } ;
305304
@@ -314,49 +313,36 @@ export function subscribeToFindMany<const S extends AnyNoContext>(
314313 let cacheEntry = decodedEntitiesCache . get ( typeName ) ;
315314
316315 if ( ! cacheEntry ) {
317- const { entities } = findMany ( handle , type ) ;
318316 const entitiesMap = new Map ( ) ;
319- for ( const entity of entities ) {
320- entitiesMap . set ( entity . id , entity ) ;
321- }
322-
323317 const queries = new Map < string , QueryEntry > ( ) ;
324318
325319 queries . set ( queryKey , {
326- data : [ ... entities ] ,
320+ data : [ ] ,
327321 listeners : [ ] ,
328- isInvalidated : false ,
322+ isInvalidated : true ,
329323 } ) ;
330324
331325 cacheEntry = {
332326 decoder : decode ,
333327 type,
334328 entities : entitiesMap ,
335329 queries,
336- isInvalidated : false ,
330+ isInvalidated : true ,
337331 } ;
338332
339333 decodedEntitiesCache . set ( typeName , cacheEntry ) ;
340-
341- for ( const entity of entities ) {
342- for ( const [ , value ] of Object . entries ( entity ) ) {
343- if ( Array . isArray ( value ) ) {
344- for ( const relationEntity of value ) {
345- let relationParentEntry = entityRelationParentsMap . get ( relationEntity . id ) ;
346- if ( relationParentEntry ) {
347- relationParentEntry . set ( cacheEntry , ( relationParentEntry . get ( cacheEntry ) ?? 0 ) + 1 ) ;
348- } else {
349- relationParentEntry = new Map ( ) ;
350- entityRelationParentsMap . set ( relationEntity . id , relationParentEntry ) ;
351- relationParentEntry . set ( cacheEntry , 1 ) ;
352- }
353- }
354- }
355- }
356- }
357334 }
358335
359- const query = cacheEntry . queries . get ( queryKey ) ;
336+ let query = cacheEntry . queries . get ( queryKey ) ;
337+ if ( ! query ) {
338+ query = {
339+ data : [ ] ,
340+ listeners : [ ] ,
341+ isInvalidated : true ,
342+ } ;
343+ // we just set up the query and expect it to correctly set itself up in findMany
344+ cacheEntry . queries . set ( queryKey , query ) ;
345+ }
360346
361347 if ( query ?. listeners ) {
362348 query . listeners . push ( callback ) ;
0 commit comments