coffy 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coffy/__init__.py +1 -1
- coffy/__pycache__/__init__.cpython-312.pyc +0 -0
- coffy/graph/__init__.py +3 -1
- coffy/graph/__pycache__/__init__.cpython-312.pyc +0 -0
- coffy/graph/__pycache__/graphdb_nx.cpython-312.pyc +0 -0
- coffy/graph/graphdb_nx.py +335 -51
- coffy/nosql/__init__.py +4 -0
- coffy/nosql/__pycache__/__init__.cpython-312.pyc +0 -0
- coffy/nosql/__pycache__/engine.cpython-312.pyc +0 -0
- coffy/nosql/engine.py +396 -33
- coffy/sql/__init__.py +7 -0
- coffy/sql/__pycache__/__init__.cpython-312.pyc +0 -0
- coffy/sql/__pycache__/engine.cpython-312.pyc +0 -0
- coffy/sql/__pycache__/sqldict.cpython-312.pyc +0 -0
- coffy/sql/engine.py +30 -3
- coffy/sql/sqldict.py +53 -13
- {coffy-0.1.4.dist-info → coffy-0.1.6.dist-info}/METADATA +6 -4
- coffy-0.1.6.dist-info/RECORD +28 -0
- coffy/graph/graph_tests.py +0 -137
- coffy/nosql/nosql_tests.py +0 -104
- coffy-0.1.4.dist-info/RECORD +0 -30
- {coffy-0.1.4.dist-info → coffy-0.1.6.dist-info}/WHEEL +0 -0
- {coffy-0.1.4.dist-info → coffy-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {coffy-0.1.4.dist-info → coffy-0.1.6.dist-info}/top_level.txt +0 -0
Binary file
|
Binary file
|
coffy/sql/engine.py
CHANGED
@@ -1,22 +1,38 @@
|
|
1
1
|
# coffy/sql/engine.py
|
2
2
|
# author: nsarathy
|
3
3
|
|
4
|
-
|
4
|
+
"""
|
5
|
+
Shameless SQLite wrapper.
|
6
|
+
"""
|
7
|
+
|
5
8
|
from .sqldict import SQLDict
|
9
|
+
import sqlite3
|
6
10
|
|
7
11
|
# Internal connection state
|
8
12
|
_connection = None
|
9
13
|
_cursor = None
|
10
14
|
|
15
|
+
|
11
16
|
def initialize(db_path=None):
|
12
|
-
"""
|
17
|
+
"""
|
18
|
+
Initialize the SQL engine with the given database path.
|
19
|
+
db_path -- Path to the SQLite database file. If None, uses an in-memory
|
20
|
+
"""
|
13
21
|
global _connection, _cursor
|
14
22
|
if _connection:
|
15
23
|
return # already initialized
|
16
|
-
|
24
|
+
# Uses in-memory DB if no path provided
|
25
|
+
_connection = sqlite3.connect(db_path or ":memory:")
|
17
26
|
_cursor = _connection.cursor()
|
18
27
|
|
28
|
+
|
19
29
|
def execute_query(sql: str):
|
30
|
+
"""
|
31
|
+
Execute a SQL query and return the results.
|
32
|
+
sql -- The SQL query to execute.
|
33
|
+
Returns SQLDict for SELECT queries or a status dict for other queries.
|
34
|
+
"""
|
35
|
+
global _connection, _cursor
|
20
36
|
if _connection is None:
|
21
37
|
initialize() # uses in-memory if not initialized
|
22
38
|
|
@@ -31,3 +47,14 @@ def execute_query(sql: str):
|
|
31
47
|
return {"status": "success", "rows_affected": _cursor.rowcount}
|
32
48
|
except Exception as e:
|
33
49
|
return {"status": "error", "message": str(e)}
|
50
|
+
|
51
|
+
|
52
|
+
def close():
|
53
|
+
"""Close the database connection."""
|
54
|
+
global _connection, _cursor
|
55
|
+
if _cursor:
|
56
|
+
_cursor.close()
|
57
|
+
_cursor = None
|
58
|
+
if _connection:
|
59
|
+
_connection.close()
|
60
|
+
_connection = None
|
coffy/sql/sqldict.py
CHANGED
@@ -1,58 +1,98 @@
|
|
1
1
|
# coffy/sql/sqldict.py
|
2
2
|
# author: nsarathy
|
3
3
|
|
4
|
+
"""
|
5
|
+
A dictionary-like object for SQL query results.
|
6
|
+
"""
|
7
|
+
|
4
8
|
from collections.abc import Sequence
|
5
9
|
import csv
|
6
10
|
import json
|
7
11
|
|
12
|
+
|
8
13
|
class SQLDict(Sequence):
|
14
|
+
"""
|
15
|
+
A dictionary-like object that holds SQL query results.
|
16
|
+
"""
|
17
|
+
|
9
18
|
def __init__(self, data):
|
19
|
+
"""
|
20
|
+
Initialize with a list of dictionaries or a single dictionary.
|
21
|
+
data -- list or dict - The SQL query results.
|
22
|
+
"""
|
10
23
|
self._data = data if isinstance(data, list) else [data]
|
11
24
|
|
12
25
|
def __getitem__(self, index):
|
26
|
+
"""
|
27
|
+
Get item by index or key.
|
28
|
+
index -- Index for list-like access or key for dict-like access.
|
29
|
+
Returns the item at the specified index or the value for the key.
|
30
|
+
"""
|
13
31
|
return self._data[index]
|
14
32
|
|
15
33
|
def __len__(self):
|
34
|
+
"""
|
35
|
+
Get the number of items.
|
36
|
+
Returns the length of the data.
|
37
|
+
"""
|
16
38
|
return len(self._data)
|
17
39
|
|
18
40
|
def __repr__(self):
|
41
|
+
"""
|
42
|
+
String representation of the SQLDict.
|
43
|
+
Returns a formatted string of the SQLDict.
|
44
|
+
"""
|
19
45
|
if not self._data:
|
20
46
|
return "<empty result>"
|
21
47
|
|
22
48
|
# Get all column names
|
23
49
|
columns = list(self._data[0].keys())
|
24
|
-
col_widths = {
|
50
|
+
col_widths = {
|
51
|
+
col: max(len(col), *(len(str(row[col])) for row in self._data))
|
52
|
+
for col in columns
|
53
|
+
}
|
25
54
|
|
26
55
|
# Header
|
27
56
|
header = " | ".join(f"{col:<{col_widths[col]}}" for col in columns)
|
28
|
-
line = "-+-".join(
|
57
|
+
line = "-+-".join("-" * col_widths[col] for col in columns)
|
29
58
|
|
30
59
|
# Rows
|
31
60
|
rows = []
|
32
61
|
for row in self._data:
|
33
|
-
row_str = " | ".join(
|
62
|
+
row_str = " | ".join(
|
63
|
+
f"{str(row[col]):<{col_widths[col]}}" for col in columns
|
64
|
+
)
|
34
65
|
rows.append(row_str)
|
35
66
|
|
36
67
|
return f"{header}\n{line}\n" + "\n".join(rows)
|
37
68
|
|
38
69
|
def as_list(self):
|
39
|
-
"""
|
70
|
+
"""
|
71
|
+
Convert the SQLDict to a list of dictionaries.
|
72
|
+
Returns a list of dictionaries representing the SQL results.
|
73
|
+
"""
|
40
74
|
return self._data
|
41
|
-
|
75
|
+
|
42
76
|
def to_csv(self, path: str):
|
43
|
-
"""
|
77
|
+
"""
|
78
|
+
Write the SQLDict data to a CSV file.
|
79
|
+
path -- The file path to write the CSV data.
|
80
|
+
"""
|
44
81
|
if not self._data:
|
45
82
|
raise ValueError("No data to write.")
|
46
|
-
|
47
|
-
with open(path, mode=
|
83
|
+
|
84
|
+
with open(path, mode="w", newline="", encoding="utf-8") as file:
|
48
85
|
writer = csv.DictWriter(file, fieldnames=self._data[0].keys())
|
49
86
|
writer.writeheader()
|
50
87
|
writer.writerows(self._data)
|
51
|
-
|
88
|
+
|
52
89
|
def to_json(self, path: str):
|
53
|
-
"""
|
90
|
+
"""
|
91
|
+
Write the SQLDict data to a JSON file.
|
92
|
+
path -- The file path to write the JSON data.
|
93
|
+
"""
|
54
94
|
if not self._data:
|
55
95
|
raise ValueError("No data to write.")
|
56
|
-
|
57
|
-
with open(path, mode=
|
58
|
-
json.dump(self._data, file, indent=4)
|
96
|
+
|
97
|
+
with open(path, mode="w", encoding="utf-8") as file:
|
98
|
+
json.dump(self._data, file, indent=4)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: coffy
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.6
|
4
4
|
Summary: Lightweight local NoSQL, SQL, and Graph embedded database engine
|
5
5
|
Author: nsarathy
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
@@ -21,6 +21,8 @@ Dynamic: summary
|
|
21
21
|
|
22
22
|
# ☕ Coffy
|
23
23
|
|
24
|
+
[](https://pypi.org/project/coffy/)
|
25
|
+
|
24
26
|
**Coffy** is a lightweight embedded database engine for Python, designed for local-first apps, scripts, and tools. It includes:
|
25
27
|
|
26
28
|
- `coffy.nosql`: A simple JSON-backed NoSQL engine with a fluent, chainable query interface
|
@@ -52,7 +54,7 @@ pip install coffy
|
|
52
54
|
- Supports nested fields, logical filters, aggregations, projections, and joins
|
53
55
|
- Built for local usage with optional persistence; minimal setup, fast iteration
|
54
56
|
|
55
|
-
📄 [NoSQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/NOSQL_DOCS.md)
|
57
|
+
📄 [NoSQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/Documentation/NOSQL_DOCS.md)
|
56
58
|
|
57
59
|
---
|
58
60
|
|
@@ -62,7 +64,7 @@ pip install coffy
|
|
62
64
|
- Supports pattern matching, label/type filtering, logical conditions, and projections
|
63
65
|
- Query results can be saved, updated, or transformed; ideal for local, schema-flexible graph data
|
64
66
|
|
65
|
-
📄 [Graph Documentation →](https://github.com/nsarathy/Coffy/blob/main/GRAPH_DOCS.md)
|
67
|
+
📄 [Graph Documentation →](https://github.com/nsarathy/Coffy/blob/main/Documentation/GRAPH_DOCS.md)
|
66
68
|
|
67
69
|
---
|
68
70
|
|
@@ -72,7 +74,7 @@ pip install coffy
|
|
72
74
|
- Outputs as readable tables or exportable lists
|
73
75
|
- Uses in-memory DB by default, or json-based if initialized with a path
|
74
76
|
|
75
|
-
📄 [SQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/SQL_DOCS.md)
|
77
|
+
📄 [SQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/Documentation/SQL_DOCS.md)
|
76
78
|
|
77
79
|
---
|
78
80
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
coffy/__init__.py,sha256=K_1TePEQ2TO62qR9-YoRjVD-XASA9XQe4pF1uesALQ4,41
|
2
|
+
coffy/__pycache__/__init__.cpython-311.pyc,sha256=Mf7ImWR3gBHSLuRr-Wf__3OsjKGpdH8D5T2qm-em5AM,150
|
3
|
+
coffy/__pycache__/__init__.cpython-312.pyc,sha256=PTqUgO0XvyMGGb4yjpAF-b96WhADWm3a-0WauCd4i4w,146
|
4
|
+
coffy/graph/__init__.py,sha256=MiGraIFerNkNBTvpEN1aqeeR40QYM5_RqWQaYyBQUrQ,118
|
5
|
+
coffy/graph/graphdb_nx.py,sha256=pvksulKaVPKPbh9gl9zNI7wGdOsqVdiela9me2dO4ok,24346
|
6
|
+
coffy/graph/__pycache__/__init__.cpython-312.pyc,sha256=VqMZWLjWJ4eKpqQVeV6WuYRKmlp7xKT0NJNDMMEl5Zc,226
|
7
|
+
coffy/graph/__pycache__/graphdb_nx.cpython-312.pyc,sha256=OzqSg_RNiweMz16ExVveNgLT5HkwhC2nvBs4yZpxoZc,30814
|
8
|
+
coffy/nosql/__init__.py,sha256=mRYXQdafQmVKXsXzF3uIlMySa3TZ4bI027YTroZ6SQM,242
|
9
|
+
coffy/nosql/engine.py,sha256=T_ZCBfZgjk6yDrmnMem_3Nrap2TuNqdewK17UPSJ14U,24484
|
10
|
+
coffy/nosql/__pycache__/__init__.cpython-311.pyc,sha256=0dDD3adswuWrOOUVE_2KjO80zmOHkPAjW7pxMg4fukk,463
|
11
|
+
coffy/nosql/__pycache__/__init__.cpython-312.pyc,sha256=t6QeCcPAN01cVFr8NHcxyJzG8nGy9V9u1mqhsLcKi8s,449
|
12
|
+
coffy/nosql/__pycache__/engine.cpython-311.pyc,sha256=lJm3ZWQbStmBV1p9pmXdrIBcf2PMPM0SGozUEHdInA8,27471
|
13
|
+
coffy/nosql/__pycache__/engine.cpython-312.pyc,sha256=fIYgy07JVBk-D1wyiOiYaVhKnUaZSYInypaiU3KIdI4,36872
|
14
|
+
coffy/sql/__init__.py,sha256=MD2lkhqm0YbqYmbAL64z38fu4fGlHrgEtDmpp6-F0Mk,383
|
15
|
+
coffy/sql/engine.py,sha256=VmlXvOhYQwcsvNXXc5uvaJFa49aikc5bYx-PSGxkGHE,1673
|
16
|
+
coffy/sql/sqldict.py,sha256=YubYlmCCamkLFu-Itz3pzfAx7tgSBVKBHgGVi8txEAs,2896
|
17
|
+
coffy/sql/__pycache__/__init__.cpython-311.pyc,sha256=6kqiDVzq_X9yFEMupVrLUCQL3JinwHVxDFXJSrme3pI,628
|
18
|
+
coffy/sql/__pycache__/__init__.cpython-312.pyc,sha256=hybta-27fBZDAKI4_kg2NL9dMizSP2FaWd_9RpCIen0,669
|
19
|
+
coffy/sql/__pycache__/engine.cpython-311.pyc,sha256=IbSKkWjikTnkXhGDgqJHo2n49SREzRULeUXQfAcFt_Q,2239
|
20
|
+
coffy/sql/__pycache__/engine.cpython-312.pyc,sha256=aQjP6O76qo7dWx6_pXKppntD1X63199aLbkPJPUFc5Q,2374
|
21
|
+
coffy/sql/__pycache__/io.cpython-312.pyc,sha256=TPkeJ3qUE_ZcvcykGIf-Yyc0__5FZKB1J95_2yTFrXY,2517
|
22
|
+
coffy/sql/__pycache__/sqldict.cpython-311.pyc,sha256=jJQDFH9ULzi2ay4pyXXye-W_PSjxuT_ULb20CdL8Ec0,5131
|
23
|
+
coffy/sql/__pycache__/sqldict.cpython-312.pyc,sha256=oWaNK-fxB-gAuKsPWRpDAirDYzoUcL0sgJ5jrIqof08,5283
|
24
|
+
coffy-0.1.6.dist-info/licenses/LICENSE,sha256=iRyxG6b7B-JPKuOcS7w5lDhrL0AD9fFSDUh6-KMKDO8,1068
|
25
|
+
coffy-0.1.6.dist-info/METADATA,sha256=zS1ZfDi7LPucbvVdFXilrPUuZrn5prVpqaLfsAa5fXo,3389
|
26
|
+
coffy-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
27
|
+
coffy-0.1.6.dist-info/top_level.txt,sha256=J1zGvXA_bfET3PsE4-qbFrtFMIN3bJNxBRMHKk6YIv0,6
|
28
|
+
coffy-0.1.6.dist-info/RECORD,,
|
coffy/graph/graph_tests.py
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import unittest
|
3
|
-
import tempfile
|
4
|
-
import os
|
5
|
-
from graphdb_nx import GraphDB
|
6
|
-
|
7
|
-
|
8
|
-
class TestGraphDB(unittest.TestCase):
|
9
|
-
|
10
|
-
def setUp(self):
|
11
|
-
self.temp_path = tempfile.NamedTemporaryFile(delete=False, suffix=".json").name
|
12
|
-
self.db = GraphDB(path=self.temp_path)
|
13
|
-
self.db.add_node("A", labels="Person", name="Alice", age=30)
|
14
|
-
self.db.add_node("B", labels="Person", name="Bob", age=25)
|
15
|
-
self.db.add_node("C", labels="Person", name="Carol", age=40)
|
16
|
-
self.db.add_relationship("A", "B", rel_type="KNOWS", since=2010)
|
17
|
-
self.db.add_relationship("B", "C", rel_type="KNOWS", since=2015)
|
18
|
-
|
19
|
-
def tearDown(self):
|
20
|
-
os.remove(self.temp_path)
|
21
|
-
|
22
|
-
def test_add_and_get_node(self):
|
23
|
-
self.db.add_node("D", labels="Person", name="Dan")
|
24
|
-
node = self.db.get_node("D")
|
25
|
-
self.assertEqual(node["name"], "Dan")
|
26
|
-
|
27
|
-
def test_remove_node(self):
|
28
|
-
self.db.remove_node("C")
|
29
|
-
self.assertFalse(self.db.has_node("C"))
|
30
|
-
|
31
|
-
def test_add_and_get_relationship(self):
|
32
|
-
rel = self.db.get_relationship("A", "B")
|
33
|
-
self.assertEqual(rel["_type"], "KNOWS")
|
34
|
-
|
35
|
-
def test_update_node(self):
|
36
|
-
self.db.update_node("A", age=35, city="Wonderland")
|
37
|
-
node = self.db.get_node("A")
|
38
|
-
self.assertEqual(node["age"], 35)
|
39
|
-
self.assertEqual(node["city"], "Wonderland")
|
40
|
-
|
41
|
-
def test_update_relationship(self):
|
42
|
-
self.db.update_relationship("A", "B", since=2020, weight=0.8)
|
43
|
-
rel = self.db.get_relationship("A", "B")
|
44
|
-
self.assertEqual(rel["since"], 2020)
|
45
|
-
self.assertEqual(rel["weight"], 0.8)
|
46
|
-
|
47
|
-
def test_set_node_update(self):
|
48
|
-
self.db.set_node("A", name="Alicia", mood="happy")
|
49
|
-
node = self.db.get_node("A")
|
50
|
-
self.assertEqual(node["name"], "Alicia")
|
51
|
-
self.assertEqual(node["mood"], "happy")
|
52
|
-
|
53
|
-
def test_set_node_add(self):
|
54
|
-
self.db.set_node("Z", labels="Robot", name="Zeta", age=5)
|
55
|
-
self.assertTrue(self.db.has_node("Z"))
|
56
|
-
node = self.db.get_node("Z")
|
57
|
-
self.assertEqual(node["name"], "Zeta")
|
58
|
-
self.assertEqual(node["age"], 5)
|
59
|
-
self.assertIn("Robot", node["_labels"])
|
60
|
-
|
61
|
-
def test_remove_relationship(self):
|
62
|
-
self.db.remove_relationship("A", "B")
|
63
|
-
self.assertFalse(self.db.has_relationship("A", "B"))
|
64
|
-
|
65
|
-
def test_find_nodes_basic(self):
|
66
|
-
results = self.db.find_nodes(name="Alice")
|
67
|
-
self.assertEqual(len(results), 1)
|
68
|
-
self.assertEqual(results[0]["name"], "Alice")
|
69
|
-
|
70
|
-
def test_find_nodes_logic_or(self):
|
71
|
-
results = self.db.find_nodes(_logic="or", name="Alice", age={"gt": 35})
|
72
|
-
names = {r["name"] for r in results}
|
73
|
-
self.assertIn("Alice", names)
|
74
|
-
self.assertIn("Carol", names)
|
75
|
-
|
76
|
-
def test_find_nodes_logic_not(self):
|
77
|
-
results = self.db.find_nodes(_logic="not", age={"lt": 35})
|
78
|
-
self.assertEqual(len(results), 1)
|
79
|
-
self.assertEqual(results[0]["name"], "Carol")
|
80
|
-
|
81
|
-
def test_find_by_label(self):
|
82
|
-
results = self.db.find_by_label("Person")
|
83
|
-
self.assertEqual(len(results), 3)
|
84
|
-
|
85
|
-
def test_find_relationships_type_and_filter(self):
|
86
|
-
results = self.db.find_relationships(rel_type="KNOWS", since={"gte": 2011})
|
87
|
-
self.assertEqual(len(results), 1)
|
88
|
-
self.assertEqual(results[0]["target"], "C")
|
89
|
-
|
90
|
-
def test_project_node_fields(self):
|
91
|
-
result = self.db.project_node("A", fields=["name"])
|
92
|
-
self.assertEqual(result, {"name": "Alice"})
|
93
|
-
|
94
|
-
def test_project_relationship_fields(self):
|
95
|
-
result = self.db.project_relationship("A", "B", fields=["since"])
|
96
|
-
self.assertEqual(result, {"since": 2010})
|
97
|
-
|
98
|
-
def test_match_node_path(self):
|
99
|
-
pattern = [{"rel_type": "KNOWS", "node": {"name": "Bob"}}]
|
100
|
-
paths = self.db.match_node_path(start={"name": "Alice"}, pattern=pattern)
|
101
|
-
self.assertEqual(len(paths), 1)
|
102
|
-
self.assertEqual(paths[0][0]["name"], "Alice")
|
103
|
-
self.assertEqual(paths[0][1]["name"], "Bob")
|
104
|
-
|
105
|
-
def test_match_full_path(self):
|
106
|
-
pattern = [{"rel_type": "KNOWS", "node": {"name": "Bob"}}, {"rel_type": "KNOWS", "node": {"name": "Carol"}}]
|
107
|
-
results = self.db.match_full_path(start={"name": "Alice"}, pattern=pattern)
|
108
|
-
self.assertEqual(len(results), 1)
|
109
|
-
self.assertEqual(results[0]["nodes"][2]["name"], "Carol")
|
110
|
-
self.assertEqual(results[0]["relationships"][0]["type"], "KNOWS")
|
111
|
-
|
112
|
-
def test_match_path_structured(self):
|
113
|
-
pattern = [{"rel_type": "KNOWS", "node": {"name": "Bob"}}]
|
114
|
-
result = self.db.match_path_structured(start={"name": "Alice"}, pattern=pattern)
|
115
|
-
self.assertEqual(len(result), 1)
|
116
|
-
path = result[0]["path"]
|
117
|
-
self.assertEqual(path[0]["node"]["name"], "Alice")
|
118
|
-
self.assertEqual(path[1]["relationship"]["type"], "KNOWS")
|
119
|
-
self.assertEqual(path[2]["node"]["name"], "Bob")
|
120
|
-
|
121
|
-
def test_save_and_load(self):
|
122
|
-
self.db.save()
|
123
|
-
new_db = GraphDB(path=self.temp_path)
|
124
|
-
self.assertTrue(new_db.has_node("A"))
|
125
|
-
self.assertTrue(new_db.has_relationship("A", "B"))
|
126
|
-
|
127
|
-
def test_save_query_result(self):
|
128
|
-
result = self.db.find_nodes(name="Alice")
|
129
|
-
temp_result_path = self.temp_path.replace(".json", "_result.json")
|
130
|
-
self.db.save_query_result(result, path=temp_result_path)
|
131
|
-
with open(temp_result_path) as f:
|
132
|
-
loaded = json.load(f)
|
133
|
-
self.assertEqual(loaded[0]["name"], "Alice")
|
134
|
-
os.remove(temp_result_path)
|
135
|
-
|
136
|
-
|
137
|
-
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(TestGraphDB))
|
coffy/nosql/nosql_tests.py
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
# coffy/nosql/nosql_tests.py
|
2
|
-
# author: nsarathy
|
3
|
-
|
4
|
-
import unittest
|
5
|
-
from engine import CollectionManager
|
6
|
-
|
7
|
-
class TestCollectionManager(unittest.TestCase):
|
8
|
-
|
9
|
-
def setUp(self):
|
10
|
-
self.col = CollectionManager(name="test_collection")
|
11
|
-
self.col.clear()
|
12
|
-
self.col.add_many([
|
13
|
-
{"name": "Alice", "age": 30, "tags": ["x", "y"]},
|
14
|
-
{"name": "Bob", "age": 25, "tags": ["y", "z"]},
|
15
|
-
{"name": "Carol", "age": 40, "nested": {"score": 100}},
|
16
|
-
])
|
17
|
-
|
18
|
-
def test_add_and_all_docs(self):
|
19
|
-
result = self.col.all_docs()
|
20
|
-
self.assertEqual(len(result), 3)
|
21
|
-
|
22
|
-
def test_where_eq(self):
|
23
|
-
q = self.col.where("name").eq("Alice")
|
24
|
-
self.assertEqual(q.count(), 1)
|
25
|
-
self.assertEqual(q.first()["age"], 30)
|
26
|
-
|
27
|
-
def test_where_gt_and_lt(self):
|
28
|
-
gt_q = self.col.where("age").gt(26)
|
29
|
-
lt_q = self.col.where("age").lt(40)
|
30
|
-
self.assertEqual(gt_q.count(), 2)
|
31
|
-
self.assertEqual(lt_q.count(), 2)
|
32
|
-
|
33
|
-
def test_exists(self):
|
34
|
-
q = self.col.where("nested").exists()
|
35
|
-
self.assertEqual(q.count(), 1)
|
36
|
-
self.assertEqual(q.first()["name"], "Carol")
|
37
|
-
|
38
|
-
def test_in_and_nin(self):
|
39
|
-
q1 = self.col.where("name").in_(["Alice", "Bob"])
|
40
|
-
q2 = self.col.where("name").nin(["Carol"])
|
41
|
-
self.assertEqual(q1.count(), 2)
|
42
|
-
self.assertEqual(q2.count(), 2)
|
43
|
-
|
44
|
-
def test_matches(self):
|
45
|
-
q = self.col.where("name").matches("^A")
|
46
|
-
self.assertEqual(q.count(), 1)
|
47
|
-
self.assertEqual(q.first()["name"], "Alice")
|
48
|
-
|
49
|
-
def test_nested_field_access(self):
|
50
|
-
q = self.col.where("nested.score").eq(100)
|
51
|
-
self.assertEqual(q.count(), 1)
|
52
|
-
self.assertEqual(q.first()["name"], "Carol")
|
53
|
-
|
54
|
-
def test_logic_and_or_not(self):
|
55
|
-
q = self.col.match_all(
|
56
|
-
lambda q: q.where("age").gte(25),
|
57
|
-
lambda q: q.where("age").lt(40)
|
58
|
-
)
|
59
|
-
self.assertEqual(q.count(), 2)
|
60
|
-
|
61
|
-
q = self.col.match_any(
|
62
|
-
lambda q: q.where("name").eq("Alice"),
|
63
|
-
lambda q: q.where("name").eq("Bob")
|
64
|
-
)
|
65
|
-
self.assertEqual(q.count(), 2)
|
66
|
-
|
67
|
-
q = self.col.not_any(
|
68
|
-
lambda q: q.where("name").eq("Bob"),
|
69
|
-
lambda q: q.where("age").eq(40)
|
70
|
-
)
|
71
|
-
self.assertEqual(q.count(), 1)
|
72
|
-
self.assertEqual(q.first()["name"], "Alice")
|
73
|
-
|
74
|
-
def test_run_with_projection(self):
|
75
|
-
q = self.col.where("age").gte(25)
|
76
|
-
result = q.run(fields=["name"])
|
77
|
-
self.assertEqual(len(result), 3)
|
78
|
-
for doc in result:
|
79
|
-
self.assertEqual(list(doc.keys()), ["name"])
|
80
|
-
|
81
|
-
def test_update_and_delete_and_replace(self):
|
82
|
-
self.col.where("name").eq("Alice").update({"updated": True})
|
83
|
-
updated = self.col.where("updated").eq(True).first()
|
84
|
-
self.assertEqual(updated["name"], "Alice")
|
85
|
-
|
86
|
-
self.col.where("name").eq("Bob").delete()
|
87
|
-
self.assertEqual(self.col.where("name").eq("Bob").count(), 0)
|
88
|
-
|
89
|
-
self.col.where("name").eq("Carol").replace({"name": "New", "age": 99})
|
90
|
-
new_doc = self.col.where("name").eq("New").first()
|
91
|
-
self.assertEqual(new_doc["age"], 99)
|
92
|
-
|
93
|
-
def test_aggregates(self):
|
94
|
-
self.assertEqual(self.col.sum("age"), 95)
|
95
|
-
self.assertEqual(self.col.avg("age"), 95 / 3)
|
96
|
-
self.assertEqual(self.col.min("age"), 25)
|
97
|
-
self.assertEqual(self.col.max("age"), 40)
|
98
|
-
|
99
|
-
def test_merge(self):
|
100
|
-
q = self.col.where("name").eq("Alice")
|
101
|
-
merged = q.merge(lambda d: {"new": d["age"] + 10}).run()
|
102
|
-
self.assertEqual(merged[0]["new"], 40)
|
103
|
-
|
104
|
-
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(TestCollectionManager))
|
coffy-0.1.4.dist-info/RECORD
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
coffy/__init__.py,sha256=Q5FwcCZtemm41gSD-n-t9zAipeh5XV7JuoBzEHze7J8,39
|
2
|
-
coffy/__pycache__/__init__.cpython-311.pyc,sha256=Mf7ImWR3gBHSLuRr-Wf__3OsjKGpdH8D5T2qm-em5AM,150
|
3
|
-
coffy/__pycache__/__init__.cpython-312.pyc,sha256=n_nvLEknScZM-FT6aNmDlpZGQd_ZHB3p_zRJirbI_Ic,146
|
4
|
-
coffy/graph/__init__.py,sha256=Z0cIgmxre3YgwBrstubB1PTElP5uatz3ZOEIuW9EwY4,80
|
5
|
-
coffy/graph/graph_tests.py,sha256=PmgoR2t14_2H2ynSavCmORuuxXqC5co1KWfYM1svKko,5661
|
6
|
-
coffy/graph/graphdb_nx.py,sha256=ol4tmfTSbLm8o-i_zXOvxjKUSi5Z4h1rl2-Hd9HOBKg,13648
|
7
|
-
coffy/graph/__pycache__/__init__.cpython-312.pyc,sha256=GCuchZzMG77ILVDNh1wX5eerxwQlrnm0VGNAqjGITXg,199
|
8
|
-
coffy/graph/__pycache__/graphdb_nx.cpython-312.pyc,sha256=JSv_32YuTTsDy9Yfl3jCSUYORtH95_pL6OHTtrvf7Ts,21201
|
9
|
-
coffy/nosql/__init__.py,sha256=HN9UfUd9pooB7udUxNn3EAttEjGG394fECOXDb6SH60,197
|
10
|
-
coffy/nosql/engine.py,sha256=6pN9uIC_Z07ZWlIkD8g4TZv8kwjZ9RMreM41HIsa6mM,11009
|
11
|
-
coffy/nosql/nosql_tests.py,sha256=fL7C8zm-o48CZPIA4-FPtDHsn59Ahj4zH4e6LlJFHQk,3743
|
12
|
-
coffy/nosql/__pycache__/__init__.cpython-311.pyc,sha256=0dDD3adswuWrOOUVE_2KjO80zmOHkPAjW7pxMg4fukk,463
|
13
|
-
coffy/nosql/__pycache__/__init__.cpython-312.pyc,sha256=NdU26hkvPHawUktm4TUoEirspFgWDMaixaQV7cGAYIc,409
|
14
|
-
coffy/nosql/__pycache__/engine.cpython-311.pyc,sha256=lJm3ZWQbStmBV1p9pmXdrIBcf2PMPM0SGozUEHdInA8,27471
|
15
|
-
coffy/nosql/__pycache__/engine.cpython-312.pyc,sha256=wzPBARp1nNbNEWhnAcHRIoI4yPxDFivFs-eGY7Db40A,25140
|
16
|
-
coffy/sql/__init__.py,sha256=dXoCW3Qyk3WoYl-y2gHCc8YK6dAUt0Eaqhtk3PEDHMQ,202
|
17
|
-
coffy/sql/engine.py,sha256=q9A7zvg9JANpWAFvbw8wfGY5BTkvObpM3eoQ6dZYu_8,1065
|
18
|
-
coffy/sql/sqldict.py,sha256=0IxlCz1hi1zj5Q4_8If9YXwQBZGczpMir7PMXBOxbjs,1837
|
19
|
-
coffy/sql/__pycache__/__init__.cpython-311.pyc,sha256=6kqiDVzq_X9yFEMupVrLUCQL3JinwHVxDFXJSrme3pI,628
|
20
|
-
coffy/sql/__pycache__/__init__.cpython-312.pyc,sha256=jB-bTlCkiV0dNzISmDoBcqf861iQF6q2O14ylDO39yw,535
|
21
|
-
coffy/sql/__pycache__/engine.cpython-311.pyc,sha256=IbSKkWjikTnkXhGDgqJHo2n49SREzRULeUXQfAcFt_Q,2239
|
22
|
-
coffy/sql/__pycache__/engine.cpython-312.pyc,sha256=fKlvycKKYZOslQd6SDHexrWV_NDcPoqNQWoiS8do49w,1746
|
23
|
-
coffy/sql/__pycache__/io.cpython-312.pyc,sha256=TPkeJ3qUE_ZcvcykGIf-Yyc0__5FZKB1J95_2yTFrXY,2517
|
24
|
-
coffy/sql/__pycache__/sqldict.cpython-311.pyc,sha256=jJQDFH9ULzi2ay4pyXXye-W_PSjxuT_ULb20CdL8Ec0,5131
|
25
|
-
coffy/sql/__pycache__/sqldict.cpython-312.pyc,sha256=T4P9qMjH7dHqDTp3gzZQbhpdpfgdopmC3ccm8O8gooc,4339
|
26
|
-
coffy-0.1.4.dist-info/licenses/LICENSE,sha256=iRyxG6b7B-JPKuOcS7w5lDhrL0AD9fFSDUh6-KMKDO8,1068
|
27
|
-
coffy-0.1.4.dist-info/METADATA,sha256=_snYfJ7Ql1pi4HagcwMcOHB8CBz-HZanwFR8MRu_cos,3256
|
28
|
-
coffy-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
29
|
-
coffy-0.1.4.dist-info/top_level.txt,sha256=J1zGvXA_bfET3PsE4-qbFrtFMIN3bJNxBRMHKk6YIv0,6
|
30
|
-
coffy-0.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|