datacommons-db 1.1.3__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 (30) hide show
  1. datacommons_db-1.1.3/PKG-INFO +241 -0
  2. datacommons_db-1.1.3/README.md +220 -0
  3. datacommons_db-1.1.3/VERSION +1 -0
  4. datacommons_db-1.1.3/datacommons_db/__init__.py +17 -0
  5. datacommons_db-1.1.3/datacommons_db/clients/__init__.py +29 -0
  6. datacommons_db-1.1.3/datacommons_db/clients/spanner_client.py +251 -0
  7. datacommons_db-1.1.3/datacommons_db/migrations/__init__.py +25 -0
  8. datacommons_db-1.1.3/datacommons_db/migrations/base.py +42 -0
  9. datacommons_db-1.1.3/datacommons_db/migrations/migration_runner.py +297 -0
  10. datacommons_db-1.1.3/datacommons_db/migrations/migration_scripts/20260817000000_bootstrap.py +50 -0
  11. datacommons_db-1.1.3/datacommons_db/migrations/migration_scripts/__init__.py +13 -0
  12. datacommons_db-1.1.3/datacommons_db/models/__init__.py +0 -0
  13. datacommons_db-1.1.3/datacommons_db/models/base.py +18 -0
  14. datacommons_db-1.1.3/datacommons_db/models/edge.py +82 -0
  15. datacommons_db-1.1.3/datacommons_db/models/node.py +57 -0
  16. datacommons_db-1.1.3/datacommons_db/models/observation.py +66 -0
  17. datacommons_db-1.1.3/datacommons_db/repositories/__init__.py +0 -0
  18. datacommons_db-1.1.3/datacommons_db/repositories/node_repository.py +38 -0
  19. datacommons_db-1.1.3/datacommons_db/session.py +93 -0
  20. datacommons_db-1.1.3/datacommons_db/utils/__init__.py +27 -0
  21. datacommons_db-1.1.3/datacommons_db/utils/validators.py +55 -0
  22. datacommons_db-1.1.3/datacommons_db.egg-info/PKG-INFO +241 -0
  23. datacommons_db-1.1.3/datacommons_db.egg-info/SOURCES.txt +28 -0
  24. datacommons_db-1.1.3/datacommons_db.egg-info/dependency_links.txt +1 -0
  25. datacommons_db-1.1.3/datacommons_db.egg-info/requires.txt +4 -0
  26. datacommons_db-1.1.3/datacommons_db.egg-info/top_level.txt +1 -0
  27. datacommons_db-1.1.3/pyproject.toml +41 -0
  28. datacommons_db-1.1.3/setup.cfg +4 -0
  29. datacommons_db-1.1.3/tests/test_spanner_client.py +444 -0
  30. datacommons_db-1.1.3/tests/test_validators.py +79 -0
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.4
2
+ Name: datacommons-db
3
+ Version: 1.1.3
4
+ Summary: Data Commons DB
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Documentation, https://github.com/datacommonsorg/datacommons#readme
7
+ Project-URL: Issues, https://github.com/datacommonsorg/datacommons/issues
8
+ Project-URL: Source, https://github.com/datacommonsorg/datacommons
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: sqlalchemy
18
+ Requires-Dist: sqlalchemy-spanner
19
+ Requires-Dist: google-cloud-spanner>=3.67.0
20
+ Requires-Dist: setuptools<=80.0.0
21
+
22
+ # Data Commons Database Module
23
+
24
+ This module provides the database models and client primitives for the Data Commons project, implementing a graph database using Google Cloud Spanner and SQLAlchemy. It defines the core data models for nodes, edges, observations, and schema migration version tracking.
25
+
26
+ ## Features
27
+
28
+ - **Direct Cloud Spanner Client (`SpannerClient`)**: Client for Spanner database operations, DDL execution, parameterized DML, and point-in-time snapshot queries.
29
+ - **SQLAlchemy ORM models**: Declarative models for nodes, edges, and observations.
30
+ - **Graph database implementation**: Built on top of Google Cloud Spanner.
31
+ - **JSON-LD document support**: Model support for data import/export.
32
+ - **Efficient indexing & querying**: Full-text and composite indexing for graph traversals.
33
+ - **Provenance tracking**: Complete auditability for all graph entities and relationships.
34
+
35
+ ## Data Model
36
+
37
+ ### NodeModel
38
+ - Primary key: `subject_id` (String)
39
+ - Properties:
40
+ - `name` (Text)
41
+ - `types` (Array of Strings)
42
+ - Relationships:
43
+ - `outgoing_edges`: One-to-many relationship with EdgeModel
44
+
45
+ ### EdgeModel
46
+ - Composite primary key: (`subject_id`, `predicate`, `object_id`, `object_hash`, `provenance`)
47
+ - Properties:
48
+ - `object_value` (Text)
49
+ - `object_value_tokenlist` (Text, full-text search)
50
+ - Relationships:
51
+ - `source_node`: Many-to-one relationship with NodeModel
52
+ - Indexes:
53
+ - `EdgeByObjectValue`: Index on `object_value` for efficient lookups
54
+
55
+ ### Observation Model
56
+ - Composite primary key: (`variable_measured`, `observation_about`, `import_name`)
57
+ - Properties:
58
+ - `observation_period` (String)
59
+ - `measurement_method` (String)
60
+ - `unit` (String)
61
+ - `scaling_factor` (String)
62
+ - `observations` (LargeBinary)
63
+ - `provenance_url` (String)
64
+
65
+ ## Usage
66
+
67
+ ### Cloud Spanner Client
68
+
69
+ The package provides `SpannerClient` for direct Spanner operations, DDL execution, and query execution.
70
+
71
+ #### Initialization
72
+
73
+ ```python
74
+ from datacommons_db.clients import SpannerClient
75
+
76
+ client = SpannerClient(
77
+ project_id="your-gcp-project",
78
+ instance_id="your-spanner-instance",
79
+ database_id="your-spanner-database",
80
+ )
81
+ ```
82
+
83
+
84
+ #### Checking Tables
85
+
86
+ ```python
87
+ # Check if a specific table exists in information_schema
88
+ if not client.table_exists("Node"):
89
+ print("Node table not found")
90
+ ```
91
+
92
+ #### Executing DDL Statements
93
+
94
+ `execute_ddl()` accepts a list of DDL statement strings and waits for Spanner Long-Running Operations (LROs) to complete, returning a `DdlResult`:
95
+
96
+ ```python
97
+ from datacommons_db.clients import ExecutionStatus
98
+
99
+ ddl_result = client.execute_ddl([
100
+ """
101
+ CREATE TABLE CustomTable (
102
+ id STRING(64) NOT NULL,
103
+ name STRING(MAX)
104
+ ) PRIMARY KEY (id)
105
+ """,
106
+ "CREATE TABLE TableB (id INT64) PRIMARY KEY (id)",
107
+ ])
108
+ if ddl_result.status != ExecutionStatus.SUCCESS:
109
+ print(f"DDL failed: {ddl_result.error_message}")
110
+ ```
111
+
112
+
113
+ #### Executing Queries & DML
114
+
115
+ ```python
116
+ from google.cloud import spanner
117
+ from datacommons_db.clients import ExecutionStatus
118
+
119
+ # Parameterized DML transaction (returns DmlResult)
120
+ dml_result = client.execute_dml(
121
+ "UPDATE CustomTable SET name = @name WHERE id = @id",
122
+ params={"name": "New Name", "id": "123"},
123
+ param_types={"name": spanner.param_types.STRING, "id": spanner.param_types.STRING},
124
+ )
125
+ if dml_result.status == ExecutionStatus.SUCCESS:
126
+ print(f"Rows affected: {dml_result.rows_affected}")
127
+ else:
128
+ print(f"DML failed: {dml_result.error_message}")
129
+
130
+ # Point-in-time Snapshot query (returns QueryResult)
131
+ query_result = client.execute_query(
132
+ "SELECT id, name FROM CustomTable WHERE id = @id",
133
+ params={"id": "123"},
134
+ param_types={"id": spanner.param_types.STRING},
135
+ )
136
+ if query_result.status == ExecutionStatus.SUCCESS:
137
+ print(f"Queried rows: {query_result.rows}")
138
+ ```
139
+
140
+
141
+
142
+ ### Schema Migrations
143
+
144
+ The package provides a timestamp-based schema migration framework with `SchemaMigration` and `MigrationRunner`.
145
+
146
+ #### Running Migrations
147
+
148
+ ```python
149
+ from datacommons_db.clients import SpannerClient
150
+ from datacommons_db.migrations import MigrationRunner
151
+
152
+ client = SpannerClient(
153
+ project_id="your-gcp-project",
154
+ instance_id="your-spanner-instance",
155
+ database_id="your-spanner-database",
156
+ )
157
+
158
+ runner = MigrationRunner(client)
159
+
160
+ # Check applied migrations in SchemaMigrations table
161
+ applied = runner.get_applied_migrations()
162
+ print(f"Applied migrations: {applied}")
163
+
164
+ # Run all pending migrations chronologically
165
+ applied_migrations = runner.run_migrations()
166
+ for migration in applied_migrations:
167
+ print(f"Applied: {migration.creation_timestamp} ({migration.description})")
168
+ ```
169
+
170
+ #### Defining a Custom Migration
171
+
172
+ Create a file in `datacommons_db/migrations/migration_scripts/` named `YYYYMMDDHHMMSS_<description>.py` (e.g. `20260920120000_add_custom_index.py`):
173
+
174
+ ```python
175
+ from datacommons_db.clients import ExecutionStatus, SpannerClient
176
+ from datacommons_db.migrations import SchemaMigration
177
+
178
+ class Migration(SchemaMigration):
179
+ description: str = "Add custom index"
180
+ creation_timestamp: str = "2026-09-20T12:00:00Z"
181
+
182
+ def roll_forward(self, spanner_client: SpannerClient) -> None:
183
+ result = spanner_client.execute_ddl([
184
+ "CREATE INDEX CustomIndex ON Node (name)"
185
+ ])
186
+ if result.status != ExecutionStatus.SUCCESS:
187
+ raise RuntimeError(f"Migration failed: {result.error_message}")
188
+ ```
189
+
190
+ ### SQLAlchemy ORM Usage
191
+
192
+ ```python
193
+ from sqlalchemy import create_engine
194
+ from sqlalchemy.orm import sessionmaker
195
+ from datacommons_db.models.node import NodeModel
196
+ from datacommons_db.models.edge import EdgeModel
197
+
198
+ # Initialize database connection
199
+ engine = create_engine('spanner:///projects/your-project/instances/your-instance/databases/your-database')
200
+
201
+ # Create a session
202
+ Session = sessionmaker(bind=engine)
203
+ session = Session()
204
+
205
+ # Example: Query nodes
206
+ nodes = session.query(NodeModel).filter(NodeModel.types.contains(['Person'])).limit(100).all()
207
+ ```
208
+
209
+ ## Namespaces
210
+
211
+ The module supports several predefined namespaces:
212
+ - `rdf`: http://www.w3.org/1999/02/22-rdf-syntax-ns#
213
+ - `rdfs`: http://www.w3.org/2000/01/rdf-schema#
214
+ - `xsd`: http://www.w3.org/2001/XMLSchema#
215
+ - `dc`: https://datacommons.org/
216
+ - `schema`: https://schema.org/
217
+
218
+ ## Performance Considerations
219
+
220
+ - Deferred loading of `object_value_tokenlist` to optimize memory usage
221
+ - Proper indexing on frequently queried fields
222
+ - Efficient relationship loading using SQLAlchemy's `joinedload`
223
+ - Support for pagination and filtering
224
+
225
+ ## Dependencies
226
+
227
+ - SQLAlchemy
228
+ - Google Cloud Spanner
229
+
230
+ ## Contributing
231
+
232
+ When contributing to this module:
233
+ 1. Ensure all database operations are properly indexed
234
+ 2. Maintain JSON-LD compatibility
235
+ 3. Add appropriate type hints
236
+ 4. Include docstrings for all public methods
237
+ 5. Add tests for new functionality
238
+
239
+ ## License
240
+
241
+ [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
@@ -0,0 +1,220 @@
1
+ # Data Commons Database Module
2
+
3
+ This module provides the database models and client primitives for the Data Commons project, implementing a graph database using Google Cloud Spanner and SQLAlchemy. It defines the core data models for nodes, edges, observations, and schema migration version tracking.
4
+
5
+ ## Features
6
+
7
+ - **Direct Cloud Spanner Client (`SpannerClient`)**: Client for Spanner database operations, DDL execution, parameterized DML, and point-in-time snapshot queries.
8
+ - **SQLAlchemy ORM models**: Declarative models for nodes, edges, and observations.
9
+ - **Graph database implementation**: Built on top of Google Cloud Spanner.
10
+ - **JSON-LD document support**: Model support for data import/export.
11
+ - **Efficient indexing & querying**: Full-text and composite indexing for graph traversals.
12
+ - **Provenance tracking**: Complete auditability for all graph entities and relationships.
13
+
14
+ ## Data Model
15
+
16
+ ### NodeModel
17
+ - Primary key: `subject_id` (String)
18
+ - Properties:
19
+ - `name` (Text)
20
+ - `types` (Array of Strings)
21
+ - Relationships:
22
+ - `outgoing_edges`: One-to-many relationship with EdgeModel
23
+
24
+ ### EdgeModel
25
+ - Composite primary key: (`subject_id`, `predicate`, `object_id`, `object_hash`, `provenance`)
26
+ - Properties:
27
+ - `object_value` (Text)
28
+ - `object_value_tokenlist` (Text, full-text search)
29
+ - Relationships:
30
+ - `source_node`: Many-to-one relationship with NodeModel
31
+ - Indexes:
32
+ - `EdgeByObjectValue`: Index on `object_value` for efficient lookups
33
+
34
+ ### Observation Model
35
+ - Composite primary key: (`variable_measured`, `observation_about`, `import_name`)
36
+ - Properties:
37
+ - `observation_period` (String)
38
+ - `measurement_method` (String)
39
+ - `unit` (String)
40
+ - `scaling_factor` (String)
41
+ - `observations` (LargeBinary)
42
+ - `provenance_url` (String)
43
+
44
+ ## Usage
45
+
46
+ ### Cloud Spanner Client
47
+
48
+ The package provides `SpannerClient` for direct Spanner operations, DDL execution, and query execution.
49
+
50
+ #### Initialization
51
+
52
+ ```python
53
+ from datacommons_db.clients import SpannerClient
54
+
55
+ client = SpannerClient(
56
+ project_id="your-gcp-project",
57
+ instance_id="your-spanner-instance",
58
+ database_id="your-spanner-database",
59
+ )
60
+ ```
61
+
62
+
63
+ #### Checking Tables
64
+
65
+ ```python
66
+ # Check if a specific table exists in information_schema
67
+ if not client.table_exists("Node"):
68
+ print("Node table not found")
69
+ ```
70
+
71
+ #### Executing DDL Statements
72
+
73
+ `execute_ddl()` accepts a list of DDL statement strings and waits for Spanner Long-Running Operations (LROs) to complete, returning a `DdlResult`:
74
+
75
+ ```python
76
+ from datacommons_db.clients import ExecutionStatus
77
+
78
+ ddl_result = client.execute_ddl([
79
+ """
80
+ CREATE TABLE CustomTable (
81
+ id STRING(64) NOT NULL,
82
+ name STRING(MAX)
83
+ ) PRIMARY KEY (id)
84
+ """,
85
+ "CREATE TABLE TableB (id INT64) PRIMARY KEY (id)",
86
+ ])
87
+ if ddl_result.status != ExecutionStatus.SUCCESS:
88
+ print(f"DDL failed: {ddl_result.error_message}")
89
+ ```
90
+
91
+
92
+ #### Executing Queries & DML
93
+
94
+ ```python
95
+ from google.cloud import spanner
96
+ from datacommons_db.clients import ExecutionStatus
97
+
98
+ # Parameterized DML transaction (returns DmlResult)
99
+ dml_result = client.execute_dml(
100
+ "UPDATE CustomTable SET name = @name WHERE id = @id",
101
+ params={"name": "New Name", "id": "123"},
102
+ param_types={"name": spanner.param_types.STRING, "id": spanner.param_types.STRING},
103
+ )
104
+ if dml_result.status == ExecutionStatus.SUCCESS:
105
+ print(f"Rows affected: {dml_result.rows_affected}")
106
+ else:
107
+ print(f"DML failed: {dml_result.error_message}")
108
+
109
+ # Point-in-time Snapshot query (returns QueryResult)
110
+ query_result = client.execute_query(
111
+ "SELECT id, name FROM CustomTable WHERE id = @id",
112
+ params={"id": "123"},
113
+ param_types={"id": spanner.param_types.STRING},
114
+ )
115
+ if query_result.status == ExecutionStatus.SUCCESS:
116
+ print(f"Queried rows: {query_result.rows}")
117
+ ```
118
+
119
+
120
+
121
+ ### Schema Migrations
122
+
123
+ The package provides a timestamp-based schema migration framework with `SchemaMigration` and `MigrationRunner`.
124
+
125
+ #### Running Migrations
126
+
127
+ ```python
128
+ from datacommons_db.clients import SpannerClient
129
+ from datacommons_db.migrations import MigrationRunner
130
+
131
+ client = SpannerClient(
132
+ project_id="your-gcp-project",
133
+ instance_id="your-spanner-instance",
134
+ database_id="your-spanner-database",
135
+ )
136
+
137
+ runner = MigrationRunner(client)
138
+
139
+ # Check applied migrations in SchemaMigrations table
140
+ applied = runner.get_applied_migrations()
141
+ print(f"Applied migrations: {applied}")
142
+
143
+ # Run all pending migrations chronologically
144
+ applied_migrations = runner.run_migrations()
145
+ for migration in applied_migrations:
146
+ print(f"Applied: {migration.creation_timestamp} ({migration.description})")
147
+ ```
148
+
149
+ #### Defining a Custom Migration
150
+
151
+ Create a file in `datacommons_db/migrations/migration_scripts/` named `YYYYMMDDHHMMSS_<description>.py` (e.g. `20260920120000_add_custom_index.py`):
152
+
153
+ ```python
154
+ from datacommons_db.clients import ExecutionStatus, SpannerClient
155
+ from datacommons_db.migrations import SchemaMigration
156
+
157
+ class Migration(SchemaMigration):
158
+ description: str = "Add custom index"
159
+ creation_timestamp: str = "2026-09-20T12:00:00Z"
160
+
161
+ def roll_forward(self, spanner_client: SpannerClient) -> None:
162
+ result = spanner_client.execute_ddl([
163
+ "CREATE INDEX CustomIndex ON Node (name)"
164
+ ])
165
+ if result.status != ExecutionStatus.SUCCESS:
166
+ raise RuntimeError(f"Migration failed: {result.error_message}")
167
+ ```
168
+
169
+ ### SQLAlchemy ORM Usage
170
+
171
+ ```python
172
+ from sqlalchemy import create_engine
173
+ from sqlalchemy.orm import sessionmaker
174
+ from datacommons_db.models.node import NodeModel
175
+ from datacommons_db.models.edge import EdgeModel
176
+
177
+ # Initialize database connection
178
+ engine = create_engine('spanner:///projects/your-project/instances/your-instance/databases/your-database')
179
+
180
+ # Create a session
181
+ Session = sessionmaker(bind=engine)
182
+ session = Session()
183
+
184
+ # Example: Query nodes
185
+ nodes = session.query(NodeModel).filter(NodeModel.types.contains(['Person'])).limit(100).all()
186
+ ```
187
+
188
+ ## Namespaces
189
+
190
+ The module supports several predefined namespaces:
191
+ - `rdf`: http://www.w3.org/1999/02/22-rdf-syntax-ns#
192
+ - `rdfs`: http://www.w3.org/2000/01/rdf-schema#
193
+ - `xsd`: http://www.w3.org/2001/XMLSchema#
194
+ - `dc`: https://datacommons.org/
195
+ - `schema`: https://schema.org/
196
+
197
+ ## Performance Considerations
198
+
199
+ - Deferred loading of `object_value_tokenlist` to optimize memory usage
200
+ - Proper indexing on frequently queried fields
201
+ - Efficient relationship loading using SQLAlchemy's `joinedload`
202
+ - Support for pagination and filtering
203
+
204
+ ## Dependencies
205
+
206
+ - SQLAlchemy
207
+ - Google Cloud Spanner
208
+
209
+ ## Contributing
210
+
211
+ When contributing to this module:
212
+ 1. Ensure all database operations are properly indexed
213
+ 2. Maintain JSON-LD compatibility
214
+ 3. Add appropriate type hints
215
+ 4. Include docstrings for all public methods
216
+ 5. Add tests for new functionality
217
+
218
+ ## License
219
+
220
+ [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)
@@ -0,0 +1 @@
1
+ 1.1.3
@@ -0,0 +1,17 @@
1
+ # Copyright 2025 Google LLC.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import importlib.metadata
16
+
17
+ __version__ = importlib.metadata.version("datacommons-db")
@@ -0,0 +1,29 @@
1
+ # Copyright 2026 Google LLC.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from datacommons_db.clients.spanner_client import (
16
+ DdlResult,
17
+ DmlResult,
18
+ ExecutionStatus,
19
+ QueryResult,
20
+ SpannerClient,
21
+ )
22
+
23
+ __all__ = [
24
+ "DdlResult",
25
+ "DmlResult",
26
+ "ExecutionStatus",
27
+ "QueryResult",
28
+ "SpannerClient",
29
+ ]