private <R> void getSortedChildren(final LmdbReadContext readContext,
                                       final Key parentKey,
                                       final int depth,
                                       final long limit,
                                       final boolean trimTop,
                                       final OpenGroups openGroups,
                                       final TimeFilter timeFilter,
                                       final ItemMapper<R> mapper,
                                       final OffsetRange range,
                                       final FetchState fetchState,
                                       final Consumer<R> resultConsumer) {
        // Remember that we have gone into this group, so we don't keep trying.
        openGroups.complete(parentKey);

        final CompiledSorter<Item> sorter = compiledSorters.get(depth);

        lmdbRowKeyFactory.createChildKeyRange(parentKey, timeFilter, keyRange -> {
            final long lengthRemaining = range.getOffset() + range.getLength() - fetchState.length;
            final int trimmedSize = (int) Math.max(Math.min(Math.min(limit, lengthRemaining), maxSortedItems), 0);

            int maxSize = trimmedSize * 2;
            maxSize = Math.max(maxSize, 1_000);

            final SortedItems sortedItems = new SortedItems(10, maxSize, trimmedSize, trimTop, sorter);
            readContext.read(keyRange, iterator -> {
                long totalRowCount = 0;
                while (iterator.hasNext()
                       && !Thread.currentThread().isInterrupted()) {
                    final KeyVal<ByteBuffer> keyVal = iterator.next();

                    // All valid keys are more than a single byte long. Single byte keys are used to store db
                    // info.
                    if (LmdbRowKeyFactoryFactory.isNotStateKey(keyVal.key())) {
                        final ByteBuffer keyBuffer = keyVal.key();
                        final ByteBuffer valueBuffer = keyVal.val();
                        boolean isFirstValue = true;
                        // It is possible to have no actual values, e.g. if you have just one col of
                        // 'currentUser()' so we still need to create and add an empty storedValues
                        while (valueBuffer.hasRemaining() || isFirstValue) {
                            isFirstValue = false;

                            final StoredValues storedValues = readValues(valueBuffer);
                            final Key key = lmdbRowKeyFactory.createKey(parentKey, storedValues, keyBuffer);
                            final ItemImpl item = new ItemImpl(readContext, key, storedValues);
                            if (mapper.hidesRows()) {
                                final R row = mapper.create(item);
                                if (row != null) {
                                    totalRowCount++;
                                    sortedItems.add(new ItemImpl(readContext, key, storedValues));
                                }
                            } else {
                                totalRowCount++;
                                sortedItems.add(new ItemImpl(readContext, key, storedValues));
                            }
                        }
                    }
                }

                // If there is a limit then pretend that the total row count is constrained.
                fetchState.totalRowCount += Math.min(totalRowCount, limit);
            });

            // Finally transfer the sorted items to the result.
            long childCount = 0;
            for (final Item item : sortedItems.getIterable()) {
                if (childCount >= limit) {
                    break;
                }
                childCount++;

                if (!fetchState.reachedRowLimit) {
                    if (range.getOffset() <= fetchState.offset) {
                        final R row = mapper.create(item);
                        resultConsumer.accept(row);
                        fetchState.length++;
                        fetchState.reachedRowLimit = fetchState.length >= range.getLength();
                        if (fetchState.reachedRowLimit) {
                            if (fetchState.countRows) {
                                fetchState.justCount = true;
                            } else {
                                fetchState.keepGoing = false;
                            }
                        }
                    }
                    fetchState.offset++;
                }

                // Add children if the group is open.
                final int childDepth = depth + 1;
                if (fetchState.keepGoing &&
                    compiledSorters.size() > childDepth &&
                    openGroups.isOpen(item.getKey())) {
                    getChildren(readContext,
                            item.getKey(),
                            childDepth,
                            maxResultSizes.size(childDepth),
                            trimTop,
                            openGroups,
                            timeFilter,
                            mapper,
                            range,
                            fetchState,
                            resultConsumer);
                }
            }
        });
    }
Write data from the data store to an output removing them from the datastore as we go as they will be transferred
to another store.

@param output The output to write to.
stroom-query/stroom-query-common/src/main/java/stroom/query/common/v2/LmdbDataStore.java