coffy 0.1.1__tar.gz → 0.1.4__tar.gz

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.
Files changed (41) hide show
  1. {coffy-0.1.1/coffy.egg-info → coffy-0.1.4}/PKG-INFO +31 -26
  2. coffy-0.1.4/README.md +90 -0
  3. coffy-0.1.4/coffy/graph/__pycache__/graphdb_nx.cpython-312.pyc +0 -0
  4. coffy-0.1.4/coffy/graph/graph_tests.py +137 -0
  5. coffy-0.1.4/coffy/graph/graphdb_nx.py +379 -0
  6. coffy-0.1.4/coffy/nosql/__pycache__/engine.cpython-312.pyc +0 -0
  7. {coffy-0.1.1 → coffy-0.1.4}/coffy/nosql/engine.py +37 -15
  8. coffy-0.1.4/coffy/nosql/nosql_tests.py +104 -0
  9. {coffy-0.1.1 → coffy-0.1.4/coffy.egg-info}/PKG-INFO +31 -26
  10. {coffy-0.1.1 → coffy-0.1.4}/coffy.egg-info/SOURCES.txt +2 -0
  11. {coffy-0.1.1 → coffy-0.1.4}/setup.py +1 -1
  12. coffy-0.1.1/README.md +0 -85
  13. coffy-0.1.1/coffy/graph/__pycache__/graphdb_nx.cpython-312.pyc +0 -0
  14. coffy-0.1.1/coffy/graph/graphdb_nx.py +0 -125
  15. coffy-0.1.1/coffy/nosql/__pycache__/engine.cpython-312.pyc +0 -0
  16. {coffy-0.1.1 → coffy-0.1.4}/LICENSE +0 -0
  17. {coffy-0.1.1 → coffy-0.1.4}/MANIFEST.in +0 -0
  18. {coffy-0.1.1 → coffy-0.1.4}/coffy/__init__.py +0 -0
  19. {coffy-0.1.1 → coffy-0.1.4}/coffy/__pycache__/__init__.cpython-311.pyc +0 -0
  20. {coffy-0.1.1 → coffy-0.1.4}/coffy/__pycache__/__init__.cpython-312.pyc +0 -0
  21. {coffy-0.1.1 → coffy-0.1.4}/coffy/graph/__init__.py +0 -0
  22. {coffy-0.1.1 → coffy-0.1.4}/coffy/graph/__pycache__/__init__.cpython-312.pyc +0 -0
  23. {coffy-0.1.1 → coffy-0.1.4}/coffy/nosql/__init__.py +0 -0
  24. {coffy-0.1.1 → coffy-0.1.4}/coffy/nosql/__pycache__/__init__.cpython-311.pyc +0 -0
  25. {coffy-0.1.1 → coffy-0.1.4}/coffy/nosql/__pycache__/__init__.cpython-312.pyc +0 -0
  26. {coffy-0.1.1 → coffy-0.1.4}/coffy/nosql/__pycache__/engine.cpython-311.pyc +0 -0
  27. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__init__.py +0 -0
  28. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/__init__.cpython-311.pyc +0 -0
  29. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/__init__.cpython-312.pyc +0 -0
  30. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/engine.cpython-311.pyc +0 -0
  31. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/engine.cpython-312.pyc +0 -0
  32. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/io.cpython-312.pyc +0 -0
  33. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/sqldict.cpython-311.pyc +0 -0
  34. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/__pycache__/sqldict.cpython-312.pyc +0 -0
  35. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/engine.py +0 -0
  36. {coffy-0.1.1 → coffy-0.1.4}/coffy/sql/sqldict.py +0 -0
  37. {coffy-0.1.1 → coffy-0.1.4}/coffy.egg-info/dependency_links.txt +0 -0
  38. {coffy-0.1.1 → coffy-0.1.4}/coffy.egg-info/requires.txt +0 -0
  39. {coffy-0.1.1 → coffy-0.1.4}/coffy.egg-info/top_level.txt +0 -0
  40. {coffy-0.1.1 → coffy-0.1.4}/pyproject.toml +0 -0
  41. {coffy-0.1.1 → coffy-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coffy
3
- Version: 0.1.1
3
+ Version: 0.1.4
4
4
  Summary: Lightweight local NoSQL, SQL, and Graph embedded database engine
5
5
  Author: nsarathy
6
6
  Classifier: Programming Language :: Python :: 3
@@ -24,10 +24,15 @@ Dynamic: summary
24
24
  **Coffy** is a lightweight embedded database engine for Python, designed for local-first apps, scripts, and tools. It includes:
25
25
 
26
26
  - `coffy.nosql`: A simple JSON-backed NoSQL engine with a fluent, chainable query interface
27
- - `coffy.sql`: A wrapper over SQLite for executing raw SQL with clean tabular results
28
- - `coffy.graph`: An graph engine built on `networkx` with advanced filtering and logic-based querying
27
+ - `coffy.graph`: An graph engine built on `networkx` with advanced filtering and logic-based querying
28
+ - `coffy.sql`: A wrapper over `SQLite` for executing raw SQL
29
29
 
30
- No dependencies (except `networkx`). No boilerplate. Just data.
30
+ ---
31
+ ## Latest Updates
32
+ - Added projection and dot-notation support for nested fields in NoSQL queries. Expanded logic chaining (`_and`, `_or`, `_not`) with improved test and better query semantics.
33
+ - GraphDB now supports saving query results, fixed relationship type serialization, and added more robust unit coverage.
34
+ - Unit tests added to test NoSQL and Graph database features.
35
+ - Documentation Updated.
31
36
 
32
37
  ---
33
38
 
@@ -43,31 +48,31 @@ pip install coffy
43
48
 
44
49
  ### `coffy.nosql`
45
50
 
46
- - JSON-based collections with fluent `.where().eq().gt()...` query chaining
47
- - Joins, updates, filters, aggregation, export/import
48
- - All data saved to human-readable `.json` files
51
+ - Embedded NoSQL document store with a fluent, chainable query API
52
+ - Supports nested fields, logical filters, aggregations, projections, and joins
53
+ - Built for local usage with optional persistence; minimal setup, fast iteration
49
54
 
50
- 📄 [NoSQL Documentation →](./NOSQL_DOCS.md)
55
+ 📄 [NoSQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/NOSQL_DOCS.md)
51
56
 
52
57
  ---
53
58
 
54
- ### `coffy.sql`
59
+ ### `coffy.graph`
55
60
 
56
- - SQLite-backed engine with raw SQL query support
57
- - Outputs as readable tables or exportable lists
58
- - Uses in-memory DB by default, or json-based if initialized with a path
61
+ - Lightweight, file-backed graph database using `networkx` under the hood
62
+ - Supports pattern matching, label/type filtering, logical conditions, and projections
63
+ - Query results can be saved, updated, or transformed; ideal for local, schema-flexible graph data
59
64
 
60
- 📄 [SQL Documentation →](./SQL_DOCS.md)
65
+ 📄 [Graph Documentation →](https://github.com/nsarathy/Coffy/blob/main/GRAPH_DOCS.md)
61
66
 
62
67
  ---
63
68
 
64
- ### `coffy.graph`
69
+ ### `coffy.sql`
65
70
 
66
- - Wrapper around `networkx` with simplified node/relationship API
67
- - Query nodes and relationships using filters like `gt`, `lt`, `eq`, `or`, `not`
71
+ - SQLite-backed engine with raw SQL query support
72
+ - Outputs as readable tables or exportable lists
68
73
  - Uses in-memory DB by default, or json-based if initialized with a path
69
74
 
70
- 📄 [Graph Documentation →](./GRAPH_DOCS.md)
75
+ 📄 [SQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/SQL_DOCS.md)
71
76
 
72
77
  ---
73
78
 
@@ -81,15 +86,6 @@ users.add({"id": 1, "name": "Neel"})
81
86
  print(users.where("name").eq("Neel").first())
82
87
  ```
83
88
 
84
- ```python
85
- from coffy.sql import init, query
86
-
87
- init("app.db")
88
- query("CREATE TABLE test (id INT, name TEXT)")
89
- query("INSERT INTO test VALUES (1, 'Neel')")
90
- print(query("SELECT * FROM test"))
91
- ```
92
-
93
89
  ```python
94
90
  from coffy.graph import GraphDB
95
91
 
@@ -99,6 +95,15 @@ g.add_relationships([{"source": 1, "target": 2, "type": "friend"}])
99
95
  print(g.find_relationships(type="friend"))
100
96
  ```
101
97
 
98
+ ```python
99
+ from coffy.sql import init, query
100
+
101
+ init("app.db")
102
+ query("CREATE TABLE test (id INT, name TEXT)")
103
+ query("INSERT INTO test VALUES (1, 'Neel')")
104
+ print(query("SELECT * FROM test"))
105
+ ```
106
+
102
107
  ---
103
108
 
104
109
  ## 📄 License
coffy-0.1.4/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # ☕ Coffy
2
+
3
+ **Coffy** is a lightweight embedded database engine for Python, designed for local-first apps, scripts, and tools. It includes:
4
+
5
+ - `coffy.nosql`: A simple JSON-backed NoSQL engine with a fluent, chainable query interface
6
+ - `coffy.graph`: An graph engine built on `networkx` with advanced filtering and logic-based querying
7
+ - `coffy.sql`: A wrapper over `SQLite` for executing raw SQL
8
+
9
+ ---
10
+ ## Latest Updates
11
+ - Added projection and dot-notation support for nested fields in NoSQL queries. Expanded logic chaining (`_and`, `_or`, `_not`) with improved test and better query semantics.
12
+ - GraphDB now supports saving query results, fixed relationship type serialization, and added more robust unit coverage.
13
+ - Unit tests added to test NoSQL and Graph database features.
14
+ - Documentation Updated.
15
+
16
+ ---
17
+
18
+ ## 🔧 Install
19
+
20
+ ```bash
21
+ pip install coffy
22
+ ```
23
+
24
+ ---
25
+
26
+ ## 📂 Modules
27
+
28
+ ### `coffy.nosql`
29
+
30
+ - Embedded NoSQL document store with a fluent, chainable query API
31
+ - Supports nested fields, logical filters, aggregations, projections, and joins
32
+ - Built for local usage with optional persistence; minimal setup, fast iteration
33
+
34
+ 📄 [NoSQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/NOSQL_DOCS.md)
35
+
36
+ ---
37
+
38
+ ### `coffy.graph`
39
+
40
+ - Lightweight, file-backed graph database using `networkx` under the hood
41
+ - Supports pattern matching, label/type filtering, logical conditions, and projections
42
+ - Query results can be saved, updated, or transformed; ideal for local, schema-flexible graph data
43
+
44
+ 📄 [Graph Documentation →](https://github.com/nsarathy/Coffy/blob/main/GRAPH_DOCS.md)
45
+
46
+ ---
47
+
48
+ ### `coffy.sql`
49
+
50
+ - SQLite-backed engine with raw SQL query support
51
+ - Outputs as readable tables or exportable lists
52
+ - Uses in-memory DB by default, or json-based if initialized with a path
53
+
54
+ 📄 [SQL Documentation →](https://github.com/nsarathy/Coffy/blob/main/SQL_DOCS.md)
55
+
56
+ ---
57
+
58
+ ## 🧪 Example
59
+
60
+ ```python
61
+ from coffy.nosql import db
62
+
63
+ users = db("users", path="users.json")
64
+ users.add({"id": 1, "name": "Neel"})
65
+ print(users.where("name").eq("Neel").first())
66
+ ```
67
+
68
+ ```python
69
+ from coffy.graph import GraphDB
70
+
71
+ g = GraphDB(directed=True)
72
+ g.add_nodes([{"id": 1, "name": "Neel"}, {"id": 2, "name": "Tanaya"}])
73
+ g.add_relationships([{"source": 1, "target": 2, "type": "friend"}])
74
+ print(g.find_relationships(type="friend"))
75
+ ```
76
+
77
+ ```python
78
+ from coffy.sql import init, query
79
+
80
+ init("app.db")
81
+ query("CREATE TABLE test (id INT, name TEXT)")
82
+ query("INSERT INTO test VALUES (1, 'Neel')")
83
+ print(query("SELECT * FROM test"))
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 📄 License
89
+
90
+ MIT © 2025 nsarathy
@@ -0,0 +1,137 @@
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))