Repository: marmotta Updated Branches: refs/heads/develop e764c2bd0 -> 4a5e588cd
- support SPARQL ASK queries in ostrich Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/4a5e588c Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/4a5e588c Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/4a5e588c Branch: refs/heads/develop Commit: 4a5e588cd274794f7b7880cab49ebd88962cb72b Parents: e764c2b Author: Sebastian Schaffert <[email protected]> Authored: Sat Feb 13 15:25:58 2016 +0100 Committer: Sebastian Schaffert <[email protected]> Committed: Sat Feb 13 15:25:58 2016 +0100 ---------------------------------------------------------------------- libraries/ostrich/backend/client/client.cc | 53 ++++++++++++---- .../backend/persistence/leveldb_service.cc | 63 ++++++++++++++------ .../backend/persistence/leveldb_service.h | 4 ++ libraries/ostrich/backend/service/sparql.proto | 4 ++ .../ostrich/backend/sparql/rasqal_adapter.cc | 47 +++++++++++++-- .../ostrich/backend/sparql/rasqal_adapter.h | 6 ++ libraries/ostrich/backend/test/SparqlTest.cc | 20 +++++++ 7 files changed, 164 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/client/client.cc ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/client/client.cc b/libraries/ostrich/backend/client/client.cc index b4a5178..8921938 100644 --- a/libraries/ostrich/backend/client/client.cc +++ b/libraries/ostrich/backend/client/client.cc @@ -70,19 +70,13 @@ class ClientReaderIterator : public util::CloseableIterator<T> { public: ClientReaderIterator() : finished(true) { } - ClientReaderIterator(ClientReader<Proto>* r) : reader(r) { - finished = !reader->Read(&buffer); + ClientReaderIterator(ClientReader<Proto>* r) : reader(r), finished(false) { + read(); } const T& next() override { current_ = T(buffer); - - if (!finished) { - finished = !reader->Read(&buffer); - if (finished) { - reader->Finish(); - } - } + read(); return current_; } @@ -99,6 +93,16 @@ class ClientReaderIterator : public util::CloseableIterator<T> { Proto buffer; T current_; bool finished; + + void read() { + if (!finished) { + finished = !reader->Read(&buffer); + if (finished) { + auto st = reader->Finish(); + LOG_IF(FATAL, !st.ok()) << "Reading results failed: " << st.error_message(); + } + } + } }; typedef ClientReaderIterator<rdf::Statement, rdf::proto::Statement> StatementReader; @@ -214,6 +218,25 @@ class MarmottaClient { } } + void askQuery(const std::string& query, std::ostream &out) { + ClientContext context; + spq::SparqlRequest request; + request.set_query(query); + + google::protobuf::BoolValue result; + + Status status = sparql_->AskQuery(&context, request, &result); + if (status.ok()) { + if (result.value()) { + out << "YES" << std::endl; + } else { + out << "NO" << std::endl; + } + } else { + LOG(FATAL) << "SPARQL query failed: " << status.error_message(); + } + } + void listNamespaces(std::ostream &out) { ClientContext context; @@ -238,7 +261,7 @@ class MarmottaClient { if (status.ok()) { return result.value(); } else { - return -1; + LOG(FATAL) << "Calculating size failed: " << status.error_message(); } } private: @@ -318,6 +341,16 @@ int main(int argc, char** argv) { } } + if ("ask" == std::string(argv[1])) { + std::string query = argv[2]; + if (FLAGS_output != "") { + std::ofstream out(FLAGS_output); + client.askQuery(query, out); + } else { + client.askQuery(query, std::cout); + } + } + if ("delete" == std::string(argv[1])) { rdf::proto::Statement query; TextFormat::ParseFromString(argv[2], &query); http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/persistence/leveldb_service.cc ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/persistence/leveldb_service.cc b/libraries/ostrich/backend/persistence/leveldb_service.cc index 5ea08a1..f2637bd 100644 --- a/libraries/ostrich/backend/persistence/leveldb_service.cc +++ b/libraries/ostrich/backend/persistence/leveldb_service.cc @@ -22,6 +22,7 @@ #include <model/rdf_operators.h> #include <util/unique.h> #include <util/time_logger.h> +#include <glog/logging.h> using grpc::Status; using grpc::StatusCode; @@ -242,18 +243,23 @@ grpc::Status LevelDBSparqlService::TupleQuery( rdf::URI base_uri = query->base_uri(); - svc.TupleQuery(query->query(), base_uri, - [&result](const SparqlService::RowType& row) { - spq::SparqlResponse response; - for (auto it = row.cbegin(); it != row.cend(); it++) { - auto b = response.add_binding(); - b->set_variable(it->first); - *b->mutable_value() = it->second.getMessage(); - } - return result->Write(response); - }); - - return Status::OK; + try { + svc.TupleQuery(query->query(), base_uri, + [&result](const SparqlService::RowType& row) { + spq::SparqlResponse response; + for (auto it = row.cbegin(); it != row.cend(); it++) { + auto b = response.add_binding(); + b->set_variable(it->first); + *b->mutable_value() = it->second.getMessage(); + } + return result->Write(response); + }); + + return Status::OK; + } catch (sparql::SparqlException e) { + LOG(ERROR) << "SPARQL execution failed: " << e.what(); + return Status(StatusCode::INVALID_ARGUMENT, e.what()); + } } @@ -265,12 +271,35 @@ grpc::Status LevelDBSparqlService::GraphQuery(grpc::ServerContext* context, rdf::URI base_uri = query->base_uri(); - svc.GraphQuery(query->query(), base_uri, - [&result](const rdf::Statement& triple) { - return result->Write(triple.getMessage()); - }); + try { + svc.GraphQuery(query->query(), base_uri, + [&result](const rdf::Statement& triple) { + return result->Write(triple.getMessage()); + }); - return Status::OK; + return Status::OK; + } catch (sparql::SparqlException e) { + LOG(ERROR) << "SPARQL execution failed: " << e.what(); + return Status(StatusCode::INVALID_ARGUMENT, e.what()); + } +} + +grpc::Status LevelDBSparqlService::AskQuery(grpc::ServerContext* context, + const spq::SparqlRequest* query, + google::protobuf::BoolValue* result) { + + SparqlService svc(util::make_unique<LevelDBTripleSource>(persistence)); + + rdf::URI base_uri = query->base_uri(); + + try { + result->set_value(svc.AskQuery(query->query(), base_uri)); + + return Status::OK; + } catch (sparql::SparqlException e) { + LOG(ERROR) << "SPARQL execution failed: " << e.what(); + return Status(StatusCode::INVALID_ARGUMENT, e.what()); + } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/persistence/leveldb_service.h ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/persistence/leveldb_service.h b/libraries/ostrich/backend/persistence/leveldb_service.h index 44056a3..87b5b12 100644 --- a/libraries/ostrich/backend/persistence/leveldb_service.h +++ b/libraries/ostrich/backend/persistence/leveldb_service.h @@ -114,6 +114,10 @@ class LevelDBSparqlService : public spq::SparqlService::Service { grpc::Status GraphQuery(grpc::ServerContext* context, const spq::SparqlRequest* pattern, grpc::ServerWriter<rdf::proto::Statement>* result) override; + + grpc::Status AskQuery(grpc::ServerContext* context, + const spq::SparqlRequest* pattern, + google::protobuf::BoolValue* result) override; private: persistence::LevelDBPersistence* persistence; }; http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/service/sparql.proto ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/service/sparql.proto b/libraries/ostrich/backend/service/sparql.proto index 6d75a5f..e6da6d3 100644 --- a/libraries/ostrich/backend/service/sparql.proto +++ b/libraries/ostrich/backend/service/sparql.proto @@ -22,6 +22,7 @@ package marmotta.sparql.proto; option java_package = "org.apache.marmotta.ostrich.client.proto"; import "model.proto"; +import "google/protobuf/wrappers.proto"; // SPARQL request consisting of a single query string. message SparqlRequest { @@ -46,4 +47,7 @@ service SparqlService { // Execute a SPARQL 1.1 graph query and stream back the triples. rpc GraphQuery(SparqlRequest) returns (stream marmotta.rdf.proto.Statement); + + // Execute a SPARQL 1.1 ask query and return true or false. + rpc AskQuery(SparqlRequest) returns (google.protobuf.BoolValue); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/sparql/rasqal_adapter.cc ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/sparql/rasqal_adapter.cc b/libraries/ostrich/backend/sparql/rasqal_adapter.cc index 09e7bd6..1f6055b 100644 --- a/libraries/ostrich/backend/sparql/rasqal_adapter.cc +++ b/libraries/ostrich/backend/sparql/rasqal_adapter.cc @@ -62,7 +62,7 @@ std::string formatBindings(const std::map<std::string, rdf::Value>& bindings) { #endif void log_handler(void *user_data, raptor_log_message *message) { - LOG(ERROR) << "SPARQL Error(" << message->code << "): " << message->text; + LOG_IF(ERROR, message->code >= 0) << "SPARQL Error(" << message->code << "): " << message->text; } // Bind the current statement to the variables configured in the triple match. @@ -262,7 +262,7 @@ void SparqlService::TupleQuery(const std::string& query, const rdf::URI& base_ur if (rasqal_query_prepare(q, STR(query), base) != 0) { raptor_free_uri(base); rasqal_free_query(q); - throw SparqlException("Query preparation failed", query); + throw SparqlException("could not parse query", query); } bool next = true; @@ -270,14 +270,14 @@ void SparqlService::TupleQuery(const std::string& query, const rdf::URI& base_ur if (r == nullptr) { raptor_free_uri(base); rasqal_free_query(q); - throw SparqlException("Query execution failed", query); + throw SparqlException("query execution failed", query); } if (!rasqal_query_results_is_bindings(r)) { rasqal_free_query_results(r); rasqal_free_query(q); raptor_free_uri(base); - throw SparqlException("Query is not a tuple query", query); + throw SparqlException("query is not a tuple query", query); } int rowcount = 0; @@ -313,7 +313,7 @@ void SparqlService::GraphQuery(const std::string& query, const rdf::URI& base_ur if (rasqal_query_prepare(q, STR(query), base) != 0) { raptor_free_uri(base); rasqal_free_query(q); - throw SparqlException("Query preparation failed", query); + throw SparqlException("could not parse query", query); } bool next = true; @@ -328,7 +328,7 @@ void SparqlService::GraphQuery(const std::string& query, const rdf::URI& base_ur rasqal_free_query_results(r); rasqal_free_query(q); raptor_free_uri(base); - throw SparqlException("Query is not a graph query", query); + throw SparqlException("query is not a graph query", query); } while (next) { @@ -341,6 +341,41 @@ void SparqlService::GraphQuery(const std::string& query, const rdf::URI& base_ur raptor_free_uri(base); } +bool SparqlService::AskQuery(const std::string& query, const rdf::URI& base_uri) { + util::TimeLogger timeLogger("SPARQL ask query"); + + auto q = rasqal_new_query(world, "sparql11-query", nullptr); + auto base = raptor_new_uri(rasqal_world_get_raptor(world), + STR(base_uri.getUri())); + if (rasqal_query_prepare(q, STR(query), base) != 0) { + raptor_free_uri(base); + rasqal_free_query(q); + throw SparqlException("could not parse query", query); + } + + auto r = rasqal_query_execute(q); + if (r == nullptr || rasqal_query_results_get_boolean(r) < 0) { + raptor_free_uri(base); + rasqal_free_query(q); + throw SparqlException("query execution failed", query); + } + + if (!rasqal_query_results_is_boolean(r)) { + rasqal_free_query_results(r); + rasqal_free_query(q); + raptor_free_uri(base); + throw SparqlException("query is not a boolean query", query); + } + + bool result = rasqal_query_results_get_boolean(r) > 0; + + rasqal_free_query_results(r); + rasqal_free_query(q); + raptor_free_uri(base); + + return result; +} + } // namespace sparql } // namespace marmotta http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/sparql/rasqal_adapter.h ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/sparql/rasqal_adapter.h b/libraries/ostrich/backend/sparql/rasqal_adapter.h index 32e82d2..2f75887 100644 --- a/libraries/ostrich/backend/sparql/rasqal_adapter.h +++ b/libraries/ostrich/backend/sparql/rasqal_adapter.h @@ -98,6 +98,12 @@ class SparqlService { void GraphQuery(const std::string& query, const rdf::URI& base_uri, std::function<bool(const rdf::Statement&)> stmt_handler); + + /** + * Execute a boolean (ASK) query, returning the boolean result. + */ + bool AskQuery(const std::string& query, const rdf::URI& base_uri); + /** * Return a reference to the triple source managed by this service. */ http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a5e588c/libraries/ostrich/backend/test/SparqlTest.cc ---------------------------------------------------------------------- diff --git a/libraries/ostrich/backend/test/SparqlTest.cc b/libraries/ostrich/backend/test/SparqlTest.cc index 0f663f5..a45a282 100644 --- a/libraries/ostrich/backend/test/SparqlTest.cc +++ b/libraries/ostrich/backend/test/SparqlTest.cc @@ -267,5 +267,25 @@ TEST(SPARQLTest, Graph) { } +TEST(SPARQLTest, AskTrue) { + rdf::Statement stmt = rdf::Statement(rdf::URI("http://example.com/s1"), + rdf::URI("http://example.com/p1"), + rdf::URI("http://example.com/o1")); + SparqlService svc(std::unique_ptr<TripleSource>(new MockTripleSource({stmt}))); + + EXPECT_TRUE(svc.AskQuery("ASK {}", base_uri)); +} + + +TEST(SPARQLTest, AskFalse) { + rdf::Statement stmt = rdf::Statement(rdf::URI("http://example.com/s1"), + rdf::URI("http://example.com/p1"), + rdf::URI("http://example.com/o1")); + SparqlService svc(std::unique_ptr<TripleSource>(new MockTripleSource({stmt}))); + + EXPECT_FALSE(svc.AskQuery("ASK { <http://example.com/s2> ?p ?o}", base_uri)); +} + + } // namespace sparql } // namespace marmotta \ No newline at end of file
