def seek(self, key_prefix: bytes, direction="forward" ) -> Iterator[Tuple[storage.StorageKey, storage.StorageItem]]: """ Find all data starting with a prefix. Args: key_prefix: the prefix the data must have direction: determines the database search order. Can be "forward" or "reverse" Returns: An iterator with the results See Also: * :func:`~neo3.storage.cache.CachedStorageAccess.find_range` """ # always read only comperator = storage.NEOByteCompare(direction) cached: List[Tuple[storage.StorageKey, storage.StorageItem]] = [] cached_keys: List[storage.StorageKey] = [] for key, value in self._dictionary.items(): if value.state != TrackState.DELETED and ( len(key_prefix) == 0 or comperator.compare(key.to_array(), key_prefix) >= 0): cached.append((key, value.item)) cached_keys.append(key) if direction == "forward": cached_sorted = sorted(cached, key=lambda pair: pair[0].to_array()) else: cached_sorted = sorted(cached, key=lambda pair: pair[0].to_array(), reverse=True) e1 = _Enumerator(iter(cached_sorted)) e2 = _Enumerator(self._internal_seek(key_prefix, direction)) c1 = e1.move_next() c2 = e2.move_next() i1 = e1.current() i2 = e2.current() comperator = storage.NEOByteCompare(direction) while c1 or c2: # filter out duplicates from internal if already found in cached if c2 and i2[0] in cached_keys: c2 = e2.move_next() i2 = e2.current() elif not c2 or (c1 and comperator.compare(i1[0].to_array(), i2[0].to_array()) < 0): # i1 values come from the cache by reference, make them "read-only" yield deepcopy(i1[0]), deepcopy(i1[1]) c1 = e1.move_next() i1 = e1.current() else: # i2 values are new objects deserialized from storage (in the case of leveldb) yield i2[0], i2[1] c2 = e2.move_next() i2 = e2.current()
def seek( self, key_prefix: bytes, direction="forward" ) -> Iterator[Tuple[storage.StorageKey, storage.StorageItem]]: # always read only comperator = storage.NEOByteCompare(direction) cached: List[Tuple[storage.StorageKey, storage.StorageItem]] = [] cached_keys: List[storage.StorageKey] = [] for key, value in self._dictionary.items(): if value.state != TrackState.DELETED and ( len(key_prefix) == 0 or comperator.compare(key.to_array(), key_prefix) >= 0): cached.append((key, value.item)) cached_keys.append(key) if direction == "forward": cached_sorted = sorted(cached, key=lambda pair: pair[0].to_array()) else: cached_sorted = sorted(cached, key=lambda pair: pair[0].to_array(), reverse=True) e1 = _Enumerator(iter(cached_sorted)) e2 = _Enumerator(self._internal_seek(key_prefix, direction)) c1 = e1.move_next() c2 = e2.move_next() i1 = e1.current() i2 = e2.current() comperator = storage.NEOByteCompare(direction) while c1 or c2: # filter out duplicates from internal if already found in cached if c2 and i2[0] in cached_keys: c2 = e2.move_next() i2 = e2.current() elif not c2 or (c1 and comperator.compare(i1[0].to_array(), i2[0].to_array()) < 0): # i1 values come from the cache by reference, make them "read-only" yield deepcopy(i1[0]), deepcopy(i1[1]) c1 = e1.move_next() i1 = e1.current() else: # i2 values are new objects deserialized from storage (in the case of leveldb) yield i2[0], i2[1] c2 = e2.move_next() i2 = e2.current()
def find_range(self, start: bytes, end: bytes, direction: str = "forward" ) -> Iterator[Tuple[storage.StorageKey, storage.StorageItem]]: """ Find all data within a given range. Args: start: the data prefix to start from end: the post fix to end the search results at direction: determines the database search order. Can be "forward" or "reverse" Returns: An iterator with the results See Also: * :func:`~neo3.storage.cache.CachedStorageAccess.seek` """ comperator = storage.NEOByteCompare(direction) for key, value in self.seek(start, direction): if comperator.compare(key.to_array(), end) < 0: yield key, value else: return None
def test_forward(self): comperator = storage.NEOByteCompare("forward") self.assertEqual(-1, comperator.compare(b'\x00', b'\x01')) self.assertEqual(0, comperator.compare(b'\x01', b'\x01')) self.assertEqual(1, comperator.compare(b'\x02', b'\x01')) self.assertEqual(-1, comperator.compare(b'\x01', b'\x02')) self.assertEqual(1, comperator.compare(b'\x01\x01', b'\x01')) self.assertEqual(1, comperator.compare(b'\xff', b'\x01')) self.assertEqual(-1, comperator.compare(b'\xf9', b'\xfb')) self.assertEqual(1, comperator.compare(b'\xf9\xff\xff\xff\x09', b'\x01\x00\x00\x00\x01')) self.assertEqual(1, comperator.compare(b'\x01\x00\x00\x00\x01\x02', b'\x01\x00\x00\x00\x01'))
def find_range( self, start: bytes, end: bytes, direction: str = "forward" ) -> Iterator[Tuple[storage.StorageKey, storage.StorageItem]]: comperator = storage.NEOByteCompare(direction) for key, value in self.seek(start, direction): if comperator.compare(key.to_array(), end) < 0: yield key, value else: return None