pyneoinstance 4.0.1__tar.gz → 4.0.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.
- {pyneoinstance-4.0.1/pyneoinstance.egg-info → pyneoinstance-4.0.3}/PKG-INFO +4 -3
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/README.md +3 -2
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/__init__.py +1 -1
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/neo4jdbms.py +30 -9
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3/pyneoinstance.egg-info}/PKG-INFO +4 -3
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/SOURCES.txt +0 -2
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyproject.toml +1 -1
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_neo4jdb.py +44 -0
- pyneoinstance-4.0.1/test/__init__.py +0 -3
- pyneoinstance-4.0.1/test/context.py +0 -8
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/LICENSE +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/MANIFEST.in +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/__init__.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/database/context.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/__init__.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/helpers.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/fileload/loadyaml.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/py.typed +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/util/__init__.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance/util/functions.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/dependency_links.txt +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/requires.txt +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/pyneoinstance.egg-info/top_level.txt +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/setup.cfg +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/setup.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/age_data.csv +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/cypher_queries.yaml +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/load_files.yaml +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/movies.csv +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/people.csv +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/roles.csv +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_fileload.py +0 -0
- {pyneoinstance-4.0.1 → pyneoinstance-4.0.3}/test/test_util_functions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyneoinstance
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.3
|
|
4
4
|
Summary: Handle Neo4j read and write transactions and provide methods for performing graph EDA
|
|
5
5
|
Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -795,7 +795,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
|
|
|
795
795
|
```python
|
|
796
796
|
from pyneoinstance import load_yaml_file
|
|
797
797
|
|
|
798
|
-
config = load_yaml_file(configuration.yaml)
|
|
798
|
+
config = load_yaml_file('configuration.yaml')
|
|
799
799
|
db_info = config['db_info']
|
|
800
800
|
graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
|
|
801
801
|
```
|
|
@@ -1038,5 +1038,6 @@ Open `schema.html` in your browser to explore the schema interactively.
|
|
|
1038
1038
|
## More detailed examples
|
|
1039
1039
|
|
|
1040
1040
|
For more detailed examples please refer to the following articles:
|
|
1041
|
-
* Data
|
|
1041
|
+
* Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
|
|
1042
1042
|
* Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
|
|
1043
|
+
* ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
|
|
@@ -93,7 +93,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
|
|
|
93
93
|
```python
|
|
94
94
|
from pyneoinstance import load_yaml_file
|
|
95
95
|
|
|
96
|
-
config = load_yaml_file(configuration.yaml)
|
|
96
|
+
config = load_yaml_file('configuration.yaml')
|
|
97
97
|
db_info = config['db_info']
|
|
98
98
|
graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
|
|
99
99
|
```
|
|
@@ -336,5 +336,6 @@ Open `schema.html` in your browser to explore the schema interactively.
|
|
|
336
336
|
## More detailed examples
|
|
337
337
|
|
|
338
338
|
For more detailed examples please refer to the following articles:
|
|
339
|
-
* Data
|
|
339
|
+
* Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
|
|
340
340
|
* Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
|
|
341
|
+
* ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
|
|
@@ -28,6 +28,11 @@ from .context import get_logger, get_batches, get_columns_diff
|
|
|
28
28
|
|
|
29
29
|
_NODE_LABEL_RE = re.compile(r"\(:?(\w*)\)")
|
|
30
30
|
_REL_TYPE_RE = re.compile(r"\[:?(\w*)\]")
|
|
31
|
+
_IMPLICIT_TX_RE = re.compile(
|
|
32
|
+
r"\bIN\s+(?:\d+\s+)?CONCURRENT\s+TRANSACTIONS\b"
|
|
33
|
+
r"|\bIN\s+TRANSACTIONS\b",
|
|
34
|
+
re.IGNORECASE,
|
|
35
|
+
)
|
|
31
36
|
|
|
32
37
|
def _get_driver(neo_info : Dict[str, str]):
|
|
33
38
|
try:
|
|
@@ -196,8 +201,14 @@ class Neo4jInstance:
|
|
|
196
201
|
params = parameters or {}
|
|
197
202
|
with _get_session(self._driver, database) as session:
|
|
198
203
|
try:
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
if _IMPLICIT_TX_RE.search(query):
|
|
205
|
+
# 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
|
|
206
|
+
# (auto-commit) transaction, so it can't go through execute_read.
|
|
207
|
+
read_result = session.run(query, **params)
|
|
208
|
+
result = DataFrame(read_result.values(), columns=read_result.keys())
|
|
209
|
+
else:
|
|
210
|
+
result = session.execute_read(
|
|
211
|
+
_read_transaction_function,query=query,**params)
|
|
201
212
|
except ServiceUnavailable as exception:
|
|
202
213
|
raise ServiceUnavailable() from exception
|
|
203
214
|
except ClientError as exception:
|
|
@@ -242,8 +253,15 @@ class Neo4jInstance:
|
|
|
242
253
|
params = parameters or {}
|
|
243
254
|
with _get_session(self._driver, database) as session:
|
|
244
255
|
try:
|
|
245
|
-
|
|
246
|
-
|
|
256
|
+
if _IMPLICIT_TX_RE.search(query):
|
|
257
|
+
# 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
|
|
258
|
+
# (auto-commit) transaction, so it can't go through execute_read.
|
|
259
|
+
graph_result = session.run(query, **params)
|
|
260
|
+
list(graph_result) # consume all records so .graph() is populated
|
|
261
|
+
graph = graph_result.graph()
|
|
262
|
+
else:
|
|
263
|
+
graph = session.execute_read(
|
|
264
|
+
_graph_transaction_function, query=query, **params)
|
|
247
265
|
except ServiceUnavailable as exception:
|
|
248
266
|
raise ServiceUnavailable() from exception
|
|
249
267
|
except ClientError as exception:
|
|
@@ -750,15 +768,18 @@ class Neo4jInstance:
|
|
|
750
768
|
parameters: Optional[Dict[str, Any]] = None
|
|
751
769
|
):
|
|
752
770
|
params = parameters or {}
|
|
771
|
+
kwargs = dict(params)
|
|
772
|
+
if rows is not None:
|
|
773
|
+
kwargs['rows'] = rows
|
|
753
774
|
try:
|
|
754
|
-
if
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
775
|
+
if _IMPLICIT_TX_RE.search(query):
|
|
776
|
+
# 'CALL { ... } IN TRANSACTIONS' is only valid in an implicit
|
|
777
|
+
# (auto-commit) transaction, so it can't go through execute_write.
|
|
778
|
+
results = session.run(query, **kwargs).consume().counters.__dict__
|
|
758
779
|
else:
|
|
759
780
|
results = session.execute_write(
|
|
760
781
|
_write_transaction_function, query,
|
|
761
|
-
**
|
|
782
|
+
**kwargs).counters.__dict__
|
|
762
783
|
except ServiceUnavailable as exception:
|
|
763
784
|
raise ServiceUnavailable() from exception
|
|
764
785
|
except ClientError as exception:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyneoinstance
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.3
|
|
4
4
|
Summary: Handle Neo4j read and write transactions and provide methods for performing graph EDA
|
|
5
5
|
Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -795,7 +795,7 @@ This YAML file can then be loaded in Python and this information can be retrieve
|
|
|
795
795
|
```python
|
|
796
796
|
from pyneoinstance import load_yaml_file
|
|
797
797
|
|
|
798
|
-
config = load_yaml_file(configuration.yaml)
|
|
798
|
+
config = load_yaml_file('configuration.yaml')
|
|
799
799
|
db_info = config['db_info']
|
|
800
800
|
graph = Neo4jInstance(db_info['uri'], db_info['user'], db_info['password'])
|
|
801
801
|
```
|
|
@@ -1038,5 +1038,6 @@ Open `schema.html` in your browser to explore the schema interactively.
|
|
|
1038
1038
|
## More detailed examples
|
|
1039
1039
|
|
|
1040
1040
|
For more detailed examples please refer to the following articles:
|
|
1041
|
-
* Data
|
|
1041
|
+
* Data Ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
|
|
1042
1042
|
* Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
|
|
1043
|
+
* ETL Best Practices: [Python & Neo4j: ETL Best Practices](https://medium.com/@jose.alvarado-guzman/python-neo4j-etl-best-practices-83265ee201d6)
|
|
@@ -18,9 +18,7 @@ pyneoinstance/fileload/helpers.py
|
|
|
18
18
|
pyneoinstance/fileload/loadyaml.py
|
|
19
19
|
pyneoinstance/util/__init__.py
|
|
20
20
|
pyneoinstance/util/functions.py
|
|
21
|
-
test/__init__.py
|
|
22
21
|
test/age_data.csv
|
|
23
|
-
test/context.py
|
|
24
22
|
test/cypher_queries.yaml
|
|
25
23
|
test/load_files.yaml
|
|
26
24
|
test/movies.csv
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pyneoinstance"
|
|
9
|
-
version = "4.0.
|
|
9
|
+
version = "4.0.3"
|
|
10
10
|
description = "Handle Neo4j read and write transactions and provide methods for performing graph EDA"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{ name = "Jose A. Alvarado-Guzman",email = "jose.alvarado-guzman@neo4j.com" }]
|
|
@@ -97,6 +97,34 @@ class TestWriteMethods(unittest.TestCase):
|
|
|
97
97
|
self.assertEqual(
|
|
98
98
|
{'nodes_created': 1, 'labels_added': 2, 'properties_set': 4}, result)
|
|
99
99
|
|
|
100
|
+
def test_execute_write_query_in_transactions(self):
|
|
101
|
+
"""Test a query with 'CALL { ... } IN TRANSACTIONS', which requires
|
|
102
|
+
an implicit transaction rather than the managed transaction used by
|
|
103
|
+
execute_write_query for regular queries."""
|
|
104
|
+
query = """
|
|
105
|
+
UNWIND range(1, 3) AS i
|
|
106
|
+
CALL (i) {
|
|
107
|
+
CREATE (:Actor {id: i})
|
|
108
|
+
} IN TRANSACTIONS OF 1 ROWS
|
|
109
|
+
"""
|
|
110
|
+
result = self.graph.execute_write_query(query, self.db)
|
|
111
|
+
self.assertEqual(
|
|
112
|
+
{'nodes_created': 3, 'labels_added': 3, 'properties_set': 3}, result)
|
|
113
|
+
|
|
114
|
+
def test_execute_write_query_in_concurrent_transactions(self):
|
|
115
|
+
"""Test a query with 'CALL { ... } IN CONCURRENT TRANSACTIONS', which
|
|
116
|
+
also requires an implicit transaction rather than the managed
|
|
117
|
+
transaction used by execute_write_query for regular queries."""
|
|
118
|
+
query = """
|
|
119
|
+
UNWIND range(1, 3) AS i
|
|
120
|
+
CALL (i) {
|
|
121
|
+
CREATE (:Actor {id: i})
|
|
122
|
+
} IN 2 CONCURRENT TRANSACTIONS OF 1 ROWS
|
|
123
|
+
"""
|
|
124
|
+
result = self.graph.execute_write_query(query, self.db)
|
|
125
|
+
self.assertEqual(
|
|
126
|
+
{'nodes_created': 3, 'labels_added': 3, 'properties_set': 3}, result)
|
|
127
|
+
|
|
100
128
|
|
|
101
129
|
class TestReadAndEdaMethods(unittest.TestCase):
|
|
102
130
|
"""Test read and EDA methods. Data is loaded once for the entire class."""
|
|
@@ -159,6 +187,22 @@ class TestReadAndEdaMethods(unittest.TestCase):
|
|
|
159
187
|
query, self.db, {'movie_name': movie})
|
|
160
188
|
self.assertEqual(movie, result['movie'][0])
|
|
161
189
|
|
|
190
|
+
def test_execute_read_query_in_transactions(self):
|
|
191
|
+
"""Test a read query with 'CALL { ... } IN TRANSACTIONS', which requires
|
|
192
|
+
an implicit transaction rather than the managed transaction used by
|
|
193
|
+
execute_read_query for regular queries."""
|
|
194
|
+
plain_count = self.graph.execute_read_query(
|
|
195
|
+
"MATCH (p:Person) RETURN count(p) AS row_num", self.db)['row_num'][0]
|
|
196
|
+
query = """
|
|
197
|
+
MATCH (p:Person)
|
|
198
|
+
CALL (p) {
|
|
199
|
+
RETURN p AS person
|
|
200
|
+
} IN TRANSACTIONS OF 1000 ROWS
|
|
201
|
+
RETURN count(person) AS row_num
|
|
202
|
+
"""
|
|
203
|
+
result = self.graph.execute_read_query(query, self.db)
|
|
204
|
+
self.assertEqual(plain_count, result['row_num'][0])
|
|
205
|
+
|
|
162
206
|
def test_get_node_label_freq(self):
|
|
163
207
|
"""Test node label frequency EDA."""
|
|
164
208
|
result = self.graph.get_node_label_freq(self.db)
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"""Re-exports package modules for use by the test suite."""
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
sys.path.insert(0, os.path.abspath(
|
|
5
|
-
os.path.join(os.path.dirname(__file__), '..')))
|
|
6
|
-
from pyneoinstance import fileload
|
|
7
|
-
from pyneoinstance import util
|
|
8
|
-
from pyneoinstance import database
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|