pyneoinstance 2.0.2__tar.gz → 3.0.0__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 (34) hide show
  1. {pyneoinstance-2.0.2/pyneoinstance.egg-info → pyneoinstance-3.0.0}/PKG-INFO +8 -7
  2. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/README.md +6 -6
  3. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/__init__.py +1 -1
  4. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/database/context.py +1 -1
  5. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/database/neo4jdbms.py +95 -67
  6. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/util/__init__.py +2 -0
  7. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/util/functions.py +51 -0
  8. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0/pyneoinstance.egg-info}/PKG-INFO +8 -7
  9. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance.egg-info/SOURCES.txt +1 -0
  10. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyproject.toml +1 -1
  11. pyneoinstance-3.0.0/test/age_data.csv +300001 -0
  12. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/test_neo4jdb.py +4 -3
  13. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/test_util_functions.py +29 -1
  14. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/LICENSE +0 -0
  15. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/MANIFEST.in +0 -0
  16. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/database/__init__.py +0 -0
  17. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/fileload/__init__.py +0 -0
  18. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/fileload/context.py +0 -0
  19. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/fileload/helpers.py +0 -0
  20. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance/fileload/loadyaml.py +0 -0
  21. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance.egg-info/dependency_links.txt +0 -0
  22. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance.egg-info/requires.txt +0 -0
  23. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/pyneoinstance.egg-info/top_level.txt +0 -0
  24. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/setup.cfg +0 -0
  25. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/setup.py +0 -0
  26. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/__init__.py +0 -0
  27. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/context.py +0 -0
  28. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/cypher_queries.yaml +0 -0
  29. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/load_files.yaml +0 -0
  30. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/movies.csv +0 -0
  31. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/people.csv +0 -0
  32. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/roles.csv +0 -0
  33. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/test.py +0 -0
  34. {pyneoinstance-2.0.2 → pyneoinstance-3.0.0}/test/test_fileload.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyneoinstance
3
- Version: 2.0.2
3
+ Version: 3.0.0
4
4
  Summary: Handle Neo4j read and write transactions and provide methos for performing graph EDA
5
5
  Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -689,6 +689,7 @@ Requires-Dist: neo4j>=5.0.0
689
689
  Requires-Dist: pandas
690
690
  Requires-Dist: PyYAML
691
691
  Requires-Dist: pyvis
692
+ Dynamic: license-file
692
693
 
693
694
  # PyNeoInstance Package
694
695
 
@@ -829,7 +830,7 @@ We can update a Neo4j database with data contained in a Pandas Data Frame using
829
830
 
830
831
  - _data_: Pandas Data Frame containing data to process.
831
832
  - _database_: Optional, database on which to execute the queries.
832
- - _partitions_: Optional, number of partitions use to break the Data Frame in smaller chucks.
833
+ - _batchSize_: Optional, number of records per batch use to break the Data Frame in smaller chucks.
833
834
  - _parallel_: Whether to execute the query using multiple Python process.
834
835
  - _workers_: The number of processes to spawn to execute the queries in parallel.
835
836
  - _parameters_: Extra arguments containing optional cypher parameters.
@@ -909,25 +910,25 @@ node_load_queries = config['loading_queries']['nodes']
909
910
  person_load_result = graph.execute_write_query_with_data(node_load_queries['Person'], person_data, db_info['database'])
910
911
  ```
911
912
 
912
- If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _partitions_ argument how many partitions would you like to break this Data Frame into. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
913
+ If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _batchSize_ argument the number of records per data batch to break this Data Frame into smaller data batches. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
913
914
 
914
915
  ```python
915
916
  person_load_result = graph.execute_write_query_with_data(
916
917
  node_load_queries['person'],
917
918
  person_data,
918
919
  db_info['database'],
919
- partitions = 5
920
+ batchSize = 100000
920
921
  )
921
922
  ```
922
923
  ## Updating a database with data in a Data Frame: Parallel
923
924
 
924
- To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _partitions_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
925
+ To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _batchSize_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
925
926
 
926
927
  ```python
927
928
  person_load_result = graph.execute_write_query_with_data(
928
929
  node_load_queries['person'],
929
930
  person_data,
930
- partitions = 5,
931
+ batchSize = 100000,
931
932
  parallel = True,
932
933
  workers = 5
933
934
  )
@@ -137,7 +137,7 @@ We can update a Neo4j database with data contained in a Pandas Data Frame using
137
137
 
138
138
  - _data_: Pandas Data Frame containing data to process.
139
139
  - _database_: Optional, database on which to execute the queries.
140
- - _partitions_: Optional, number of partitions use to break the Data Frame in smaller chucks.
140
+ - _batchSize_: Optional, number of records per batch use to break the Data Frame in smaller chucks.
141
141
  - _parallel_: Whether to execute the query using multiple Python process.
142
142
  - _workers_: The number of processes to spawn to execute the queries in parallel.
143
143
  - _parameters_: Extra arguments containing optional cypher parameters.
@@ -217,25 +217,25 @@ node_load_queries = config['loading_queries']['nodes']
217
217
  person_load_result = graph.execute_write_query_with_data(node_load_queries['Person'], person_data, db_info['database'])
218
218
  ```
219
219
 
220
- If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _partitions_ argument how many partitions would you like to break this Data Frame into. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
220
+ If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _batchSize_ argument the number of records per data batch to break this Data Frame into smaller data batches. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
221
221
 
222
222
  ```python
223
223
  person_load_result = graph.execute_write_query_with_data(
224
224
  node_load_queries['person'],
225
225
  person_data,
226
226
  db_info['database'],
227
- partitions = 5
227
+ batchSize = 100000
228
228
  )
229
229
  ```
230
230
  ## Updating a database with data in a Data Frame: Parallel
231
231
 
232
- To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _partitions_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
232
+ To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _batchSize_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
233
233
 
234
234
  ```python
235
235
  person_load_result = graph.execute_write_query_with_data(
236
236
  node_load_queries['person'],
237
237
  person_data,
238
- partitions = 5,
238
+ batchSize = 100000,
239
239
  parallel = True,
240
240
  workers = 5
241
241
  )
@@ -305,4 +305,4 @@ schema.show('schema.html',notebook=False)
305
305
 
306
306
  For more detailed examples please refer to the following articles:
307
307
  * Data ingestion: [PyNeoInstance: A User-Friendly Python Library for Neo4j](https://medium.com/@a-gilmore/pyneoinstance-a-user-friendly-python-library-for-neo4j-dbefa3117bb2).
308
- * Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
308
+ * Graph EDA: [Performing graph EDA with the PyNeoInstance Python Package](https://medium.com/@jose.alvarado-guzman/0c0f6f230aa9)
@@ -1,4 +1,4 @@
1
1
  from .database.neo4jdbms import Neo4jInstance
2
2
  from .fileload.loadyaml import load_yaml_file
3
3
 
4
- __version__ = '2.0.2'
4
+ __version__ = '3.0.0'
@@ -3,4 +3,4 @@ import os
3
3
  import sys
4
4
  sys.path.insert(0, os.path.abspath(
5
5
  os.path.join(os.path.dirname(__file__), '..')))
6
- from pyneoinstance.util.functions import get_logger
6
+ from pyneoinstance.util.functions import get_logger,get_batches,get_columns_diff
@@ -22,6 +22,7 @@ from neo4j.exceptions import ServiceUnavailable
22
22
  from neo4j.exceptions import AuthError
23
23
  from neo4j.exceptions import ClientError
24
24
  from neo4j.exceptions import ConfigurationError
25
+ from .context import get_logger, get_batches, get_columns_diff
25
26
 
26
27
  def _get_driver(neo_info : Dict[str, str]):
27
28
  try:
@@ -59,54 +60,6 @@ def _read_transaction_function(transaction, query, **kwargs):
59
60
  data = DataFrame(results.values(), columns=results.keys())
60
61
  return data
61
62
 
62
- def _execute_write(session, query,
63
- rows: Optional[Dict[str, Any]] = None,
64
- parameters: Optional[Dict[str, Any]] = None
65
- ):
66
- if parameters:
67
- params = parameters
68
- else:
69
- params = {}
70
- try:
71
- if rows:
72
- results = session.execute_write(
73
- _write_transaction_function, query,
74
- rows = rows, **params).counters.__dict__
75
- else:
76
- results = session.execute_write(
77
- _write_transaction_function, query,
78
- **params).counters.__dict__
79
- except ServiceUnavailable as exception:
80
- raise ServiceUnavailable() from exception
81
- except ClientError as exception:
82
- raise ClientError() from exception
83
- return results
84
-
85
- def _execute_write_parallel(neo_info, database, query,
86
- rows: Optional[Dict[str, Any]] = None,
87
- parameters: Optional[Dict[str, Any]] = None
88
- ):
89
- if parameters:
90
- params = parameters
91
- else:
92
- params = {}
93
- with _get_driver(neo_info) as driver:
94
- with _get_session(driver, database) as session:
95
- try:
96
- if rows:
97
- results = session.execute_write(
98
- _write_transaction_function, query,
99
- rows = rows, **params).counters.__dict__
100
- else:
101
- results = session.execute_write(
102
- _write_transaction_function, query,
103
- **params).counters.__dict__
104
- except ServiceUnavailable as exception:
105
- raise ServiceUnavailable() from exception
106
- except ClientError as exception:
107
- raise ClientError() from exception
108
- return results
109
-
110
63
  class Neo4jInstance:
111
64
  """Class use to handle Neo4j requests.
112
65
 
@@ -169,7 +122,12 @@ class Neo4jInstance:
169
122
  self.neo_info['password'] = password
170
123
  self.neo_info['encrypted'] = kwargs.get('encrypted') or ''
171
124
  self.__results = defaultdict(int)
172
-
125
+ self.__logger = get_logger('pyneoinstance', logging.WARNING)
126
+ stream_handler = logging.StreamHandler()
127
+ formatter = logging.Formatter(
128
+ '%(levelname)s - %(message)s')
129
+ stream_handler.setFormatter(formatter)
130
+ self.__logger.addHandler(stream_handler)
173
131
  def execute_read_query(self, query: str,
174
132
  database: Optional[str] = None,
175
133
  parameters: Optional[Dict[str, Any]] = None
@@ -251,7 +209,7 @@ class Neo4jInstance:
251
209
  with _get_driver(self.neo_info) as driver:
252
210
  with _get_session(driver, database) as session:
253
211
  for query in queries:
254
- result = _execute_write(session, query, parameters=params)
212
+ result = self._execute_write(session, query, parameters=params)
255
213
  for key, value in result.items():
256
214
  if key != '_contains_updates':
257
215
  results[key] += value
@@ -289,9 +247,10 @@ class Neo4jInstance:
289
247
  result = self.execute_write_queries([query], database, parameters)
290
248
  return result
291
249
 
292
- def execute_write_queries_with_data(self, queries: List[str], data: DataFrame,
250
+ def execute_write_queries_with_data(self, queries: List[str],
251
+ data: DataFrame,
293
252
  database: Optional[str] = None,
294
- partitions: Optional[int] = 1,
253
+ batchSize: Optional[int] = 100_000,
295
254
  parallel: Optional[bool] = False,
296
255
  workers: Optional[int] = None,
297
256
  parameters: Optional[Dict[str, Any]] = None
@@ -307,8 +266,8 @@ class Neo4jInstance:
307
266
  database : str, optional
308
267
  Name of the Neo4j database of which to execute the transactions.
309
268
  If not provided the default database is going to be use.
310
- partitions : int, optional
311
- The number of partitions in which to split the data frame.
269
+ batchSize : int, optional
270
+ The number of records per batch or partitions of the data frame.
312
271
  parallel : bool, optional
313
272
  Wheather to execute the load in parallel.
314
273
  workers : int, optional
@@ -332,10 +291,13 @@ class Neo4jInstance:
332
291
  When the number of batch sizes to split the DataFrame on
333
292
  is larger than the number of rows in it.
334
293
  """
335
- if partitions > data.shape[0]:
336
- raise ValueError(
337
- "The batch size cannot be greater than the number of rows in the data.")
338
- data_chunks = np.array_split(data,partitions)
294
+ row_num = data.shape[0]
295
+ batchSize = batchSize if batchSize <= row_num else row_num
296
+ msg = f'Partitioning the data in batches of size {batchSize}'
297
+ self.__logger.info(msg)
298
+ data_chunks = get_batches(data, batchSize)
299
+ partitions = len(data_chunks)
300
+ parallel = parallel if len(data_chunks)>1 else False
339
301
  if parameters:
340
302
  params = parameters
341
303
  else:
@@ -345,21 +307,33 @@ class Neo4jInstance:
345
307
  workers_num = workers
346
308
  else:
347
309
  workers_num = mp.cpu_count()
310
+ msg = f'Loading {partitions:,.0f} data chunks using {workers_num} thread(s)'
311
+ self.__logger.info(msg)
348
312
  for query in queries:
313
+ col_diff = get_columns_diff(query,data.columns)
314
+ msg = f'This columns are not in your data: {col_diff}'
315
+ if len(col_diff)!=0:
316
+ self.__logger.warning(msg)
349
317
  with mp.Pool(workers_num) as worker_pool:
350
318
  results = worker_pool.starmap(
351
- _execute_write_parallel,
352
- [(self.neo_info, database,query, d.fillna(value='').to_dict('records'),
319
+ self._execute_write_parallel,
320
+ [(self.neo_info, database,query, data.iloc[d].to_dict('records'),
353
321
  params) for d in data_chunks]
354
322
  )
355
323
  else:
324
+ msg = f'Loading {partitions:,.0f} data chunks sequentially'
325
+ self.__logger.info(msg)
356
326
  results = []
357
327
  with _get_driver(self.neo_info) as driver:
358
328
  with _get_session(driver, database) as session:
359
- for rows in data_chunks:
360
- rows_dict = {'rows': rows.fillna(value="").to_dict('records')}
329
+ for idx in data_chunks:
330
+ rows_dict = {'rows': data.iloc[idx].to_dict('records')}
361
331
  for query in queries:
362
- results.append(_execute_write(session,
332
+ col_diff = get_columns_diff(query,data.columns)
333
+ msg = f'This columns are not in your data: {col_diff}'
334
+ if len(col_diff)!=0:
335
+ self.__logger.warning(msg)
336
+ results.append(self._execute_write(session,
363
337
  query,
364
338
  rows_dict['rows'],
365
339
  params))
@@ -369,12 +343,14 @@ class Neo4jInstance:
369
343
  self.__results[key] += value
370
344
  results = dict(self.__results.copy())
371
345
  self.__results.clear()
346
+ msg = f'Completed load with results {results}'
347
+ self.__logger.info(msg)
372
348
  return results
373
349
 
374
350
  def execute_write_query_with_data(self,
375
351
  query: str, data: DataFrame,
376
352
  database: Optional[str] = None,
377
- partitions: Optional[int] = 1,
353
+ batchSize: Optional[int] = 100_000,
378
354
  parallel: Optional[bool] = False,
379
355
  workers: Optional[int] = None,
380
356
  parameters: Optional[Dict[str, Any]] = None
@@ -390,8 +366,8 @@ class Neo4jInstance:
390
366
  database : str, optional
391
367
  Name of the Neo4j database of which to execute the transaction.
392
368
  If not provided the default database is going to be use.
393
- partitions : int, optional
394
- The number of partitions in which to split the data frame.
369
+ batchSize : int, optional
370
+ The number of records per batch or partitions of the data frame.
395
371
  parallel : bool, optional
396
372
  Wheather to execute the load in parallel.
397
373
  workers : int, optional
@@ -420,7 +396,7 @@ class Neo4jInstance:
420
396
  else:
421
397
  params = {}
422
398
  result = self.execute_write_queries_with_data(
423
- [query], data,database, partitions, parallel, workers, params)
399
+ [query], data,database, batchSize, parallel, workers, params)
424
400
  return result
425
401
 
426
402
  def get_node_label_freq(self, database: Optional[str] = None) -> DataFrame:
@@ -806,3 +782,55 @@ class Neo4jInstance:
806
782
  rela_parts = relationship.split("|")
807
783
  network.add_edge(rela_parts[0],rela_parts[2],title=rela_parts[1])
808
784
  return network
785
+
786
+ def _execute_write_parallel(self, neo_info, database, query,
787
+ rows: Optional[Dict[str, Any]] = None,
788
+ parameters: Optional[Dict[str, Any]] = None
789
+ ):
790
+ if parameters:
791
+ params = parameters
792
+ else:
793
+ params = {}
794
+ with _get_driver(neo_info) as driver:
795
+ with _get_session(driver, database) as session:
796
+ try:
797
+ if rows:
798
+ results = session.execute_write(
799
+ _write_transaction_function, query,
800
+ rows = rows, **params).counters.__dict__
801
+ else:
802
+ results = session.execute_write(
803
+ _write_transaction_function, query,
804
+ **params).counters.__dict__
805
+ except ServiceUnavailable as exception:
806
+ raise ServiceUnavailable() from exception
807
+ except ClientError as exception:
808
+ raise ClientError() from exception
809
+ msg = f'Successfully loaded partition with resulting in {results}'
810
+ self.__logger.info(msg)
811
+ return results
812
+
813
+ def _execute_write(self, session, query,
814
+ rows: Optional[Dict[str, Any]] = None,
815
+ parameters: Optional[Dict[str, Any]] = None
816
+ ):
817
+ if parameters:
818
+ params = parameters
819
+ else:
820
+ params = {}
821
+ try:
822
+ if rows:
823
+ results = session.execute_write(
824
+ _write_transaction_function, query,
825
+ rows = rows, **params).counters.__dict__
826
+ else:
827
+ results = session.execute_write(
828
+ _write_transaction_function, query,
829
+ **params).counters.__dict__
830
+ except ServiceUnavailable as exception:
831
+ raise ServiceUnavailable() from exception
832
+ except ClientError as exception:
833
+ raise ClientError() from exception
834
+ msg = f'Successfully loaded partition with resulting in {results}'
835
+ self.__logger.info(msg)
836
+ return results
@@ -1,3 +1,5 @@
1
1
  from .functions import get_file_name
2
2
  from .functions import is_reachable_url
3
3
  from .functions import get_logger
4
+ from .functions import get_batches
5
+ from .functions import get_columns_diff
@@ -4,6 +4,9 @@
4
4
  import functools
5
5
  import time
6
6
  import logging
7
+ import re
8
+ import numpy as np
9
+ from pandas import DataFrame
7
10
  from datetime import datetime
8
11
  from typing import Tuple, Any, Callable, List
9
12
  from urllib.request import urlopen
@@ -94,3 +97,51 @@ def timing(function : Callable) -> Tuple[Any, float]:
94
97
  elapsed_time = end - start * 1.0
95
98
  return result, elapsed_time
96
99
  return wrap
100
+
101
+ def get_batches(data: DataFrame, batch_size) -> List[List[int]]:
102
+ """Partition the data indexes in batches of the specified size.
103
+
104
+ Parameters
105
+ ----------
106
+ data : DataFrame
107
+ Pandas DataFrame containing the data
108
+ batch_size : int
109
+ Number of records per partition
110
+
111
+ Returns
112
+ -------
113
+ List[List[int, bool]]
114
+ List containing the list of indexes of the DataFrame per partition.
115
+ """
116
+ records = data.shape[0]
117
+ indexes = data.index
118
+ partitions = records // batch_size
119
+ if partitions < 2:
120
+ batches = [list(indexes)]
121
+ else:
122
+ remainder = records % batch_size
123
+ array = np.array(
124
+ indexes[:records-remainder]).reshape(partitions,-1)
125
+ batches = array.tolist()
126
+ if remainder > 0:
127
+ batches.append(indexes[-remainder:])
128
+ return batches
129
+
130
+ def get_columns_diff(query: str, columns: List[str]) -> List[str]:
131
+ """Get a list of the columns that a Cypher query is trying to access that are
132
+ not in the list of columns provided.
133
+
134
+ Parameters
135
+ ----------
136
+ query : str
137
+ Cypher query
138
+ columns : List[str]
139
+ List containing the column names of a Pandas Data Frame
140
+ Returns
141
+ -------
142
+ List[str]
143
+ List containing the columns that Cypher is trying to access that are not
144
+ included in the columns names of the data frame.
145
+ """
146
+ cypher_attributes = set([w.split('.')[1] for w in re.findall(r'\brow\.\w+',query)])
147
+ return [a for a in cypher_attributes if a not in columns]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyneoinstance
3
- Version: 2.0.2
3
+ Version: 3.0.0
4
4
  Summary: Handle Neo4j read and write transactions and provide methos for performing graph EDA
5
5
  Author-email: "Jose A. Alvarado-Guzman" <jose.alvarado-guzman@neo4j.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -689,6 +689,7 @@ Requires-Dist: neo4j>=5.0.0
689
689
  Requires-Dist: pandas
690
690
  Requires-Dist: PyYAML
691
691
  Requires-Dist: pyvis
692
+ Dynamic: license-file
692
693
 
693
694
  # PyNeoInstance Package
694
695
 
@@ -829,7 +830,7 @@ We can update a Neo4j database with data contained in a Pandas Data Frame using
829
830
 
830
831
  - _data_: Pandas Data Frame containing data to process.
831
832
  - _database_: Optional, database on which to execute the queries.
832
- - _partitions_: Optional, number of partitions use to break the Data Frame in smaller chucks.
833
+ - _batchSize_: Optional, number of records per batch use to break the Data Frame in smaller chucks.
833
834
  - _parallel_: Whether to execute the query using multiple Python process.
834
835
  - _workers_: The number of processes to spawn to execute the queries in parallel.
835
836
  - _parameters_: Extra arguments containing optional cypher parameters.
@@ -909,25 +910,25 @@ node_load_queries = config['loading_queries']['nodes']
909
910
  person_load_result = graph.execute_write_query_with_data(node_load_queries['Person'], person_data, db_info['database'])
910
911
  ```
911
912
 
912
- If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _partitions_ argument how many partitions would you like to break this Data Frame into. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
913
+ If the number of rows in your Data Frame is 100K or more it is recommended to break the Data Frame in smaller chunks so that they can be process in different transactions. This will avoid running out of heap error. To do this, just indicate in the _batchSize_ argument the number of records per data batch to break this Data Frame into smaller data batches. These partitions will be loaded in different sequential transactions unless you specified loading them in parallel by setting the _parallel_ argument to true. We will discuss this process in the next section.
913
914
 
914
915
  ```python
915
916
  person_load_result = graph.execute_write_query_with_data(
916
917
  node_load_queries['person'],
917
918
  person_data,
918
919
  db_info['database'],
919
- partitions = 5
920
+ batchSize = 100000
920
921
  )
921
922
  ```
922
923
  ## Updating a database with data in a Data Frame: Parallel
923
924
 
924
- To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _partitions_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
925
+ To load the data in the Data Frame in parallel, we need to partition the Data Frame by incrementing the _batchSize_ argument and set the _parallel_ argument to true. By default, this will use all the available virtual CPUs in the machine. If you need to use less, you can specify how many to use by setting the _workers_ argument.
925
926
 
926
927
  ```python
927
928
  person_load_result = graph.execute_write_query_with_data(
928
929
  node_load_queries['person'],
929
930
  person_data,
930
- partitions = 5,
931
+ batchSize = 100000,
931
932
  parallel = True,
932
933
  workers = 5
933
934
  )
@@ -19,6 +19,7 @@ pyneoinstance/fileload/loadyaml.py
19
19
  pyneoinstance/util/__init__.py
20
20
  pyneoinstance/util/functions.py
21
21
  test/__init__.py
22
+ test/age_data.csv
22
23
  test/context.py
23
24
  test/cypher_queries.yaml
24
25
  test/load_files.yaml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "pyneoinstance"
9
- version = "2.0.2"
9
+ version = "3.0.0"
10
10
  description = "Handle Neo4j read and write transactions and provide methos for performing graph EDA"
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Jose A. Alvarado-Guzman",email = "jose.alvarado-guzman@neo4j.com" }]