Skip to content

Commit

Permalink
Feat/cache queries (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
JensZack authored Nov 22, 2024
1 parent 24c1a0b commit 0f52e9b
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 26 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "plexosdb"
version = "0.0.6"
version = "0.0.7"
readme = "README.md"
license = {file = "LICENSE.txt"}
keywords = []
Expand Down
3 changes: 2 additions & 1 deletion src/plexosdb/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ CREATE TABLE `t_category`

CREATE TABLE `t_object`
(
`object_id` INTEGER,
`object_id` INTEGER UNIQUE,
`class_id` INT NULL,
`name` VARCHAR(512) NULL COLLATE NOCASE,
`category_id` INT NULL,
Expand All @@ -241,6 +241,7 @@ CREATE TABLE `t_object`
`X` INT NULL,
`Y` INT NULL,
`Z` INT NULL,
UNIQUE (`class_id`, `name`)
CONSTRAINT PK_t_object
PRIMARY KEY (`object_id`)
);
Expand Down
27 changes: 22 additions & 5 deletions src/plexosdb/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def __init__(
super().__init__()
self._conn = sqlite3.connect(":memory:")
self._sqlite_config()
self._QUERY_CACHE: dict[tuple, int] = {}

if create_collations:
self._create_collations()
self._create_table_schema()
Expand Down Expand Up @@ -444,6 +446,11 @@ def add_object(
-----
By default, we add all objects to the system membership.
Raises
------
sqlite.IntegrityError
if an object is inserted without a unique name/class pair
Returns
-------
int
Expand Down Expand Up @@ -795,6 +802,11 @@ def _get_id(
"object_name": object_name,
}

# tuple that should be unique for any id returned
query_key = (table.name, object_name, class_name, parent_class_name, child_class_name)
if query_key in self._QUERY_CACHE:
return self._QUERY_CACHE[query_key]

query = f"SELECT {column_name} FROM `{table_name}`"
conditions = []
join_clauses = []
Expand Down Expand Up @@ -837,7 +849,6 @@ def _get_id(
if conditions
else f" WHERE {table_name}.name = :object_name"
)

result = self.query(query, params)

if not result:
Expand All @@ -847,7 +858,12 @@ def _get_id(
if len(result) > 1:
msg = f"Multiple ids returned for {object_name} and {class_name}. Try passing addtional filters"
raise ValueError(msg)
return result[0][0] # Get first element of tuple

ret: int = result[0][0] # Get first element of tuple

self._QUERY_CACHE[query_key] = ret

return ret

def get_membership_id(
self,
Expand Down Expand Up @@ -1054,8 +1070,6 @@ def query(self, query_string: str, params=None) -> list[tuple]:
String to get passed to the database connector.
params
Tuple or dict for passing
fetchone
Return firstrow
Note
----
Expand All @@ -1065,7 +1079,9 @@ def query(self, query_string: str, params=None) -> list[tuple]:
"""
with self._conn as conn:
res = conn.execute(query_string, params) if params else conn.execute(query_string)
return res.fetchall()
ret = res.fetchall()

return ret

def ingest_from_records(self, tag: str, record_data: Sequence):
"""Insert elements from xml to database."""
Expand All @@ -1088,6 +1104,7 @@ def ingest_from_records(self, tag: str, record_data: Sequence):
conn.execute(ingestion_sql, record)
except sqlite3.Error as err:
raise err

logger.trace("Finished ingesting {}", tag)
return

Expand Down
2 changes: 1 addition & 1 deletion tests/data/plexosdb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<t_object>
<object_id>3</object_id>
<class_id>2</class_id>
<name>SolarPV01</name>
<name>SolarPV02</name>
<category_id>97</category_id>
<GUID>40d15a07-8ccc-460e-919a-ec8e211899a8</GUID>
</t_object>
Expand Down
43 changes: 25 additions & 18 deletions tests/test_plexosdb_sqlite.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import pytest
import shutil
import xml.etree.ElementTree as ET # noqa: N817
from plexosdb.enums import ClassEnum, CollectionEnum, Schema
from plexosdb.sqlite import PlexosSQLite
from sqlite3 import IntegrityError
from collections.abc import Generator
from pathlib import Path

DB_FILENAME = "plexosdb.xml"

Expand All @@ -12,8 +16,13 @@ def db_empty() -> "PlexosSQLite":


@pytest.fixture
def db(data_folder) -> PlexosSQLite:
return PlexosSQLite(xml_fname=data_folder.joinpath(DB_FILENAME))
def db(data_folder: Path, tmp_path: Path) -> Generator[PlexosSQLite, None, None]:
xml_fname = data_folder / DB_FILENAME
xml_copy = tmp_path / f"copy_{DB_FILENAME}"
shutil.copy(xml_fname, xml_copy)
db = PlexosSQLite(xml_fname=str(xml_copy))
yield db
xml_copy.unlink()


def test_database_initialization(db):
Expand Down Expand Up @@ -80,10 +89,6 @@ def test_check_id_exists(db):
assert isinstance(system_check, bool)
assert not system_check

# Check that returns ValueError if multiple object founds
with pytest.raises(ValueError):
_ = db.check_id_exists(Schema.Objects, "SolarPV01", class_name=ClassEnum.Generator)


@pytest.mark.get_functions
def test_get_id(db):
Expand Down Expand Up @@ -156,27 +161,29 @@ def test_get_collection_id(db):

@pytest.mark.get_functions
def test_get_object_id(db):
gen_01_name = "gen1"
gen_id = db.add_object(
gen_01_name, ClassEnum.Generator, CollectionEnum.Generators, description="Test Gen"
)
assert gen_id

gen_id_get = db.get_object_id(gen_01_name, class_name=ClassEnum.Generator)
assert gen_id == gen_id_get

# Add generator with same name different category
gen_01_name = "gen1"
category_name = "PV Gens"

gen_id = db.add_object(
gen_01_name,
ClassEnum.Generator,
CollectionEnum.Generators,
description="Test Gen",
category_name=category_name,
)
with pytest.raises(ValueError):
_ = db.get_object_id(gen_01_name, class_name=ClassEnum.Generator)
assert gen_id

gen_id_get = db.get_object_id(gen_01_name, class_name=ClassEnum.Generator)
assert gen_id == gen_id_get

# Add generator with same name and no category
with pytest.raises(IntegrityError):
gen_id = db.add_object(
gen_01_name,
ClassEnum.Generator,
CollectionEnum.Generators,
description="Test Gen",
)

max_rank = db.get_category_max_id(ClassEnum.Generator)
assert max_rank == 2 # Data has ranks 0, 1. 2 is with the new category
Expand Down

0 comments on commit 0f52e9b

Please sign in to comment.