@@ -127,6 +127,122 @@ public List<DataBlock<T>> readBlocks(PositionValueAccess pva, List<long[]> posit
127127 return blocks ;
128128 }
129129
130+ /**
131+ * For a Non-Sharded dataset, reads all existing data blocks from the given positions.
132+ * The method checks if a block exists at a given position before attempting to read it.
133+ *
134+ * @param pva a {@code PositionValueAccess} instance to access data based on positions
135+ * @param positions a list of long arrays representing the grid positions of the blocks to be read
136+ * @return a list of {@code DataBlock<T>} objects for all valid and existing blocks at the specified positions
137+ */
138+ private List <DataBlock <T >> readBlocksExistsNonSharded (PositionValueAccess pva , List <long []> positions ) {
139+ final ArrayList <DataBlock <T >> blocks = new ArrayList <>();
140+ for (long [] pos : positions ) {
141+ final NestedPosition position = grid .nestedPosition (pos );
142+ if (pva .exists (position .key ())) {
143+ final DataBlock <T > block = readBlock (pva , pos );
144+ if (block != null )
145+ blocks .add (block );
146+ }
147+ }
148+ return blocks ;
149+ }
150+
151+ /**
152+ * For a Sharded Dataset, reads all existing data blocks from the given positions.
153+ * The method groups blocks by shard, checks shard existence, reads shard data,
154+ * and determines which blocks within the shard exist before returning them.
155+ * If the KVA supports partial reads, this should take advantage of reading
156+ * the index only to check existence and only read blocks that exist.
157+ *
158+ * @param pva a {@code PositionValueAccess} instance to access data based on shard keys
159+ * @param positions a list of long arrays representing the grid positions of the blocks to be read
160+ * @return a list of {@code DataBlock<T>} objects for all valid and existing blocks at the specified positions
161+ */
162+ private List <DataBlock <T >> readBlocksExistsSharded (PositionValueAccess pva , List <long []> positions ) {
163+ /* group blocks by shard, read shard index to determine which blocks exist */
164+ final List <NestedPosition > blockPositions = positions .stream ().map (grid ::nestedPosition ).collect (Collectors .toList ());
165+ final int outermostLevel = grid .numLevels () - 1 ;
166+ final Collection <List <NestedPosition >> blocksPerOutermostShard = groupInnerPositions (grid , blockPositions , outermostLevel );
167+
168+ final ArrayList <DataBlock <T >> blocks = new ArrayList <>();
169+ for (List <NestedPosition > blocksInSingleShard : blocksPerOutermostShard ) {
170+ if (blocksInSingleShard .isEmpty ())
171+ continue ;
172+
173+ final NestedPosition firstBlock = blocksInSingleShard .get (0 );
174+ final long [] shardKey = firstBlock .key ();
175+
176+ /* check if the shard key value exists */
177+ boolean shardExists = pva .exists (shardKey );
178+ if (!shardExists )
179+ continue ;
180+
181+
182+ final ReadData shardData = pva .get (shardKey );
183+ final List <DataBlock <T >> shardBlocks = readBlocksExistsShardedRecursive (shardData , blocksInSingleShard , outermostLevel );
184+ blocks .addAll (shardBlocks );
185+ }
186+ return blocks ;
187+ }
188+
189+ /**
190+ * Read only the blocks that exist within a shard by checking the shard index first.
191+ */
192+ private List <DataBlock <T >> readBlocksExistsShardedRecursive (
193+ final ReadData readData ,
194+ final List <NestedPosition > positions ,
195+ final int level ) {
196+
197+ if (readData == null || level == 0 || positions .isEmpty ())
198+ return Collections .emptyList ();
199+
200+ final NestedPosition firstBlock = positions .get (0 );
201+ final long [] shardPosition = firstBlock .absolute (level );
202+
203+ final BlockCodec <RawShard > codec = (BlockCodec <RawShard >) codecs [level ];
204+ final RawShard shard = codec .decode (readData , shardPosition ).getData ();
205+
206+ final ArrayList <DataBlock <T >> blocks = new ArrayList <>();
207+ if (level == 1 ) {
208+ /* base case: check the index and read blocks that exist */
209+ for (NestedPosition blockPosition : positions ) {
210+ final long [] elementPos = blockPosition .relative (0 );
211+ if (shard .index ().get (elementPos ) != null ) {
212+ final ReadData elementData = shard .getElementData (elementPos );
213+ final DataBlock <T > block = readBlockRecursive (elementData , blockPosition , 0 );
214+ if (block != null )
215+ blocks .add (block );
216+ }
217+ }
218+ } else {
219+ /* nested shards: group by next level and recurse */
220+ final Collection <List <NestedPosition >> nextLevelShards = groupInnerPositions (grid , positions , level - 1 );
221+ for (List <NestedPosition > innerPositions : nextLevelShards ) {
222+ if (innerPositions .isEmpty ())
223+ continue ;
224+ final NestedPosition firstInner = innerPositions .get (0 );
225+ final long [] innerShardPos = firstInner .relative (level - 1 );
226+ if (shard .index ().get (innerShardPos ) != null ) {
227+ final ReadData innerShardData = shard .getElementData (innerShardPos );
228+ blocks .addAll (readBlocksExistsShardedRecursive (innerShardData , innerPositions , level - 1 ));
229+ }
230+ }
231+ }
232+
233+ return blocks ;
234+ }
235+
236+ @ Override
237+ public List <DataBlock <T >> readBlocksExists (PositionValueAccess pva , List <long []> positions ) throws N5IOException {
238+
239+ boolean unsharded = grid .numLevels () == 1 ;
240+ if (unsharded )
241+ return readBlocksExistsNonSharded (pva , positions );
242+ else
243+ return readBlocksExistsSharded (pva , positions );
244+ }
245+
130246 /**
131247 * Bulk Read operation on a shard. `positions` MUST all be in the same shard.
132248 * That is, for each `position` in `positions`, `position.absolute(level)` must be the same.
0 commit comments