-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Graph Store HTTP Protocol (GET, POST) back end #1668
base: master
Are you sure you want to change the base?
Changes from all commits
0e1394a
f840757
a1a96e6
a7cb7ba
e863950
364d425
6c9cc65
eaa76d4
8deab38
63aa1da
ff6d023
a2a95c9
2fd9677
c700734
c2ec8cf
5bafc3d
4dd0ab7
750dd6f
ec8f076
d45b2da
ff36dfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright 2024, University of Freiburg | ||
// Chair of Algorithms and Data Structures | ||
// Authors: Julian Mundhahs <[email protected]> | ||
|
||
#include "engine/GraphStoreProtocol.h" | ||
|
||
#include <boost/beast.hpp> | ||
|
||
// ____________________________________________________________________________ | ||
GraphOrDefault GraphStoreProtocol::extractTargetGraph( | ||
const ad_utility::url_parser::ParamValueMap& params) { | ||
// Extract the graph to be acted upon using `Indirect Graph | ||
// Identification`. | ||
const std::optional<std::string> graphIri = | ||
ad_utility::url_parser::checkParameter(params, "graph", std::nullopt); | ||
const bool isDefault = | ||
ad_utility::url_parser::checkParameter(params, "default", "").has_value(); | ||
if (!(graphIri.has_value() || isDefault)) { | ||
throw std::runtime_error("No graph IRI specified in the request."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can elaborate on how to specify graphs, to make life easier for the user. |
||
} | ||
if (graphIri.has_value() && isDefault) { | ||
throw std::runtime_error( | ||
"Only one of `default` and `graph` may be used for graph " | ||
"identification."); | ||
} | ||
if (graphIri.has_value()) { | ||
return GraphRef::fromIrirefWithoutBrackets(graphIri.value()); | ||
} else { | ||
AD_CORRECTNESS_CHECK(isDefault); | ||
return DEFAULT{}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright 2024, University of Freiburg | ||
// Chair of Algorithms and Data Structures | ||
// Authors: Julian Mundhahs <[email protected]> | ||
|
||
#pragma once | ||
|
||
#include <gtest/gtest_prod.h> | ||
|
||
#include "parser/ParsedQuery.h" | ||
#include "parser/RdfParser.h" | ||
#include "util/http/HttpUtils.h" | ||
#include "util/http/UrlParser.h" | ||
|
||
class GraphStoreProtocol { | ||
private: | ||
static ParsedQuery transformPost( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document the functions and everything. |
||
const ad_utility::httpUtils::HttpRequest auto& rawRequest, | ||
const GraphOrDefault& graph) { | ||
using namespace boost::beast::http; | ||
using Re2Parser = RdfStringParser<TurtleParser<Tokenizer>>; | ||
std::string contentTypeString; | ||
if (rawRequest.find(field::content_type) != rawRequest.end()) { | ||
contentTypeString = rawRequest.at(field::content_type); | ||
} | ||
if (contentTypeString.empty()) { | ||
// ContentType not set or empty; we don't try to guess -> 400 Bad Request | ||
} | ||
const auto contentType = | ||
ad_utility::getMediaTypeFromAcceptHeader(contentTypeString); | ||
Comment on lines
+22
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract this as a function. |
||
std::vector<TurtleTriple> triples; | ||
switch (contentType.value()) { | ||
case ad_utility::MediaType::turtle: | ||
case ad_utility::MediaType::ntriples: { | ||
auto parser = Re2Parser(); | ||
parser.setInputStream(rawRequest.body()); | ||
triples = parser.parseAndReturnAllTriples(); | ||
break; | ||
} | ||
default: { | ||
// Unsupported media type -> 415 Unsupported Media Type | ||
throw std::runtime_error(absl::StrCat( | ||
"Mediatype \"", ad_utility::toString(contentType.value()), | ||
"\" is not supported for SPARQL Graph Store HTTP " | ||
"Protocol in QLever.")); | ||
} | ||
} | ||
Comment on lines
+30
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can also be a small function (which can be defined in the |
||
ParsedQuery res; | ||
auto transformTurtleTriple = [&graph](const TurtleTriple& triple) { | ||
AD_CORRECTNESS_CHECK(triple.graphIri_.isId() && | ||
triple.graphIri_.getId() == | ||
qlever::specialIds().at(DEFAULT_GRAPH_IRI)); | ||
SparqlTripleSimpleWithGraph::Graph g{std::monostate{}}; | ||
if (std::holds_alternative<GraphRef>(graph)) { | ||
g = Iri(std::get<GraphRef>(graph).toStringRepresentation()); | ||
} | ||
Comment on lines
+53
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
return SparqlTripleSimpleWithGraph(triple.subject_, triple.predicate_, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be able to |
||
triple.object_, g); | ||
}; | ||
updateClause::GraphUpdate up{ | ||
ad_utility::transform(triples, transformTurtleTriple), {}}; | ||
res._clause = parsedQuery::UpdateClause{up}; | ||
return res; | ||
Comment on lines
+57
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The actual triple parsing can also be a separate function. Everything that doesn't directly depend on the templated |
||
} | ||
FRIEND_TEST(GraphStoreProtocolTest, transformPost); | ||
|
||
static ParsedQuery transformGet(const GraphOrDefault& graph) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a template, can be defined in .cpp, and in the header it can have acomment. |
||
ParsedQuery res; | ||
res._clause = parsedQuery::ConstructClause( | ||
{{Variable("?s"), Variable("?p"), Variable("?o")}}); | ||
res._rootGraphPattern = {}; | ||
parsedQuery::GraphPattern selectSPO; | ||
selectSPO._graphPatterns.emplace_back(parsedQuery::BasicGraphPattern{ | ||
{SparqlTriple(Variable("?s"), "?p", Variable("?o"))}}); | ||
if (std::holds_alternative<ad_utility::triple_component::Iri>(graph)) { | ||
parsedQuery::GroupGraphPattern selectSPOWithGraph{ | ||
std::move(selectSPO), | ||
std::get<ad_utility::triple_component::Iri>(graph)}; | ||
res._rootGraphPattern._graphPatterns.emplace_back( | ||
std::move(selectSPOWithGraph)); | ||
} else { | ||
AD_CORRECTNESS_CHECK(std::holds_alternative<DEFAULT>(graph)); | ||
res._rootGraphPattern = std::move(selectSPO); | ||
} | ||
Comment on lines
+78
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can't we store the rootGraphPattern directly in the first case? |
||
return res; | ||
} | ||
FRIEND_TEST(GraphStoreProtocolTest, transformGet); | ||
|
||
public: | ||
// Every Graph Store Protocol requests has equivalent SPARQL Query or Update. | ||
// Transform the Graph Store Protocol request into it's equivalent Query or | ||
// Update. | ||
static ParsedQuery transformGraphStoreProtocol( | ||
const ad_utility::httpUtils::HttpRequest auto& rawRequest) { | ||
ad_utility::url_parser::ParsedUrl parsedUrl = | ||
ad_utility::url_parser::parseRequestTarget(rawRequest.target()); | ||
GraphOrDefault graph = extractTargetGraph(parsedUrl.parameters_); | ||
|
||
using enum boost::beast::http::verb; | ||
auto method = rawRequest.method(); | ||
if (method == get) { | ||
return transformGet(graph); | ||
} else if (method == put) { | ||
throw std::runtime_error( | ||
"PUT in the SPARQL Graph Store HTTP Protocol is not yet implemented " | ||
"in QLever."); | ||
Comment on lines
+103
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can get rid of the code dupliccation (everything except for the method name is redundant, implement a lambda that does the throwing). |
||
} else if (method == delete_) { | ||
throw std::runtime_error( | ||
"DELETE in the SPARQL Graph Store HTTP Protocol is not yet " | ||
"implemented in QLever."); | ||
} else if (method == post) { | ||
return transformPost(rawRequest, graph); | ||
} else if (method == head) { | ||
throw std::runtime_error( | ||
"HEAD in the SPARQL Graph Store HTTP Protocol is not yet implemented " | ||
"in QLever."); | ||
} else if (method == patch) { | ||
throw std::runtime_error( | ||
"PATCH in the SPARQL Graph Store HTTP Protocol is not yet " | ||
"implemented in QLever."); | ||
} else { | ||
throw std::runtime_error( | ||
absl::StrCat("Unsupported HTTP method \"", | ||
std::string_view{rawRequest.method_string()}, | ||
"\" for the SPARQL Graph Store HTTP Protocol.")); | ||
} | ||
} | ||
|
||
private: | ||
// Extract the graph to be acted upon using `Indirect Graph | ||
// Identification`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please elaborate what |
||
static GraphOrDefault extractTargetGraph( | ||
const ad_utility::url_parser::ParamValueMap& params); | ||
FRIEND_TEST(GraphStoreProtocolTest, extractTargetGraph); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,6 +175,10 @@ class TripleComponent { | |
} | ||
[[nodiscard]] Variable& getVariable() { return std::get<Variable>(_variant); } | ||
|
||
bool isId() const { return std::holds_alternative<Id>(_variant); } | ||
[[nodiscard]] const Id& getId() const { return std::get<Id>(_variant); } | ||
[[nodiscard]] Id& getId() { return std::get<Id>(_variant); } | ||
Comment on lines
+179
to
+180
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't use nodiscard by default. |
||
|
||
/// Convert to an RDF literal. `std::strings` will be emitted directly, | ||
/// `int64_t` is converted to a `xsd:integer` literal, and a `double` is | ||
/// converted to a `xsd:double`. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ enum class MediaType { | |
tsv, | ||
csv, | ||
turtle, | ||
ntriples, | ||
octetStream | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant to comment in header file.