linkml-store 0.1.11__tar.gz → 0.1.13__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.
Potentially problematic release.
This version of linkml-store might be problematic. Click here for more details.
- {linkml_store-0.1.11 → linkml_store-0.1.13}/PKG-INFO +6 -2
- {linkml_store-0.1.11 → linkml_store-0.1.13}/pyproject.toml +6 -2
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/client.py +2 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/collection.py +58 -9
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/config.py +12 -1
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/database.py +34 -3
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/duckdb/duckdb_database.py +31 -3
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/mongodb/mongodb_database.py +31 -1
- linkml_store-0.1.13/src/linkml_store/api/stores/neo4j/neo4j_collection.py +429 -0
- linkml_store-0.1.13/src/linkml_store/api/stores/neo4j/neo4j_database.py +154 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/cli.py +29 -2
- linkml_store-0.1.13/src/linkml_store/graphs/graph_map.py +24 -0
- linkml_store-0.1.13/src/linkml_store/utils/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/format_utils.py +132 -14
- linkml_store-0.1.13/src/linkml_store/utils/mongodb_utils.py +145 -0
- linkml_store-0.1.13/src/linkml_store/utils/neo4j_utils.py +42 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/sql_utils.py +7 -2
- linkml_store-0.1.13/src/linkml_store/webapi/__init__.py +0 -0
- linkml_store-0.1.13/src/linkml_store/webapi/html/generic.html.j2 +43 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/main.py +346 -63
- linkml_store-0.1.11/src/linkml_store/webapi/html/generic.html.j2 +0 -46
- {linkml_store-0.1.11 → linkml_store-0.1.13}/LICENSE +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/README.md +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/queries.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/chromadb/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/chromadb/chromadb_collection.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/chromadb/chromadb_database.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/duckdb/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/duckdb/duckdb_collection.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/duckdb/mappings.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/filesystem/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/filesystem/filesystem_collection.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/filesystem/filesystem_database.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/hdf5/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/hdf5/hdf5_collection.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/hdf5/hdf5_database.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/mongodb/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/mongodb/mongodb_collection.py +0 -0
- {linkml_store-0.1.11/src/linkml_store/index/implementations → linkml_store-0.1.13/src/linkml_store/api/stores/neo4j}/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/solr/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/solr/solr_collection.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/solr/solr_database.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/solr/solr_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/types.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/constants.py +0 -0
- {linkml_store-0.1.11/src/linkml_store/utils → linkml_store-0.1.13/src/linkml_store/graphs}/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/index/__init__.py +0 -0
- {linkml_store-0.1.11/src/linkml_store/webapi → linkml_store-0.1.13/src/linkml_store/index/implementations}/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/index/implementations/llm_indexer.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/index/implementations/simple_indexer.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/index/indexer.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/change_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/file_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/io.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/object_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/pandas_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/patch_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/query_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/utils/schema_utils.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/html/__init__.py +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/html/base.html.j2 +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/html/collection_details.html.j2 +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/html/database_details.html.j2 +0 -0
- {linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/webapi/html/databases.html.j2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: linkml-store
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: linkml-store
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Author 1
|
|
@@ -21,6 +21,7 @@ Provides-Extra: h5py
|
|
|
21
21
|
Provides-Extra: llm
|
|
22
22
|
Provides-Extra: map
|
|
23
23
|
Provides-Extra: mongodb
|
|
24
|
+
Provides-Extra: neo4j
|
|
24
25
|
Provides-Extra: pyarrow
|
|
25
26
|
Provides-Extra: renderer
|
|
26
27
|
Provides-Extra: tests
|
|
@@ -28,7 +29,7 @@ Provides-Extra: validation
|
|
|
28
29
|
Requires-Dist: black (>=24.0.0) ; extra == "tests"
|
|
29
30
|
Requires-Dist: chromadb ; extra == "chromadb"
|
|
30
31
|
Requires-Dist: click
|
|
31
|
-
Requires-Dist: duckdb (>=0.10.1
|
|
32
|
+
Requires-Dist: duckdb (>=0.10.1)
|
|
32
33
|
Requires-Dist: duckdb-engine (>=0.11.2)
|
|
33
34
|
Requires-Dist: fastapi ; extra == "fastapi"
|
|
34
35
|
Requires-Dist: frictionless ; extra == "frictionless"
|
|
@@ -41,8 +42,11 @@ Requires-Dist: linkml_map ; extra == "map"
|
|
|
41
42
|
Requires-Dist: linkml_renderer ; extra == "renderer"
|
|
42
43
|
Requires-Dist: llm ; extra == "llm"
|
|
43
44
|
Requires-Dist: matplotlib ; extra == "analytics"
|
|
45
|
+
Requires-Dist: neo4j ; extra == "neo4j"
|
|
46
|
+
Requires-Dist: networkx ; extra == "neo4j"
|
|
44
47
|
Requires-Dist: pandas (>=2.2.1) ; extra == "analytics"
|
|
45
48
|
Requires-Dist: plotly ; extra == "analytics"
|
|
49
|
+
Requires-Dist: py2neo ; extra == "neo4j"
|
|
46
50
|
Requires-Dist: pyarrow ; extra == "pyarrow"
|
|
47
51
|
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
48
52
|
Requires-Dist: pymongo ; extra == "mongodb"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "linkml-store"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.13"
|
|
4
4
|
description = "linkml-store"
|
|
5
5
|
authors = ["Author 1 <author@org.org>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -13,7 +13,7 @@ pydantic = "^2.0.0"
|
|
|
13
13
|
linkml-runtime = ">=1.8.0"
|
|
14
14
|
streamlit = { version = "^1.32.2", optional = true }
|
|
15
15
|
sqlalchemy = "*"
|
|
16
|
-
duckdb = "
|
|
16
|
+
duckdb = ">=0.10.1"
|
|
17
17
|
duckdb-engine = ">=0.11.2"
|
|
18
18
|
matplotlib = { version = "*", optional = true }
|
|
19
19
|
seaborn = { version = "*", optional = true }
|
|
@@ -22,6 +22,9 @@ pystow = "^0.5.4"
|
|
|
22
22
|
black = { version=">=24.0.0", optional = true }
|
|
23
23
|
llm = { version="*", optional = true }
|
|
24
24
|
pymongo = { version="*", optional = true }
|
|
25
|
+
neo4j = { version="*", optional = true }
|
|
26
|
+
py2neo = { version="*", optional = true }
|
|
27
|
+
networkx = { version="*", optional = true }
|
|
25
28
|
chromadb = { version="*", optional = true }
|
|
26
29
|
pyarrow = { version="*", optional = true }
|
|
27
30
|
h5py = { version="*", optional = true }
|
|
@@ -66,6 +69,7 @@ app = ["streamlit"]
|
|
|
66
69
|
tests = ["black"]
|
|
67
70
|
llm = ["llm"]
|
|
68
71
|
mongodb = ["pymongo"]
|
|
72
|
+
neo4j = ["neo4j", "py2neo", "networkx"]
|
|
69
73
|
chromadb = ["chromadb"]
|
|
70
74
|
h5py = ["h5py"]
|
|
71
75
|
pyarrow = ["pyarrow"]
|
|
@@ -11,6 +11,7 @@ from linkml_store.api.stores.chromadb.chromadb_database import ChromaDBDatabase
|
|
|
11
11
|
from linkml_store.api.stores.duckdb.duckdb_database import DuckDBDatabase
|
|
12
12
|
from linkml_store.api.stores.filesystem.filesystem_database import FileSystemDatabase
|
|
13
13
|
from linkml_store.api.stores.mongodb.mongodb_database import MongoDBDatabase
|
|
14
|
+
from linkml_store.api.stores.neo4j.neo4j_database import Neo4jDatabase
|
|
14
15
|
from linkml_store.api.stores.solr.solr_database import SolrDatabase
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
@@ -21,6 +22,7 @@ HANDLE_MAP = {
|
|
|
21
22
|
"solr": SolrDatabase,
|
|
22
23
|
"mongodb": MongoDBDatabase,
|
|
23
24
|
"chromadb": ChromaDBDatabase,
|
|
25
|
+
"neo4j": Neo4jDatabase,
|
|
24
26
|
"file": FileSystemDatabase,
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -4,7 +4,21 @@ import hashlib
|
|
|
4
4
|
import logging
|
|
5
5
|
from collections import defaultdict
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
ClassVar,
|
|
11
|
+
Dict,
|
|
12
|
+
Generic,
|
|
13
|
+
Iterable,
|
|
14
|
+
Iterator,
|
|
15
|
+
List,
|
|
16
|
+
Optional,
|
|
17
|
+
TextIO,
|
|
18
|
+
Tuple,
|
|
19
|
+
Type,
|
|
20
|
+
Union,
|
|
21
|
+
)
|
|
8
22
|
|
|
9
23
|
import numpy as np
|
|
10
24
|
from linkml_runtime import SchemaView
|
|
@@ -202,6 +216,12 @@ class Collection(Generic[DatabaseType]):
|
|
|
202
216
|
self._materialize_derivations()
|
|
203
217
|
self._initialized = True
|
|
204
218
|
|
|
219
|
+
def _pre_insert_hook(self, objs: List[OBJECT], **kwargs):
|
|
220
|
+
if self.metadata.validate_modifications:
|
|
221
|
+
errors = list(self.iter_validate_collection(objs))
|
|
222
|
+
if errors:
|
|
223
|
+
raise ValueError(f"Validation errors: {errors}")
|
|
224
|
+
|
|
205
225
|
def _post_insert_hook(self, objs: List[OBJECT], **kwargs):
|
|
206
226
|
self._initialized = True
|
|
207
227
|
patches = [{"op": "add", "path": "/0", "value": obj} for obj in objs]
|
|
@@ -346,7 +366,10 @@ class Collection(Generic[DatabaseType]):
|
|
|
346
366
|
id_field = self.identifier_attribute_name
|
|
347
367
|
if not id_field:
|
|
348
368
|
raise ValueError(f"No identifier for {self.name}")
|
|
349
|
-
|
|
369
|
+
if len(ids) == 1:
|
|
370
|
+
return self.find({id_field: ids[0]})
|
|
371
|
+
else:
|
|
372
|
+
return self.find({id_field: {"$in": ids}})
|
|
350
373
|
|
|
351
374
|
def get_one(self, id: IDENTIFIER, **kwargs) -> Optional[OBJECT]:
|
|
352
375
|
"""
|
|
@@ -518,7 +541,7 @@ class Collection(Generic[DatabaseType]):
|
|
|
518
541
|
:return:
|
|
519
542
|
"""
|
|
520
543
|
cd = self.class_definition()
|
|
521
|
-
return cd is not None
|
|
544
|
+
return cd is not None and cd.attributes
|
|
522
545
|
|
|
523
546
|
def load_from_source(self, load_if_exists=False):
|
|
524
547
|
"""
|
|
@@ -535,11 +558,19 @@ class Collection(Generic[DatabaseType]):
|
|
|
535
558
|
kwargs = source.arguments or {}
|
|
536
559
|
if source.local_path:
|
|
537
560
|
objects = load_objects(
|
|
538
|
-
metadata.source.local_path,
|
|
561
|
+
metadata.source.local_path,
|
|
562
|
+
format=source.format,
|
|
563
|
+
expected_type=source.expected_type,
|
|
564
|
+
compression=source.compression,
|
|
565
|
+
**kwargs,
|
|
539
566
|
)
|
|
540
567
|
elif metadata.source.url:
|
|
541
568
|
objects = load_objects_from_url(
|
|
542
|
-
metadata.source.url,
|
|
569
|
+
metadata.source.url,
|
|
570
|
+
format=source.format,
|
|
571
|
+
expected_type=source.expected_type,
|
|
572
|
+
compression=source.compression,
|
|
573
|
+
**kwargs,
|
|
543
574
|
)
|
|
544
575
|
self.insert(objects)
|
|
545
576
|
|
|
@@ -746,6 +777,7 @@ class Collection(Generic[DatabaseType]):
|
|
|
746
777
|
sv: SchemaView = self.parent.schema_view
|
|
747
778
|
if sv:
|
|
748
779
|
cls = sv.get_class(self.target_class_name)
|
|
780
|
+
# cls = sv.schema.classes[self.target_class_name]
|
|
749
781
|
if cls and not cls.attributes:
|
|
750
782
|
if not sv.class_induced_slots(cls.name):
|
|
751
783
|
for att in self._induce_attributes():
|
|
@@ -868,7 +900,7 @@ class Collection(Generic[DatabaseType]):
|
|
|
868
900
|
exact_dimensions_list.append(v.shape)
|
|
869
901
|
break
|
|
870
902
|
if isinstance(v, list):
|
|
871
|
-
v = v[0]
|
|
903
|
+
v = v[0] if v else None
|
|
872
904
|
multivalueds.append(True)
|
|
873
905
|
elif isinstance(v, dict):
|
|
874
906
|
v = list(v.values())[0]
|
|
@@ -966,11 +998,14 @@ class Collection(Generic[DatabaseType]):
|
|
|
966
998
|
patches_from_objects_lists(src_objs, tgt_objs, primary_key=primary_key)
|
|
967
999
|
return patches_from_objects_lists(src_objs, tgt_objs, primary_key=primary_key)
|
|
968
1000
|
|
|
969
|
-
def iter_validate_collection(
|
|
1001
|
+
def iter_validate_collection(
|
|
1002
|
+
self, objects: Optional[Iterable[OBJECT]] = None, **kwargs
|
|
1003
|
+
) -> Iterator["ValidationResult"]:
|
|
970
1004
|
"""
|
|
971
1005
|
Validate the contents of the collection
|
|
972
1006
|
|
|
973
1007
|
:param kwargs:
|
|
1008
|
+
:param objects: objects to validate
|
|
974
1009
|
:return: iterator over validation results
|
|
975
1010
|
"""
|
|
976
1011
|
from linkml.validator import JsonschemaValidationPlugin, Validator
|
|
@@ -980,10 +1015,24 @@ class Collection(Generic[DatabaseType]):
|
|
|
980
1015
|
cd = self.class_definition()
|
|
981
1016
|
if not cd:
|
|
982
1017
|
raise ValueError(f"Cannot find class definition for {self.target_class_name}")
|
|
1018
|
+
type_designator = None
|
|
1019
|
+
for att in self.parent.schema_view.class_induced_slots(cd.name):
|
|
1020
|
+
if att.designates_type:
|
|
1021
|
+
type_designator = att.name
|
|
983
1022
|
class_name = cd.name
|
|
984
|
-
|
|
1023
|
+
if objects is None:
|
|
1024
|
+
objects = self.find_iter(**kwargs)
|
|
1025
|
+
for obj in objects:
|
|
985
1026
|
obj = clean_empties(obj)
|
|
986
|
-
|
|
1027
|
+
v_class_name = class_name
|
|
1028
|
+
if type_designator is not None:
|
|
1029
|
+
# TODO: move type designator logic to core linkml
|
|
1030
|
+
this_class_name = obj.get(type_designator)
|
|
1031
|
+
if this_class_name:
|
|
1032
|
+
if ":" in this_class_name:
|
|
1033
|
+
this_class_name = this_class_name.split(":")[-1]
|
|
1034
|
+
v_class_name = this_class_name
|
|
1035
|
+
yield from validator.iter_results(obj, v_class_name)
|
|
987
1036
|
|
|
988
1037
|
def commit(self):
|
|
989
1038
|
"""
|
|
@@ -2,6 +2,8 @@ from typing import Any, Dict, List, Optional
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
+
from linkml_store.graphs.graph_map import GraphProjection
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class ConfiguredBaseModel(BaseModel, extra="forbid"):
|
|
7
9
|
"""
|
|
@@ -33,6 +35,7 @@ class CollectionSource(ConfiguredBaseModel):
|
|
|
33
35
|
refresh_interval_days: Optional[float] = None
|
|
34
36
|
expected_type: Optional[str] = None
|
|
35
37
|
format: Optional[str] = None
|
|
38
|
+
compression: Optional[str] = None
|
|
36
39
|
arguments: Optional[Dict[str, Any]] = None
|
|
37
40
|
|
|
38
41
|
|
|
@@ -73,11 +76,19 @@ class CollectionConfig(ConfiguredBaseModel):
|
|
|
73
76
|
default=None,
|
|
74
77
|
description="Metadata about the source",
|
|
75
78
|
)
|
|
76
|
-
# TODO: derived_from
|
|
77
79
|
derived_from: Optional[List[DerivationConfiguration]] = Field(
|
|
78
80
|
default=None,
|
|
79
81
|
description="LinkML-Map derivations",
|
|
80
82
|
)
|
|
83
|
+
page_size: Optional[int] = Field(default=None, description="Suggested page size (items per page) in apps and APIs")
|
|
84
|
+
graph_projection: Optional[GraphProjection] = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
description="Optional graph projection configuration",
|
|
87
|
+
)
|
|
88
|
+
validate_modifications: Optional[bool] = Field(
|
|
89
|
+
default=False,
|
|
90
|
+
description="Whether to validate inserts, updates, and deletes",
|
|
91
|
+
)
|
|
81
92
|
|
|
82
93
|
|
|
83
94
|
class DatabaseConfig(ConfiguredBaseModel):
|
|
@@ -19,7 +19,7 @@ from typing import (
|
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
from linkml_store.api.types import CollectionType
|
|
22
|
-
from linkml_store.utils.format_utils import load_objects, render_output
|
|
22
|
+
from linkml_store.utils.format_utils import Format, load_objects, render_output
|
|
23
23
|
from linkml_store.utils.patch_utils import PatchDict
|
|
24
24
|
|
|
25
25
|
try:
|
|
@@ -505,8 +505,10 @@ class Database(ABC, Generic[CollectionType]):
|
|
|
505
505
|
if isinstance(schema_view, str):
|
|
506
506
|
schema_view = SchemaView(schema_view)
|
|
507
507
|
self._schema_view = schema_view
|
|
508
|
+
# self._schema_view = SchemaView(schema_view.materialize_derived_schema())
|
|
508
509
|
if not self._collections:
|
|
509
510
|
return
|
|
511
|
+
|
|
510
512
|
# align with induced schema
|
|
511
513
|
roots = [c for c in schema_view.all_classes().values() if c.tree_root]
|
|
512
514
|
if len(roots) == 0:
|
|
@@ -705,7 +707,7 @@ class Database(ABC, Generic[CollectionType]):
|
|
|
705
707
|
"""
|
|
706
708
|
raise NotImplementedError()
|
|
707
709
|
|
|
708
|
-
def import_database(self, location: str, source_format: Optional[str] = None, **kwargs):
|
|
710
|
+
def import_database(self, location: str, source_format: Optional[Union[str, Format]] = None, **kwargs):
|
|
709
711
|
"""
|
|
710
712
|
Import a database from a file or location.
|
|
711
713
|
|
|
@@ -713,11 +715,27 @@ class Database(ABC, Generic[CollectionType]):
|
|
|
713
715
|
:param source_format: source format
|
|
714
716
|
:param kwargs: additional arguments
|
|
715
717
|
"""
|
|
718
|
+
if isinstance(source_format, str):
|
|
719
|
+
source_format = Format(source_format)
|
|
720
|
+
if isinstance(source_format, Format):
|
|
721
|
+
if source_format.is_dump_format() and source_format in [Format.SQLDUMP_DUCKDB, Format.DUMP_MONGODB]:
|
|
722
|
+
# import into a test instance
|
|
723
|
+
tmp_handle = source_format.value
|
|
724
|
+
client = self.parent
|
|
725
|
+
tmp_db = client.attach_database(tmp_handle, alias="tmp")
|
|
726
|
+
# TODO: check for infinite recursion
|
|
727
|
+
tmp_db.import_database(location, source_format=source_format)
|
|
728
|
+
obj = {}
|
|
729
|
+
for coll in tmp_db.list_collections():
|
|
730
|
+
qr = coll.find({}, limit=-1)
|
|
731
|
+
obj[coll.alias] = qr.rows
|
|
732
|
+
self.store(obj)
|
|
733
|
+
return
|
|
716
734
|
objects = load_objects(location, format=source_format)
|
|
717
735
|
for obj in objects:
|
|
718
736
|
self.store(obj)
|
|
719
737
|
|
|
720
|
-
def export_database(self, location: str, target_format: Optional[str] = None, **kwargs):
|
|
738
|
+
def export_database(self, location: str, target_format: Optional[Union[str, Format]] = None, **kwargs):
|
|
721
739
|
"""
|
|
722
740
|
Export a database to a file or location.
|
|
723
741
|
|
|
@@ -726,10 +744,23 @@ class Database(ABC, Generic[CollectionType]):
|
|
|
726
744
|
:param kwargs: additional arguments
|
|
727
745
|
"""
|
|
728
746
|
obj = {}
|
|
747
|
+
if isinstance(target_format, str):
|
|
748
|
+
target_format = Format(target_format)
|
|
729
749
|
for coll in self.list_collections():
|
|
730
750
|
qr = coll.find({}, limit=-1)
|
|
731
751
|
obj[coll.alias] = qr.rows
|
|
732
752
|
logger.info(f"Exporting object with {len(obj)} collections to {location} in {target_format} format")
|
|
753
|
+
if isinstance(target_format, Format):
|
|
754
|
+
if target_format.is_dump_format() and target_format in [Format.SQLDUMP_DUCKDB, Format.DUMP_MONGODB]:
|
|
755
|
+
tmp_handle = target_format.value
|
|
756
|
+
client = self.parent
|
|
757
|
+
tmp_db = client.attach_database(tmp_handle, alias="tmp")
|
|
758
|
+
tmp_db.store(obj)
|
|
759
|
+
# TODO: check for infinite recursion
|
|
760
|
+
tmp_db.export_database(location, target_format=target_format)
|
|
761
|
+
return
|
|
762
|
+
if Path(location).is_dir():
|
|
763
|
+
raise ValueError(f"{location} is a directory; cannot write {target_format} to a dir")
|
|
733
764
|
with open(location, "w", encoding="utf-8") as stream:
|
|
734
765
|
stream.write(render_output(obj, format=target_format))
|
|
735
766
|
|
{linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/duckdb/duckdb_database.py
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
4
|
+
from typing import Optional, Union
|
|
5
5
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import sqlalchemy
|
|
8
|
-
from duckdb import DuckDBPyConnection
|
|
9
8
|
from linkml_runtime import SchemaView
|
|
10
9
|
from linkml_runtime.linkml_model import ClassDefinition, SlotDefinition
|
|
11
10
|
from linkml_runtime.utils.schema_builder import SchemaBuilder
|
|
@@ -14,6 +13,7 @@ from sqlalchemy import NullPool, text
|
|
|
14
13
|
from linkml_store.api import Database
|
|
15
14
|
from linkml_store.api.queries import Query, QueryResult
|
|
16
15
|
from linkml_store.api.stores.duckdb.duckdb_collection import DuckDBCollection
|
|
16
|
+
from linkml_store.utils.format_utils import Format
|
|
17
17
|
from linkml_store.utils.sql_utils import introspect_schema, query_to_sql
|
|
18
18
|
|
|
19
19
|
TYPE_MAP = {
|
|
@@ -45,7 +45,7 @@ class DuckDBDatabase(Database):
|
|
|
45
45
|
types are used for nested inlined objects.
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
|
-
_connection: DuckDBPyConnection = None
|
|
48
|
+
# _connection: DuckDBPyConnection = None
|
|
49
49
|
_engine: sqlalchemy.Engine = None
|
|
50
50
|
collection_class = DuckDBCollection
|
|
51
51
|
|
|
@@ -202,3 +202,31 @@ class DuckDBDatabase(Database):
|
|
|
202
202
|
cls = ClassDefinition(name=collection_metadata.type, attributes=collection_metadata.attributes)
|
|
203
203
|
schema.classes[cls.name] = cls
|
|
204
204
|
return SchemaView(schema)
|
|
205
|
+
|
|
206
|
+
def export_database(self, location: str, target_format: Optional[Union[str, Format]] = None, **kwargs):
|
|
207
|
+
if target_format == "duckdb" or target_format == Format.SQLDUMP_DUCKDB:
|
|
208
|
+
path = Path(location)
|
|
209
|
+
if path.exists():
|
|
210
|
+
if path.is_file():
|
|
211
|
+
path.unlink()
|
|
212
|
+
with self.engine.connect() as conn:
|
|
213
|
+
sql = text(f"EXPORT DATABASE '{location}'")
|
|
214
|
+
conn.execute(sql)
|
|
215
|
+
else:
|
|
216
|
+
super().export_database(location, target_format=target_format, **kwargs)
|
|
217
|
+
|
|
218
|
+
def import_database(self, location: str, source_format: Optional[str] = None, **kwargs):
|
|
219
|
+
"""
|
|
220
|
+
Import a database from a file or location.
|
|
221
|
+
|
|
222
|
+
:param location: location of the file
|
|
223
|
+
:param source_format: source format
|
|
224
|
+
:param kwargs: additional arguments
|
|
225
|
+
"""
|
|
226
|
+
if source_format == Format.SQLDUMP_DUCKDB.value or source_format == Format.SQLDUMP_DUCKDB:
|
|
227
|
+
with self.engine.connect() as conn:
|
|
228
|
+
sql = text(f"IMPORT DATABASE '{location}'")
|
|
229
|
+
conn.execute(sql)
|
|
230
|
+
conn.commit()
|
|
231
|
+
else:
|
|
232
|
+
super().import_database(location, source_format=source_format, **kwargs)
|
{linkml_store-0.1.11 → linkml_store-0.1.13}/src/linkml_store/api/stores/mongodb/mongodb_database.py
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# mongodb_database.py
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Union
|
|
5
6
|
|
|
6
7
|
from pymongo import MongoClient
|
|
7
8
|
from pymongo.database import Database as NativeDatabase
|
|
@@ -9,6 +10,9 @@ from pymongo.database import Database as NativeDatabase
|
|
|
9
10
|
from linkml_store.api import Database
|
|
10
11
|
from linkml_store.api.queries import Query, QueryResult
|
|
11
12
|
from linkml_store.api.stores.mongodb.mongodb_collection import MongoDBCollection
|
|
13
|
+
from linkml_store.utils.file_utils import safe_remove_directory
|
|
14
|
+
from linkml_store.utils.format_utils import Format
|
|
15
|
+
from linkml_store.utils.mongodb_utils import import_mongodb
|
|
12
16
|
|
|
13
17
|
logger = logging.getLogger(__name__)
|
|
14
18
|
|
|
@@ -27,6 +31,8 @@ class MongoDBDatabase(Database):
|
|
|
27
31
|
def __init__(self, handle: Optional[str] = None, **kwargs):
|
|
28
32
|
if handle is None:
|
|
29
33
|
handle = "mongodb://localhost:27017/test"
|
|
34
|
+
if handle == "mongodb":
|
|
35
|
+
handle = "mongodb://localhost:27017/temporary"
|
|
30
36
|
super().__init__(handle=handle, **kwargs)
|
|
31
37
|
|
|
32
38
|
@property
|
|
@@ -77,3 +83,27 @@ class MongoDBDatabase(Database):
|
|
|
77
83
|
if collection_name not in self._collections:
|
|
78
84
|
collection = MongoDBCollection(name=collection_name, parent=self)
|
|
79
85
|
self._collections[collection_name] = collection
|
|
86
|
+
|
|
87
|
+
def export_database(self, location: str, target_format: Optional[Union[str, Format]] = None, **kwargs):
|
|
88
|
+
if target_format == Format.DUMP_MONGODB.value or target_format == Format.DUMP_MONGODB:
|
|
89
|
+
path = Path(location)
|
|
90
|
+
if path.exists():
|
|
91
|
+
safe_remove_directory(path, no_backup=True)
|
|
92
|
+
from linkml_store.utils.mongodb_utils import export_mongodb
|
|
93
|
+
|
|
94
|
+
export_mongodb(self.handle, location)
|
|
95
|
+
else:
|
|
96
|
+
super().export_database(location, target_format=target_format, **kwargs)
|
|
97
|
+
|
|
98
|
+
def import_database(self, location: str, source_format: Optional[str] = None, **kwargs):
|
|
99
|
+
"""
|
|
100
|
+
Import a database from a file or location.
|
|
101
|
+
|
|
102
|
+
:param location: location of the file
|
|
103
|
+
:param source_format: source format
|
|
104
|
+
:param kwargs: additional arguments
|
|
105
|
+
"""
|
|
106
|
+
if source_format == Format.DUMP_MONGODB.value or source_format == Format.DUMP_MONGODB:
|
|
107
|
+
import_mongodb(self.handle, location, drop=True)
|
|
108
|
+
else:
|
|
109
|
+
super().import_database(location, source_format=source_format, **kwargs)
|