Source code for system.indexes.indexes
#
# This is ExplainDB, educational database systems materials.
#
# Copyright (C) 2026 Prof. Dr. Jens Dittrich, Saarland University
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
#
"""Simple index implementation backed by a Python dictionary."""
from typing import Iterator
from system.interfaces.indexing.Index import KeyValueStore, PutInfo
[docs]
class PythonDictionaryIndex[Key, Value](KeyValueStore[Key, Value]):
"""A simple key-value store implemented using a Python dictionary (yes, a Python dictionary is a key/value
store). Supports duplicates by mapping a key to a list of Values, i.e. multiple values can be associated with the
same key.
"""
def __init__(self):
"""Initializes an empty index backed by a Python dictionary that maps each key to a list of values,
so that multiple values can be associated with the same key.
"""
self.index: dict[Key, list[Value]] = dict[Key, list[Value]]()
[docs]
def size(self) -> int:
"""See :meth:`Index.size`."""
return len(self.index)
[docs]
def put(self, key: Key, value: Value) -> PutInfo | None:
"""See :meth:`Index.put`.
Appends the value to the list stored for the key, so duplicates are kept rather than overwritten.
"""
self.index.setdefault(key, []).append(value)
[docs]
def delete(self, key: Key, value: Value = None) -> None:
"""See :meth:`Index.delete`.
Removes the given value from the list stored for the key and drops the key entirely once its last
value has been removed. Raises ``KeyError`` if the key or the value is not present.
"""
if key not in self.index:
raise KeyError(f"Key {key} not found")
# get value list:
list_of_values = self.index[key]
# key was found but value not available in list?
if value not in list_of_values:
raise KeyError(f"Value {value} not found for key {key}")
# remove entry from index list:
list_of_values.remove(value)
# any other entry in list?
if len(list_of_values) == 0:
# then delete the key:
del self.index[key]
else:
# otherwise update the index (not really needed, but for completeness):
self.index[key] = list_of_values
[docs]
def flush(self, key: Key | None = None) -> None:
"""See :meth:`Index.flush`.
No-op: a Python dictionary lives in volatile memory and is not backed by persistent storage. Raises
``KeyError`` if the given key is not present.
"""
if key not in self.index:
raise KeyError(f"Key {key} not found")
# no action needed, as we are using a Python dictionary which is not backed by persistent storage
[docs]
def show(self) -> None:
"""See :meth:`Index.show`.
Prints the underlying dictionary.
"""
print(self.index)
[docs]
def get(self, key: Key) -> Iterator[Value]:
"""See :meth:`PointQueryMixIn.get`.
Raises ``KeyError`` if the key is not present.
"""
if key not in self.index:
raise KeyError(f"Key {key} not found")
yield from self.index[key]