From 6c018466d12df1f984c366b4684a683266e18e86 Mon Sep 17 00:00:00 2001 From: "jack.verhoosel" Date: Sun, 25 Jan 2026 17:12:19 +0100 Subject: [PATCH 1/3] made a first implementation of the UNION construct --- request_processor.py | 157 ++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 62 deletions(-) diff --git a/request_processor.py b/request_processor.py index a9735f1..10b30da 100644 --- a/request_processor.py +++ b/request_processor.py @@ -50,6 +50,7 @@ class RequestDecomposition(BaseModel): optionalPatterns: list = [] values: list = [] insertPattern: list = [] + subDecompositions: list = [] ################## @@ -57,6 +58,8 @@ class RequestDecomposition(BaseModel): ################## def constructGraphFromKnowledgeNetwork(query: str, requester_id: str, gaps_enabled) -> tuple[Graph, list]: + # TEST query + #query = "SELECT * WHERE {?s ?p ?o}" # first parse the query try: parsed_query = parseQuery(query) @@ -84,35 +87,41 @@ def constructGraphFromKnowledgeNetwork(query: str, requester_id: str, gaps_enabl # decompose the query algebra and get the main BGP pattern, possible OPTIONAL patterns and possible VALUES statements try: - query_decomposition = RequestDecomposition() - query_decomposition = decomposeRequest(algebra['p']['p'], query_decomposition) - logger.debug(f"Query decomposition VALUES is: {query_decomposition.values}") + query_decomposition = decomposeRequest(algebra['p']['p'], RequestDecomposition()) except Exception as e: raise Exception(f"Could not decompose query to get graph patterns, {e}") + # deal with multiple VALUES clauses, combine them and delete incorrect combinations + query_decomposition = combineValuesStatements(query_decomposition) + # now show the derived query decomposition showRequestDecomposition(query_decomposition, prologue.namespace_manager) - - # if there are multiple VALUES clauses, combine them and delete incorrect combinations - if len(query_decomposition.values) > 1: - logger.info(f"Now combining the VALUES statements") - query_decomposition = combineValuesStatements(query_decomposition) - # now show the query decomposition after values application - showRequestDecomposition(query_decomposition, prologue.namespace_manager) - # search bindings in the knowledge network for the graph patterns and build a local graph of them + # build up a graph (and optionally knowledge gaps) by executing the decomposition on the knowledge network graph = Graph() knowledge_gaps = [] + graph, knowledge_gaps = buildGraphFromDecomposition(graph, query_decomposition, requester_id, gaps_enabled, knowledge_gaps) + + logger.info(f"Knowledge network successfully responded to all the ask patterns!") + + return graph, knowledge_gaps + + +def buildGraphFromDecomposition(graph: Graph, + decomposition: RequestDecomposition, + requester_id: str, + gaps_enabled: bool, + knowledge_gaps: list) -> tuple[Graph, list]: - if len(query_decomposition.mainPattern) > 0: - # first, ask the main graph pattern and add the bindings to the graph + # first, ask the main graph pattern and add the bindings to the graph + if len(decomposition.mainPattern) > 0: logger.info('Main graph pattern is being asked from the knowledge network!') try: - pattern = query_decomposition.mainPattern + pattern = decomposition.mainPattern logger.info(f"Pattern that is asked: {pattern}") bindings = [{}] - if len(query_decomposition.values) > 0: - bindings = query_decomposition.values[0] + if len(decomposition.values) > 0: + bindings = decomposition.values[0] logger.info(f"Bindings that accompany the ASK: {bindings}") answer = knowledge_network.askPatternAtKnowledgeNetwork(requester_id, pattern, bindings, gaps_enabled) logger.info(f"Received answer from the knowledge network: {answer}") @@ -131,22 +140,33 @@ def constructGraphFromKnowledgeNetwork(query: str, requester_id: str, gaps_enabl except Exception as e: raise Exception(f"An error occurred when contacting the knowledge network: {e}") logger.info(f"Knowledge network successfully responded to the main graph pattern!") - - # second, loop over the optional graph patterns and add the bindings to the graph - try: - logger.info('Optional graph patterns are being asked from the knowledge network!') - for pattern in query_decomposition.optionalPatterns: - logger.info(f"Pattern that is asked: {pattern}") - answer = knowledge_network.askPatternAtKnowledgeNetwork(requester_id, pattern, [{}], gaps_enabled) - logger.info(f'Received answer from the knowledge network: {answer}') - # extend the graph with the triples and values in the bindings - graph = buildGraphFromTriplesAndBindings(graph, pattern, answer["bindingSet"]) - except Exception as e: - raise Exception(f"An error occurred when contacting the knowledge network: {e}") - logger.info(f"Knowledge network successfully responded to all the ask patterns!") else: logger.info(f"No main graph pattern is derived from the query, so the result is empty!") + # second, loop over the optional graph patterns and add the bindings to the graph + try: + logger.info('Optional graph patterns are being asked from the knowledge network!') + for pattern in decomposition.optionalPatterns: + logger.info(f"Pattern that is asked: {pattern}") + answer = knowledge_network.askPatternAtKnowledgeNetwork(requester_id, pattern, [{}], gaps_enabled) + logger.info(f'Received answer from the knowledge network: {answer}') + # extend the graph with the triples and values in the bindings + graph = buildGraphFromTriplesAndBindings(graph, pattern, answer["bindingSet"]) + except Exception as e: + raise Exception(f"An error occurred when contacting the knowledge network: {e}") + + # third, check if there sub-decompositions and loop over them to extend the graph and optionally knowledge gaps + try: + if len(decomposition.subDecompositions) > 0: + logger.info(f"Sub decompositions are being handled!") + for decomp in decomposition.subDecompositions: + graph, knowledge_gaps = buildGraphFromDecomposition(graph, decomp, requester_id, gaps_enabled, knowledge_gaps) + else: + logger.info(f"No sub decompositions to be handled!") + except Exception as e: + logger.info(f"Something went wrong when dealing with sub decompositions!") + raise Exception(f"An error occurred when contacting the knowledge network: {e}") + return graph, knowledge_gaps @@ -177,20 +197,15 @@ def checkAndDecomposeUpdate(update: str) -> RequestDecomposition: # decompose the request algebra and get the INSERT and WHERE part (if present) of the request try: - update_decomposition = RequestDecomposition() - update_decomposition = decomposeRequest(algebra[0], update_decomposition) + update_decomposition = decomposeRequest(algebra[0], RequestDecomposition()) except Exception as e: raise Exception(f"Could not decompose update request to get INSERT or WHERE graph pattern, {e}") - # now show the derived query decomposition + # deal with multiple VALUES clauses, combine them and delete incorrect combinations + update_decomposition = combineValuesStatements(update_decomposition) + + # now show the derived request decomposition showRequestDecomposition(update_decomposition, prologue.namespace_manager) - - # if there are multiple VALUES clauses, combine them and delete incorrect combinations - if len(update_decomposition.values) > 1: - logger.info(f"Now combining the VALUES statements") - update_decomposition = combineValuesStatements(update_decomposition) - # now show the query decomposition after values application - showRequestDecomposition(update_decomposition, prologue.namespace_manager) return update_decomposition @@ -292,6 +307,10 @@ def decomposeRequest(algebra: dict, decomposition: RequestDecomposition) -> Requ else: # it is either a filter_exists or a filter_not_exists raise Exception(f"Unsupported construct type {str(algebra['expr']).split('{')[0]} in construct type {type}. Please contact the endpoint administrator to implement this!") + case "Union": + # two new sub decompositions should be derived + decomposition.subDecompositions.append(decomposeRequest(algebra['p1'], RequestDecomposition())) + decomposition.subDecompositions.append(decomposeRequest(algebra['p2'], RequestDecomposition())) case "Join": # both parts should be added to the same main graph pattern decomposition = decomposeRequest(algebra['p1'], decomposition) @@ -337,29 +356,39 @@ def filterBindingsOnPatternVariables(bindings: list , pattern: list) -> list: def combineValuesStatements(decomposition: RequestDecomposition) -> RequestDecomposition: - # derive all combinations of VALUES clause elements - values_combinations = list(itertools.product(*decomposition.values)) - correct_values_combinations = [] - # delete all incorrect value combinations - for values_combination in values_combinations: - # check if this values combination is correct - correct = True - correct_values_combination = {} - # loop over the elements in the values combination - for element in values_combination: - for key in element.keys(): - logger.debug(f"Value for {key} is: {element[key]}") - if key not in correct_values_combination.keys(): - correct_values_combination[key] = element[key] - else: - if correct_values_combination[key] != element[key]: - # this values combination is inconsistent, because one key must only have one value - correct = False - if correct: - correct_values_combinations.append(correct_values_combination) - - decomposition.values = [correct_values_combinations] - logger.debug(f"Values after combining are: {decomposition.values}") + # if there are VALUES clause elements, combine them + if len(decomposition.values) > 1: + logger.info(f"Now combining the VALUES statements") + # derive all combinations of VALUES clause elements + logger.debug(f"Query decomposition VALUES is: {decomposition.values}") + values_combinations = list(itertools.product(*decomposition.values)) + correct_values_combinations = [] + # delete all incorrect value combinations + for values_combination in values_combinations: + # check if this values combination is correct + correct = True + correct_values_combination = {} + # loop over the elements in the values combination + for element in values_combination: + for key in element.keys(): + logger.debug(f"Value for {key} is: {element[key]}") + if key not in correct_values_combination.keys(): + correct_values_combination[key] = element[key] + else: + if correct_values_combination[key] != element[key]: + # this values combination is inconsistent, because one key must only have one value + correct = False + if correct: + correct_values_combinations.append(correct_values_combination) + + decomposition.values = [correct_values_combinations] + logger.debug(f"Values after combining are: {decomposition.values}") + + # if there are sub decompositions, do check if they have VALUES statements to be combined + subDecomp_combined_values = [] + for decomp in decomposition.subDecompositions: + subDecomp_combined_values.append(combineValuesStatements(decomp)) + decomposition.subDecompositions = subDecomp_combined_values return decomposition @@ -428,3 +457,7 @@ def showRequestDecomposition(qd: RequestDecomposition, nm: NamespaceManager): bound_triple += "\n" pattern += bound_triple logger.info(f"Derived the following insert pattern from the request:\n{pattern}") + + for decomp in qd.subDecompositions: + showRequestDecomposition(decomp,nm) + From f6ada4505d9422d4c4f89e3791fa053663e7f81a Mon Sep 17 00:00:00 2001 From: "jack.verhoosel" Date: Tue, 27 Jan 2026 16:02:21 +0100 Subject: [PATCH 2/3] First attempt to add DISTINCT, LIMIT and Filter of blanks --- request_processor.py | 50 +++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/request_processor.py b/request_processor.py index 10b30da..cd5aa0f 100644 --- a/request_processor.py +++ b/request_processor.py @@ -87,7 +87,7 @@ def constructGraphFromKnowledgeNetwork(query: str, requester_id: str, gaps_enabl # decompose the query algebra and get the main BGP pattern, possible OPTIONAL patterns and possible VALUES statements try: - query_decomposition = decomposeRequest(algebra['p']['p'], RequestDecomposition()) + query_decomposition = decomposeRequest(algebra['p'], RequestDecomposition()) except Exception as e: raise Exception(f"Could not decompose query to get graph patterns, {e}") @@ -115,7 +115,7 @@ def buildGraphFromDecomposition(graph: Graph, # first, ask the main graph pattern and add the bindings to the graph if len(decomposition.mainPattern) > 0: - logger.info('Main graph pattern is being asked from the knowledge network!') + logger.info('A main graph pattern is being asked from the knowledge network!') try: pattern = decomposition.mainPattern logger.info(f"Pattern that is asked: {pattern}") @@ -140,31 +140,28 @@ def buildGraphFromDecomposition(graph: Graph, except Exception as e: raise Exception(f"An error occurred when contacting the knowledge network: {e}") logger.info(f"Knowledge network successfully responded to the main graph pattern!") - else: - logger.info(f"No main graph pattern is derived from the query, so the result is empty!") # second, loop over the optional graph patterns and add the bindings to the graph try: - logger.info('Optional graph patterns are being asked from the knowledge network!') for pattern in decomposition.optionalPatterns: + logger.info('An optional graph pattern is being asked from the knowledge network!') logger.info(f"Pattern that is asked: {pattern}") answer = knowledge_network.askPatternAtKnowledgeNetwork(requester_id, pattern, [{}], gaps_enabled) logger.info(f'Received answer from the knowledge network: {answer}') # extend the graph with the triples and values in the bindings graph = buildGraphFromTriplesAndBindings(graph, pattern, answer["bindingSet"]) + logger.info(f"Knowledge network successfully responded to an optional graph pattern!") except Exception as e: raise Exception(f"An error occurred when contacting the knowledge network: {e}") # third, check if there sub-decompositions and loop over them to extend the graph and optionally knowledge gaps try: if len(decomposition.subDecompositions) > 0: - logger.info(f"Sub decompositions are being handled!") for decomp in decomposition.subDecompositions: + logger.info(f"A sub decomposition is being handled!") graph, knowledge_gaps = buildGraphFromDecomposition(graph, decomp, requester_id, gaps_enabled, knowledge_gaps) - else: - logger.info(f"No sub decompositions to be handled!") + logger.info(f"The sub decomposition has successfully been handled!") except Exception as e: - logger.info(f"Something went wrong when dealing with sub decompositions!") raise Exception(f"An error occurred when contacting the knowledge network: {e}") return graph, knowledge_gaps @@ -301,12 +298,20 @@ def decomposeRequest(algebra: dict, decomposition: RequestDecomposition) -> Requ logger.debug(f"Value clause after transforming to JSON is: {values_clause}") decomposition.values.append(values_clause) case "Filter": - if not str(algebra['expr']).startswith("Builtin"): - # it is a filter with a value for a variable, so this does not contain triples to be added to the graph pattern - decomposition = decomposeRequest(algebra['p'], decomposition) - else: - # it is either a filter_exists or a filter_not_exists - raise Exception(f"Unsupported construct type {str(algebra['expr']).split('{')[0]} in construct type {type}. Please contact the endpoint administrator to implement this!") + filter_type = algebra['expr'].name + logger.debug(f"Filter expression is {filter_type}") + match filter_type: + case "RelationalExpression": + # it is a filter that checks a relation between a variable and a value => this can be ignored, continue with the rest + decomposition = decomposeRequest(algebra['p'], decomposition) + case "ConditionalAndExpression": + # it is a filter that checks multiple conditions in an AND setting => this can be ignored, continue with the rest + decomposition = decomposeRequest(algebra['p'], decomposition) + case "Builtin_isBLANK": + # it is a filter that checks whether the argument is a blank node => this can be ignored, continue with the rest + decomposition = decomposeRequest(algebra['p'], decomposition) + case _: + raise Exception(f"Unsupported construct type {filter_type}. Please contact the endpoint administrator to implement this!") case "Union": # two new sub decompositions should be derived decomposition.subDecompositions.append(decomposeRequest(algebra['p1'], RequestDecomposition())) @@ -320,6 +325,15 @@ def decomposeRequest(algebra: dict, decomposition: RequestDecomposition) -> Requ decomposition = decomposeRequest(algebra['p1'], decomposition) # part p2 is an optional part which is BGP and its triples should be added as optional graph pattern decomposition.optionalPatterns.append(algebra['p2']['triples']) + case "Distinct": + # the distinct contains a part p that should be further processed + decomposition = decomposeRequest(algebra['p'], decomposition) + case "Project": + # the project contains a part p that should be further processed + decomposition = decomposeRequest(algebra['p'], decomposition) + case "Slice": + # the slice contains a part p that should be further processed + decomposition = decomposeRequest(algebra['p'], decomposition) case "Extend": # the extend contains a part p that should be further processed decomposition = decomposeRequest(algebra['p'], decomposition) @@ -421,7 +435,8 @@ def showRequestDecomposition(qd: RequestDecomposition, nm: NamespaceManager): bound_triple += element.n3(namespace_manager = nm) + " " bound_triple += "\n" pattern += bound_triple - logger.info(f"Derived the following main graph pattern from the request:\n{pattern}") + if pattern != "": + logger.info(f"Derived the following main graph pattern from the request:\n{pattern}") for p in qd.optionalPatterns: pattern = "" @@ -456,7 +471,8 @@ def showRequestDecomposition(qd: RequestDecomposition, nm: NamespaceManager): bound_triple += element.n3(namespace_manager = nm) + " " bound_triple += "\n" pattern += bound_triple - logger.info(f"Derived the following insert pattern from the request:\n{pattern}") + if pattern != "": + logger.info(f"Derived the following insert pattern from the request:\n{pattern}") for decomp in qd.subDecompositions: showRequestDecomposition(decomp,nm) From 9f7b5e06f1aeb9c327dae7ec286087b2c478bc37 Mon Sep 17 00:00:00 2001 From: "jack.verhoosel" Date: Thu, 26 Feb 2026 17:28:46 +0100 Subject: [PATCH 3/3] updated test_unit.py to cover all tests --- tests/test_unit.py | 164 +++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 71 deletions(-) diff --git a/tests/test_unit.py b/tests/test_unit.py index 8391fd4..8e28871 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -222,17 +222,32 @@ def test_get_query_URL_encoded_as_parameter_without_token(): assert value.startswith("http://example.org/BiggestClimateStrikes") logger.info("\n") - ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED + # check query with UNION that should give correct results + query = """PREFIX ex: + SELECT * WHERE { + { ?event ex:hasNumberOfPeople ?people . } + UNION + { ?event ex:mainPersonsInvolved ?person . } + }""" + params = {"query": query} + response = client.get("/query/", params=params, headers=headers) + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/IntroductionOfTheEuro") + logger.info("\n") - # check query with DISTINCT that is not yet allowed + # check query with DISTINCT that should give correct results query = """PREFIX ex: SELECT DISTINCT ?datetime WHERE { ?event ex:hasOccurredAt ?datetime . }""" params = {"query": query} response = client.get("/query/", params=params, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["datetime"]["value"] + assert value == "1969-07-20T20:05:00+00:00" logger.info("\n") # check query with LIMIT that is not yet allowed @@ -242,22 +257,13 @@ def test_get_query_URL_encoded_as_parameter_without_token(): } LIMIT 1""" params = {"query": query} response = client.get("/query/", params=params, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/FirstLandingOnTheMoon") logger.info("\n") - # check query with UNION that is not yet allowed - query = """PREFIX ex: - SELECT * WHERE { - { ?event ex:hasOccurredAt ?datetime . } - UNION - { ?event ex:mainPersonsInvolved ?person . } - }""" - params = {"query": query} - response = client.get("/query/", params=params, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") - logger.info("\n") + ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED # check query with FILTER EXISTS that should give correct results query = """PREFIX ex: @@ -464,39 +470,45 @@ def test_post_query_unencoded_in_body_without_token(): assert value.startswith("http://example.org/BiggestClimateStrikes") logger.info("\n") - ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED + # check query with UNION that should give correct results + query = """PREFIX ex: + SELECT * WHERE { + { ?event ex:hasNumberOfPeople ?people . } + UNION + { ?event ex:mainPersonsInvolved ?person . } + }""" + response = client.post("/query/", data=query, headers=headers) + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/IntroductionOfTheEuro") + logger.info("\n") - # check query with DISTINCT that is not yet allowed + # check query with DISTINCT that should give correct results query = """PREFIX ex: SELECT DISTINCT ?datetime WHERE { ?event ex:hasOccurredAt ?datetime . }""" response = client.post("/query/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["datetime"]["value"] + assert value == "1969-07-20T20:05:00+00:00" logger.info("\n") - # check query with LIMIT that is not yet allowed + # check query with LIMIT that should give correct results query = """PREFIX ex: SELECT * WHERE { ?event ex:hasOccurredAt ?datetime . } LIMIT 1""" response = client.post("/query/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/FirstLandingOnTheMoon") logger.info("\n") - # check query with UNION that is not yet allowed - query = """PREFIX ex: - SELECT * WHERE { - { ?event ex:hasOccurredAt ?datetime . } - UNION - { ?event ex:mainPersonsInvolved ?person . } - }""" - response = client.post("/query/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") - logger.info("\n") + ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED # check query with FILTER EXISTS that should give correct results query = """PREFIX ex: @@ -723,7 +735,20 @@ def test_post_query_URL_encoded_in_body_without_token(): assert value.startswith("http://example.org/BiggestClimateStrikes") logger.info("\n") - ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED + # check query with UNION that is not yet allowed + query = """PREFIX ex: + SELECT * WHERE { + { ?event ex:hasNumberOfPeople ?people . } + UNION + { ?event ex:mainPersonsInvolved ?person . } + }""" + payload = f"query={quote(query, safe='')}" + response = client.post("/query/", data=payload, headers=headers) + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/IntroductionOfTheEuro") + logger.info("\n") # check query with DISTINCT that is not yet allowed query = """PREFIX ex: @@ -732,8 +757,10 @@ def test_post_query_URL_encoded_in_body_without_token(): }""" payload = f"query={quote(query, safe='')}" response = client.post("/query/", data=payload, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["datetime"]["value"] + assert value == "1969-07-20T20:05:00+00:00" logger.info("\n") # check query with LIMIT that is not yet allowed @@ -743,22 +770,13 @@ def test_post_query_URL_encoded_in_body_without_token(): } LIMIT 1""" payload = f"query={quote(query, safe='')}" response = client.post("/query/", data=payload, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/FirstLandingOnTheMoon") logger.info("\n") - # check query with UNION that is not yet allowed - query = """PREFIX ex: - SELECT * WHERE { - { ?event ex:hasOccurredAt ?datetime . } - UNION - { ?event ex:mainPersonsInvolved ?person . } - }""" - payload = f"query={quote(query, safe='')}" - response = client.post("/query/", data=payload, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") - logger.info("\n") + ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED # check query with FILTER EXISTS that should give correct results query = """PREFIX ex: @@ -786,8 +804,6 @@ def test_post_query_URL_encoded_in_body_without_token(): logger.info("Query test successful!\n") - - # Test of post query with gaps unencoded in body without token def test_post_query_with_gaps_unencoded_in_body_without_token(): @@ -963,7 +979,19 @@ def test_post_query_with_gaps_unencoded_in_body_without_token(): assert value == "?event " logger.info("\n") - ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED + # check query with UNION that is not yet allowed + query = """PREFIX ex: + SELECT * WHERE { + { ?event ex:hasNumberOfPeople ?people . } + UNION + { ?event ex:mainPersonsInvolved ?person . } + }""" + response = client.post("/query-with-gaps/", data=query, headers=headers) + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/IntroductionOfTheEuro") + logger.info("\n") # check query with DISTINCT that is not yet allowed query = """PREFIX ex: @@ -971,8 +999,10 @@ def test_post_query_with_gaps_unencoded_in_body_without_token(): ?event ex:hasOccurredAt ?datetime . }""" response = client.post("/query-with-gaps/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["datetime"]["value"] + assert value == "1969-07-20T20:05:00+00:00" logger.info("\n") # check query with LIMIT that is not yet allowed @@ -981,21 +1011,13 @@ def test_post_query_with_gaps_unencoded_in_body_without_token(): ?event ex:hasOccurredAt ?datetime . } LIMIT 1""" response = client.post("/query-with-gaps/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") + assert response.status_code == 200 + content = response.json() + value = content["results"]["bindings"][0]["event"]["value"] + assert value.startswith("http://example.org/FirstLandingOnTheMoon") logger.info("\n") - # check query with UNION that is not yet allowed - query = """PREFIX ex: - SELECT * WHERE { - { ?event ex:hasOccurredAt ?datetime . } - UNION - { ?event ex:mainPersonsInvolved ?person . } - }""" - response = client.post("/query-with-gaps/", data=query, headers=headers) - assert response.status_code == 400 - assert response.json()['detail'].startswith("Query could not be processed by the endpoint: Could not decompose query to get graph patterns, Unsupported construct type") - logger.info("\n") + ### BELOW ARE QUERIES WITH CONSTRUCTS THAT ARE NOT YET SUPPORTED # check query with FILTER EXISTS that should give correct results query = """PREFIX ex: