4242from graph_notebook .decorators .decorators import display_exceptions , magic_variables , neptune_db_only
4343from graph_notebook .magics .ml import neptune_ml_magic_handler , generate_neptune_ml_parser
4444from graph_notebook .magics .streams import StreamViewer
45- from graph_notebook .neptune .client import ClientBuilder , Client ,PARALLELISM_OPTIONS , PARALLELISM_HIGH , \
45+ from graph_notebook .neptune .client import ClientBuilder , Client , PARALLELISM_OPTIONS , PARALLELISM_HIGH , \
4646 LOAD_JOB_MODES , MODE_AUTO , FINAL_LOAD_STATUSES , SPARQL_ACTION , FORMAT_CSV , FORMAT_OPENCYPHER , FORMAT_NTRIPLE , \
4747 DB_LOAD_TYPES , ANALYTICS_LOAD_TYPES , VALID_BULK_FORMATS , VALID_INCREMENTAL_FORMATS , \
4848 FORMAT_NQUADS , FORMAT_RDFXML , FORMAT_TURTLE , STREAM_RDF , STREAM_PG , STREAM_ENDPOINTS , \
4949 NEPTUNE_CONFIG_HOST_IDENTIFIERS , is_allowed_neptune_host , \
50- STATISTICS_LANGUAGE_INPUTS , STATISTICS_MODES , SUMMARY_MODES , \
51- SPARQL_EXPLAIN_MODES , OPENCYPHER_EXPLAIN_MODES , OPENCYPHER_PLAN_CACHE_MODES , OPENCYPHER_DEFAULT_TIMEOUT
50+ STATISTICS_LANGUAGE_INPUTS , STATISTICS_LANGUAGE_INPUTS_SPARQL , STATISTICS_MODES , SUMMARY_MODES , \
51+ SPARQL_EXPLAIN_MODES , OPENCYPHER_EXPLAIN_MODES , OPENCYPHER_PLAN_CACHE_MODES , OPENCYPHER_DEFAULT_TIMEOUT , \
52+ OPENCYPHER_STATUS_STATE_MODES , normalize_service_name
5253from graph_notebook .network import SPARQLNetwork
5354from graph_notebook .network .gremlin .GremlinNetwork import parse_pattern_list_str , GremlinNetwork
5455from graph_notebook .visualization .rows_and_columns import sparql_get_rows_and_columns , opencypher_get_rows_and_columns
@@ -255,22 +256,31 @@ def get_load_ids(neptune_client):
255256 return ids , res
256257
257258
258- def process_statistics_400 (is_summary : bool , response ):
259+ def process_statistics_400 (response , is_summary : bool = False , is_analytics : bool = False ):
259260 bad_request_res = json .loads (response .text )
260261 res_code = bad_request_res ['code' ]
261262 if res_code == 'StatisticsNotAvailableException' :
262- print ("No statistics found. Please ensure that auto-generation of DFE statistics is enabled by running "
263- "'%statistics' and checking if 'autoCompute' if set to True. Alternately, you can manually "
264- "trigger statistics generation by running: '%statistics --mode refresh'." )
263+ print ("No statistics found. " , end = "" )
264+ if not is_analytics :
265+ print ("Please ensure that auto-generation of DFE statistics is enabled by running '%statistics' and "
266+ "checking if 'autoCompute' if set to True. Alternately, you can manually trigger statistics "
267+ "generation by running: '%statistics --mode refresh'." )
268+ return
265269 elif res_code == "BadRequestException" :
266- print ("Unable to query the statistics endpoint. Please check that your Neptune instance is of size r5.large or "
267- "greater in order to have DFE statistics enabled." )
268- if is_summary and "Statistics is disabled" not in bad_request_res ["detailedMessage" ]:
269- print ("\n Please also note that the Graph Summary API is only available in Neptune engine version 1.2.1.0 "
270- "and later." )
271- else :
272- print ("Query encountered 400 error, please see below." )
270+ if is_analytics :
271+ if bad_request_res ["message" ] == 'Bad route: /summary' :
272+ logger .debug ("Encountered bad route exception for Analytics, retrying with legacy statistics endpoint." )
273+ return 1
274+ else :
275+ print ("Unable to query the statistics endpoint. Please check that your Neptune instance is of size "
276+ "r5.large or greater in order to have DFE statistics enabled." )
277+ if is_summary and "Statistics is disabled" not in bad_request_res ["detailedMessage" ]:
278+ print ("\n Please also note that the Graph Summary API is only available in Neptune engine version "
279+ "1.2.1.0 and later." )
280+ return
281+ print ("Query encountered 400 error, please see below." )
273282 print (f"\n Full response: { bad_request_res } " )
283+ return
274284
275285
276286def mcl_to_bytes (mcl ):
@@ -445,6 +455,7 @@ def stream_viewer(self,line):
445455 @line_magic
446456 @needs_local_scope
447457 @display_exceptions
458+ @neptune_db_only
448459 def statistics (self , line , local_ns : dict = None ):
449460 parser = argparse .ArgumentParser ()
450461 parser .add_argument ('language' , nargs = '?' , type = str .lower , default = "propertygraph" ,
@@ -476,9 +487,9 @@ def statistics(self, line, local_ns: dict = None):
476487 statistics_res = self .client .statistics (args .language , args .summary , mode )
477488 if statistics_res .status_code == 400 :
478489 if args .summary :
479- process_statistics_400 (True , statistics_res )
490+ process_statistics_400 (statistics_res )
480491 else :
481- process_statistics_400 (False , statistics_res )
492+ process_statistics_400 (statistics_res )
482493 return
483494 statistics_res .raise_for_status ()
484495 statistics_res_json = statistics_res .json ()
@@ -508,10 +519,21 @@ def summary(self, line, local_ns: dict = None):
508519 else :
509520 mode = "basic"
510521
511- summary_res = self .client .statistics (args .language , True , mode )
522+ language_ep = args .language
523+ if self .client .is_analytics_domain ():
524+ is_analytics = True
525+ if language_ep in STATISTICS_LANGUAGE_INPUTS_SPARQL :
526+ print ("SPARQL is not supported for Neptune Analytics, defaulting to PropertyGraph." )
527+ language_ep = 'propertygraph'
528+ else :
529+ is_analytics = False
530+ summary_res = self .client .statistics (language_ep , True , mode , is_analytics )
512531 if summary_res .status_code == 400 :
513- process_statistics_400 (True , summary_res )
514- return
532+ retry_legacy = process_statistics_400 (summary_res , is_summary = True , is_analytics = is_analytics )
533+ if retry_legacy == 1 :
534+ summary_res = self .client .statistics (language_ep , True , mode , False )
535+ else :
536+ return
515537 summary_res .raise_for_status ()
516538 summary_res_json = summary_res .json ()
517539 if not args .silent :
@@ -530,6 +552,16 @@ def graph_notebook_host(self, line):
530552 self ._generate_client_from_config (self .graph_notebook_config )
531553 print (f'set host to { self .graph_notebook_config .host } ' )
532554
555+ @line_magic
556+ def graph_notebook_service (self , line ):
557+ if line == '' :
558+ print (f'current service name: { self .graph_notebook_config .neptune_service } ' )
559+ return
560+
561+ self .graph_notebook_config .neptune_service = normalize_service_name (line )
562+ self ._generate_client_from_config (self .graph_notebook_config )
563+ print (f'set service name to { self .graph_notebook_config .neptune_service } ' )
564+
533565 @magic_variables
534566 @cell_magic
535567 @needs_local_scope
@@ -1177,6 +1209,7 @@ def opencypher_status(self, line='', local_ns: dict = None):
11771209 @line_magic
11781210 @needs_local_scope
11791211 @display_exceptions
1212+ @neptune_db_only
11801213 def status (self , line = '' , local_ns : dict = None ):
11811214 logger .info (f'calling for status on endpoint { self .graph_notebook_config .host } ' )
11821215 parser = argparse .ArgumentParser ()
@@ -1547,6 +1580,7 @@ def load(self, line='', local_ns: dict = None):
15471580 value = str (args .concurrency ),
15481581 placeholder = 1 ,
15491582 min = 1 ,
1583+ max = 2 ** 16 ,
15501584 disabled = False ,
15511585 layout = widgets .Layout (display = concurrency_hbox_visibility ,
15521586 width = widget_width )
@@ -1556,6 +1590,7 @@ def load(self, line='', local_ns: dict = None):
15561590 value = args .periodic_commit ,
15571591 placeholder = 0 ,
15581592 min = 0 ,
1593+ max = 1000000 ,
15591594 disabled = False ,
15601595 layout = widgets .Layout (display = periodic_commit_hbox_visibility ,
15611596 width = widget_width )
@@ -1770,13 +1805,12 @@ def on_button_clicked(b):
17701805 source_format_validation_label = widgets .HTML ('<p style="color:red;">Format cannot be blank.</p>' )
17711806 source_format_hbox .children += (source_format_validation_label ,)
17721807
1773- if not arn .value .startswith ('arn:aws' ) and source .value .startswith (
1774- "s3://" ): # only do this validation if we are using an s3 bucket.
1775- validated = False
1776- arn_validation_label = widgets .HTML ('<p style="color:red;">Load ARN must start with "arn:aws"</p>' )
1777- arn_hbox .children += (arn_validation_label ,)
1778-
17791808 if load_type == 'bulk' :
1809+ if not arn .value .startswith ('arn:aws' ) and source .value .startswith (
1810+ "s3://" ): # only do this validation if we are using an s3 bucket.
1811+ validated = False
1812+ arn_validation_label = widgets .HTML ('<p style="color:red;">Load ARN must start with "arn:aws"</p>' )
1813+ arn_hbox .children += (arn_validation_label ,)
17801814 dependencies_list = list (filter (None , dependencies .value .split ('\n ' )))
17811815 if not len (dependencies_list ) < 64 :
17821816 validated = False
@@ -3105,9 +3139,15 @@ def handle_opencypher_status(self, line, local_ns):
31053139 parser .add_argument ('-c' , '--cancelQuery' , action = 'store_true' , default = False ,
31063140 help = 'Tells the status command to cancel a query. This parameter does not take a value.' )
31073141 parser .add_argument ('-w' , '--includeWaiting' , action = 'store_true' , default = False ,
3108- help = 'When set to true and other parameters are not present, causes status information '
3109- 'for waiting queries to be returned as well as for running queries. '
3110- 'This parameter does not take a value.' )
3142+ help = 'Neptune DB only. When set to true and other parameters are not present, causes '
3143+ 'status information for waiting queries to be returned as well as for running '
3144+ 'queries. This parameter does not take a value.' )
3145+ parser .add_argument ('--state' , type = str .upper , default = 'ALL' ,
3146+ help = f'Neptune Analytics only. Specifies what subset of query states to retrieve the '
3147+ f'status of. Default is ALL. Accepted values: ${ OPENCYPHER_STATUS_STATE_MODES } ' )
3148+ parser .add_argument ('-m' , '--maxResults' , type = int , default = 200 ,
3149+ help = f'Neptune Analytics only. Sets an upper limit on the set of returned queries whose '
3150+ f'status matches --state. Default is 200.' )
31113151 parser .add_argument ('-s' , '--silent-cancel' , action = 'store_true' , default = False ,
31123152 help = 'If silent_cancel=true then the running query is cancelled and the HTTP response '
31133153 'code is 200. If silent_cancel is not present or silent_cancel=false, '
@@ -3116,21 +3156,50 @@ def handle_opencypher_status(self, line, local_ns):
31163156 parser .add_argument ('--store-to' , type = str , default = '' , help = 'store query result to this variable' )
31173157 args = parser .parse_args (line .split ())
31183158
3159+ using_analytics = self .client .is_analytics_domain ()
31193160 if not args .cancelQuery :
3120- if args .includeWaiting and not args .queryId :
3121- res = self .client .opencypher_status (include_waiting = args .includeWaiting )
3161+ query_id = ''
3162+ include_waiting = None
3163+ state = ''
3164+ max_results = None
3165+ if args .includeWaiting and not args .queryId and not self .client .is_analytics_domain ():
3166+ include_waiting = args .includeWaiting
3167+ elif args .state and not args .queryId and self .client .is_analytics_domain ():
3168+ state = args .state
3169+ max_results = args .maxResults
31223170 else :
3123- res = self .client .opencypher_status (query_id = args .queryId )
3171+ query_id = args .queryId
3172+ res = self .client .opencypher_status (query_id = query_id ,
3173+ include_waiting = include_waiting ,
3174+ state = state ,
3175+ max_results = max_results ,
3176+ use_analytics_endpoint = using_analytics )
3177+ if using_analytics and res .status_code == 400 and 'Bad route: /queries' in res .json ()["message" ]:
3178+ res = self .client .opencypher_status (query_id = query_id ,
3179+ include_waiting = include_waiting ,
3180+ state = state ,
3181+ max_results = max_results ,
3182+ use_analytics_endpoint = False )
31243183 res .raise_for_status ()
31253184 else :
31263185 if args .queryId == '' :
31273186 if not args .silent :
31283187 print (OPENCYPHER_CANCEL_HINT_MSG )
31293188 return
31303189 else :
3131- res = self .client .opencypher_cancel (args .queryId , args .silent_cancel )
3190+ res = self .client .opencypher_cancel (args .queryId ,
3191+ silent = args .silent_cancel ,
3192+ use_analytics_endpoint = using_analytics )
3193+ if using_analytics and res .status_code == 400 and 'Bad route: /queries' in res .json ()["message" ]:
3194+ res = self .client .opencypher_cancel (args .queryId ,
3195+ silent = args .silent_cancel ,
3196+ use_analytics_endpoint = False )
31323197 res .raise_for_status ()
3133- js = res .json ()
3134- store_to_ns (args .store_to , js , local_ns )
3135- if not args .silent :
3136- print (json .dumps (js , indent = 2 ))
3198+ if using_analytics and args .cancelQuery :
3199+ if not args .silent :
3200+ print (f'Submitted cancellation request for query ID: { args .queryId } ' )
3201+ else :
3202+ js = res .json ()
3203+ store_to_ns (args .store_to , js , local_ns )
3204+ if not args .silent :
3205+ print (json .dumps (js , indent = 2 ))
0 commit comments