Query stripping now uses a parse tree and differentiates between int literals in a range expression (not stripped) and outside of a range (stripped).

Summary: See above

Reviewers: buda, mislav.bradac

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D98
This commit is contained in:
florijan 2017-03-08 14:11:05 +01:00
parent 00d818c762
commit 971e006d13
35 changed files with 523 additions and 181 deletions

View File

@ -346,6 +346,7 @@ set(memgraph_src_files
${src_dir}/logging/log.cpp
${src_dir}/database/graph_db.cpp
${src_dir}/database/graph_db_accessor.cpp
${src_dir}/query/stripper.cpp
${src_dir}/query/backend/cpp/cypher_main_visitor.cpp
${src_dir}/query/backend/cpp/typed_value.cpp
)

74
src/query/parameters.hpp Normal file
View File

@ -0,0 +1,74 @@
//
// Copyright 2017 Memgraph
// Created by Florijan Stamenkovic on 08.03.17.
//
#ifndef MEMGRAPH_PARAMETERS_HPP
#define MEMGRAPH_PARAMETERS_HPP
#include <query/backend/cpp/typed_value.hpp>
/**
* Encapsulates user provided parameters (and stripped literals)
* and provides ways of obtaining them by name or position.
*/
struct Parameters {
public:
/**
* Adds a value to the stripped arguments under a sequentially
* generated name and returns a reference to that name.
*
* @param value
* @return
*/
const std::string &Add(const TypedValue &value) {
return storage_.emplace(NextName(), value).first->first;
}
/**
* Returns the value found for the given name.
* The name MUST be present in this container
* (this is asserted).
*
* @param name Param name.
* @return Value for the given param.
*/
const TypedValue &At(const std::string &name) const {
auto found = storage_.find(name);
permanent_assert(found != storage_.end(),
"Name must be present in stripped arg container");
return found->second;
}
/**
* Returns the position-th stripped value. Asserts that this
* container has at least (position + 1) elements.
*
* This is future proofing for when both query params and
* stripping will be supported and naming collisions will have to
* be avoided.
*
* @param position Which stripped param is sought.
* @return Stripped param.
*/
const TypedValue &At(const size_t position) const {
permanent_assert(position < storage_.size(), "Invalid position");
return storage_.find(NameForPosition(position))->second;
}
/** Returns the number of arguments in this container */
const size_t Size() const { return storage_.size(); }
private:
std::map<std::string, TypedValue> storage_;
/** Generates and returns a new name */
std::string NextName() const { return NameForPosition(storage_.size()); }
/** Returns a name for positon */
std::string NameForPosition(unsigned long position) const {
return "stripped_arg_" + std::to_string(position);
}
};
#endif //MEMGRAPH_PARAMETERS_HPP

View File

@ -1,6 +1,7 @@
#pragma once
#include "database/graph_db_accessor.hpp"
#include "query/stripped.hpp"
/**
@ -18,12 +19,12 @@ class PlanInterface {
* concrete execution plan.
*
* @param db_accessor Accessor for ihe database.
* @param args plan argument (vector of literal values from the query)
* @param args Plan arguments (including literals stripped from the query).
* @param stream stream for results writing
*
* @return bool status after execution (success OR fail)
*/
virtual bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
virtual bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) = 0;
/**

View File

@ -31,14 +31,10 @@ class QueryPreprocessor : public Loggable {
* @return QueryStripped object
*/
auto preprocess(const std::string &query) {
auto preprocessed = stripper.strip(query);
auto preprocessed = query::Strip(query);
logger.info("stripped_query = {}", preprocessed.query);
logger.info("query_hash = {}", preprocessed.hash);
return preprocessed;
}
private:
QueryStripper stripper;
};

View File

@ -1,7 +1,13 @@
#pragma once
#include <map>
#include "query/backend/cpp/typed_value.hpp"
#include "storage/property_value_store.hpp"
#include "utils/assert.hpp"
#include "utils/hashing/fnv.hpp"
#include "parameters.hpp"
/*
* StrippedQuery contains:
@ -10,14 +16,13 @@
* * hash of stripped query
*/
struct StrippedQuery {
StrippedQuery(const std::string &&query, PropertyValueStore<> &&arguments,
StrippedQuery(const std::string &&query,
const Parameters &arguments,
HashType hash)
: query(std::forward<const std::string>(query)),
arguments(std::forward<PropertyValueStore<>>(arguments)),
hash(hash) {}
: query(query), arguments(arguments), hash(hash) {}
/**
* Copy constructor is deleted because we don't want to make unecessary
* Copy constructor is deleted because we don't want to make unnecessary
* copies of this object (copying of string and vector could be expensive)
*/
StrippedQuery(const StrippedQuery &other) = delete;
@ -38,7 +43,7 @@ struct StrippedQuery {
/**
* Stripped arguments
*/
const PropertyValueStore<> arguments;
const Parameters arguments;
/**
* Hash based on stripped query.

221
src/query/stripper.cpp Normal file
View File

@ -0,0 +1,221 @@
//
// Copyright 2017 Memgraph
// Created by Florijan Stamenkovic on 07.03.17.
//
#include <iostream>
#include <string>
#include <vector>
#include "logging/loggable.hpp"
#include "query/stripper.hpp"
#include "utils/hashing/fnv.hpp"
#include "utils/assert.hpp"
#include "query/frontend/opencypher/generated/CypherBaseVisitor.h"
#include "query/frontend/opencypher/generated/CypherLexer.h"
#include "query/frontend/opencypher/generated/CypherParser.h"
using namespace antlr4;
using namespace antlropencypher;
namespace query {
/**
* A visitor that for each literal that is not enclosed
* by a range literal calls the given callback (which should
* then replace Tokens of literals with placeholders).
*/
class StripperVisitor : public antlropencypher::CypherBaseVisitor {
public:
/**
* @param callback Callback function (see class description) called
* with start and stop tokens of a literal.
*/
StripperVisitor(const std::function<void(Token *, Token *)> callback)
: callback_(callback) {}
antlrcpp::Any visitRangeLiteral(
CypherParser::RangeLiteralContext *ctx) override {
is_in_range_ = true;
auto r_val = visitChildren(ctx);
is_in_range_ = false;
return r_val;
}
antlrcpp::Any visitLiteral(
CypherParser::LiteralContext *ctx) override {
if (ctx->booleanLiteral() != nullptr ||
ctx->StringLiteral() != nullptr ||
ctx->numberLiteral() != nullptr)
callback_(ctx->getStart(), ctx->getStop());
is_in_literal_ = true;
auto r_val = visitChildren(ctx);
is_in_literal_ = false;
return r_val;
}
antlrcpp::Any visitIntegerLiteral(
CypherParser::IntegerLiteralContext *ctx) override {
// convert integer literals into param placeholders only if not in range
// literal
if (!is_in_range_ && !is_in_literal_) callback_(ctx->getStart(), ctx->getStop());
return visitChildren(ctx);
}
private:
const std::function<void(Token *, Token *)> callback_;
bool is_in_range_{false};
bool is_in_literal_{false};
};
/**
* Strips the input query and returns stripped query, stripped arguments and
* stripped query hash.
*
* @param query input query
* @return stripped query, stripped arguments and stripped query hash as a
* single object of class StrippedQuery
*/
StrippedQuery Strip(const std::string &query) {
// tokenize the query
ANTLRInputStream input(query);
CypherLexer lexer(&input);
CommonTokenStream token_stream(&lexer);
token_stream.fill();
std::vector<Token *> tokens = token_stream.getTokens();
// initialize data structures we return
Parameters stripped_arguments;
// convert tokens to strings, perform lowercasing and filtering
std::vector<std::string> token_strings;
token_strings.reserve(tokens.size());
for (int i = 0; i < tokens.size(); ++i)
switch (tokens[i]->getType()) {
case CypherLexer::UNION:
case CypherLexer::ALL:
case CypherLexer::OPTIONAL:
case CypherLexer::MATCH:
case CypherLexer::UNWIND:
case CypherLexer::AS:
case CypherLexer::MERGE:
case CypherLexer::ON:
case CypherLexer::CREATE:
case CypherLexer::SET:
case CypherLexer::DETACH:
case CypherLexer::DELETE:
case CypherLexer::REMOVE:
case CypherLexer::WITH:
case CypherLexer::DISTINCT:
case CypherLexer::RETURN:
case CypherLexer::ORDER:
case CypherLexer::BY:
case CypherLexer::L_SKIP:
case CypherLexer::LIMIT:
case CypherLexer::ASCENDING:
case CypherLexer::ASC:
case CypherLexer::DESCENDING:
case CypherLexer::DESC:
case CypherLexer::WHERE:
case CypherLexer::OR:
case CypherLexer::XOR:
case CypherLexer::AND:
case CypherLexer::NOT:
case CypherLexer::IN:
case CypherLexer::STARTS:
case CypherLexer::ENDS:
case CypherLexer::CONTAINS:
case CypherLexer::IS:
case CypherLexer::CYPHERNULL:
case CypherLexer::COUNT:
case CypherLexer::FILTER:
case CypherLexer::EXTRACT:
case CypherLexer::ANY:
case CypherLexer::NONE:
case CypherLexer::SINGLE:
token_strings.push_back(tokens[i]->getText());
std::transform(token_strings.back().begin(),
token_strings.back().end(),
token_strings.back().begin(), ::tolower);
break;
case CypherLexer::SP:
case Token::EOF:
token_strings.push_back("");
break;
default:
token_strings.push_back(tokens[i]->getText());
break;
}
// a helper function that generates a new param name for the stripped
// literal, appends is to the the stripped_query and adds the passed
// value to stripped args
auto replace_stripped = [&stripped_arguments, &token_strings](
const TypedValue &value, size_t token_position) {
const auto &stripped_name = stripped_arguments.Add(value);
token_strings[token_position] = "$" + stripped_name;
};
// callback for every literal that should be changed
// TODO consider literal parsing problems (like an int with 100 digits)
auto callback = [&replace_stripped](Token *start, Token *end) {
assert(start->getTokenIndex() == end->getTokenIndex());
switch (start->getType()) {
case CypherLexer::DecimalInteger:
replace_stripped(std::stoi(start->getText()),
start->getTokenIndex());
break;
case CypherLexer::HexInteger:
replace_stripped(std::stoi(start->getText(), 0, 16),
start->getTokenIndex());
break;
case CypherLexer::OctalInteger:
replace_stripped(std::stoi(start->getText(), 0, 8),
start->getTokenIndex());
break;
case CypherLexer::StringLiteral:
replace_stripped(start->getText(),
start->getTokenIndex());
break;
case CypherLexer::RegularDecimalReal:
case CypherLexer::ExponentDecimalReal:
replace_stripped(std::stof(start->getText()),
start->getTokenIndex());
break;
case CypherLexer::TRUE:
replace_stripped(true, start->getTokenIndex());
break;
case CypherLexer::FALSE:
replace_stripped(false, start->getTokenIndex());
break;
default:
permanent_assert(true, "Unsupported literal type");
}
};
// parse the query and visit the AST with a stripping visitor
CypherParser parser(&token_stream);
tree::ParseTree *tree = parser.cypher();
StripperVisitor stripper_visitor(callback);
stripper_visitor.visit(tree);
// concatenate the stripped query tokens
std::string stripped_query;
stripped_query.reserve(query.size());
for (const std::string &token_string : token_strings) {
stripped_query += token_string;
if (token_string.size() > 0)
stripped_query += " ";
}
// return stripped query, stripped arguments and stripped query hash
return StrippedQuery(std::move(stripped_query),
std::move(stripped_arguments),
fnv(stripped_query));
}
};

View File

@ -1,104 +1,7 @@
#pragma once
#include <iostream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include "logging/loggable.hpp"
#include "query/stripped.hpp"
#include "storage/property_value_store.hpp"
#include "utils/hashing/fnv.hpp"
#include "utils/string/transform.hpp"
#include "utils/variadic/variadic.hpp"
#include "antlr4-runtime.h"
#include "query/frontend/opencypher/generated/CypherLexer.h"
#include "query/frontend/opencypher/generated/CypherParser.h"
using namespace antlr4;
using namespace antlropencypher;
/**
* @brief QueryStripper
*
* Strips the input query and returns stripped query, stripped arguments and
* stripped query hash.
*/
class QueryStripper : public Loggable {
public:
QueryStripper() : Loggable("QueryStripper") {}
QueryStripper(const QueryStripper &) = delete;
QueryStripper(QueryStripper &&) = delete;
QueryStripper &operator=(const QueryStripper &) = delete;
QueryStripper &operator=(QueryStripper &&) = delete;
/**
* Strips the input query (openCypher query).
*
* @param query input query
* @param separator char that is added between all literals in the stripped
* query because after stripping compiler frontend will use the stripped
* query to AST and literals have to be separated
*
* @return stripped query, stripped arguments and stripped query hash as a
* single object of class StrippedQuery
*/
auto strip(const std::string &query, const std::string &separator = " ") {
// generate tokens
ANTLRInputStream input(query);
CypherLexer lexer(&input);
CommonTokenStream tokens(&lexer);
// initialize data structures that will be populated
PropertyValueStore<> stripped_arguments;
std::string stripped_query;
stripped_query.reserve(query.size());
// iterate through tokens -> take arguments and generate stripped query
tokens.fill();
int counter = 0;
for (auto token : tokens.getTokens()) {
int type = token->getType();
if (type == CypherLexer::DecimalInteger ||
type == CypherLexer::StringLiteral ||
type == CypherLexer::RegularDecimalReal) { // TODO: add others
switch (type) {
case CypherLexer::DecimalInteger:
stripped_arguments.set(counter, std::stoi(token->getText()));
break;
case CypherLexer::StringLiteral:
stripped_arguments.set(counter, token->getText());
break;
case CypherLexer::RegularDecimalReal:
stripped_arguments.set(counter, std::stof(token->getText()));
break;
}
stripped_query += std::to_string(counter++) + separator;
} else if (type == CypherLexer::MATCH || type == CypherLexer::CREATE ||
type == CypherLexer::RETURN || type == CypherLexer::DELETE ||
type == CypherLexer::SET ||
type == CypherLexer::WHERE) { // TODO: add others
auto value = token->getText();
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
stripped_query += value + separator;
} else if (type == CypherLexer::SP || type < 0 ||
type > 113) { // SP || EOF // TODO: add others if exist
// pass
} else {
stripped_query += token->getText() + separator;
}
}
// calculate query hash
HashType hash = fnv(stripped_query);
// return stripped query, stripped arguments and stripped query hash
return StrippedQuery(std::move(stripped_query),
std::move(stripped_arguments), hash);
}
namespace query {
StrippedQuery Strip(const std::string &query);
};

View File

@ -31,8 +31,7 @@ int main(int argc, char **argv) {
for (auto &test : tests) {
auto *benchmark =
benchmark::RegisterBenchmark(test.c_str(), BM_Strip, preprocess, test)
->RangeMultiplier(2)
->Range(1, 8 << 10)
->Range(1, 1)
->Complexity(benchmark::oN);
}

View File

@ -17,7 +17,7 @@ benchmark_queries:
- "MATCH (u:User) WITH {key: u} AS nodes DELETE nodes.key"
- "CREATE ()-[:T {id: 42, alive: true, name: kifla, height=4.2}]->()"
- "CREATE ()-[:T {id: 42, alive: true, name: kifla, height: 4.2}]->()"
- "MATCH p = ()-[r:T]-() WHERE r.id = 42 DELETE r"

View File

@ -9,6 +9,7 @@
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "utils/assert.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -20,7 +21,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, CliqueQuery::FIND_ALL);
}

View File

@ -9,6 +9,7 @@
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "utils/assert.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -116,7 +117,7 @@ class Bitset {
enum CliqueQuery { SCORE_AND_LIMIT, FIND_ALL };
bool run_general_query(GraphDbAccessor &db_accessor,
const PropertyValueStore<> &args, Stream &stream,
const Parameters &args, Stream &stream,
enum CliqueQuery query_type) {
if (query_type == CliqueQuery::FIND_ALL)
stream.write_fields(
@ -129,7 +130,8 @@ bool run_general_query(GraphDbAccessor &db_accessor,
// happening in code review!!!
auto vertices_iterator = db_accessor.vertices();
auto edge_iterator = db_accessor.edges();
std::vector<VertexAccessor> vertices(vertices_iterator.begin(), vertices_iterator.end());
std::vector<VertexAccessor> vertices(vertices_iterator.begin(),
vertices_iterator.end());
std::vector<EdgeAccessor> edges(edge_iterator.begin(), edge_iterator.end());
std::vector<VertexAccessor *> vertices_indexed;
@ -142,11 +144,11 @@ bool run_general_query(GraphDbAccessor &db_accessor,
if (query_type == CliqueQuery::SCORE_AND_LIMIT &&
vertices[i].has_label(db_accessor.label("profile"))) {
auto has_prop =
vertices[i].PropsAt(db_accessor.property("profile_id")) == args.at(0);
vertices[i].PropsAt(db_accessor.property("profile_id")) == args.At(0);
if (has_prop.type() == TypedValue::Type::Null) continue;
if (has_prop.Value<bool>() == false) continue;
has_prop =
vertices[i].PropsAt(db_accessor.property("partner_id")) == args.at(1);
vertices[i].PropsAt(db_accessor.property("partner_id")) == args.At(1);
if (has_prop.type() == TypedValue::Type::Null) continue;
if (has_prop.Value<bool>() == false) continue;
profile_index = i;
@ -184,8 +186,7 @@ bool run_general_query(GraphDbAccessor &db_accessor,
* @param bitset bitset to update.
* @param edges edges from which to update bitset.
*/
auto update = [&db_accessor, &query](Bitset<int64_t> &bitset,
auto &&edges) {
auto update = [&db_accessor, &query](Bitset<int64_t> &bitset, auto &&edges) {
for (auto e : edges) {
if (e.edge_type() != db_accessor.edge_type("default_outfit")) continue;
const int from = query(e.from());
@ -208,7 +209,7 @@ bool run_general_query(GraphDbAccessor &db_accessor,
for (int i = 0; i < n; ++i) {
const VertexAccessor v = *vertices_indexed[i];
auto cmp_res = v.PropsAt(db_accessor.property("garment_id")) ==
args.at(query_type == CliqueQuery::SCORE_AND_LIMIT ? 8 : 0);
args.At(query_type == CliqueQuery::SCORE_AND_LIMIT ? 8 : 0);
if (cmp_res.type() != TypedValue::Type::Bool) continue;
if (cmp_res.Value<bool>() != true) continue;
auto neigh = connected[i].Ones();
@ -288,7 +289,7 @@ bool run_general_query(GraphDbAccessor &db_accessor,
reverse(results.begin(), results.end());
}
const int limit = query_type == CliqueQuery::SCORE_AND_LIMIT
? args.at((int)args.size() - 1).Value<int>()
? args.At((int)args.Size() - 1).Value<int>()
: (int)results.size();
for (int i = 0; i < std::min(limit, (int)results.size()); ++i) {
stream.write_record();

View File

@ -9,6 +9,7 @@
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "utils/assert.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -26,7 +27,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream,
CliqueQuery::SCORE_AND_LIMIT);

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,12 +15,12 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
v.PropsSet(db_accessor.property("partner_id"), args.at(1));
v.PropsSet(db_accessor.property("conceals"), args.at(2));
v.PropsSet(db_accessor.property("profile_id"), args.At(0));
v.PropsSet(db_accessor.property("partner_id"), args.At(1));
v.PropsSet(db_accessor.property("conceals"), args.At(2));
v.add_label(db_accessor.label("profile"));
stream.write_field("p");
stream.write_vertex_record(v);

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -13,11 +14,11 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
v.PropsSet(db_accessor.property("partner_id"), args.at(1));
v.PropsSet(db_accessor.property("profile_id"), args.At(0));
v.PropsSet(db_accessor.property("partner_id"), args.At(1));
v.add_label(db_accessor.label("profile"));
stream.write_field("p");
stream.write_vertex_record(v);

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,12 +15,12 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.PropsSet(db_accessor.property("profile_id"), args.at(0));
v.PropsSet(db_accessor.property("partner_id"), args.at(1));
v.PropsSet(db_accessor.property("reveals"), args.at(2));
v.PropsSet(db_accessor.property("profile_id"), args.At(0));
v.PropsSet(db_accessor.property("partner_id"), args.At(1));
v.PropsSet(db_accessor.property("reveals"), args.At(2));
v.add_label(db_accessor.label("profile"));
stream.write_field("p");
stream.write_vertex_record(v);

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -13,12 +14,12 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.add_label(db_accessor.label("garment"));
v.PropsSet(db_accessor.property("garment_id"), args.at(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.at(1));
v.PropsSet(db_accessor.property("garment_id"), args.At(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.At(1));
stream.write_field("g");
stream.write_vertex_record(v);
stream.write_meta("rw");

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,13 +15,13 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.add_label(db_accessor.label("garment"));
v.PropsSet(db_accessor.property("garment_id"), args.at(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.at(1));
v.PropsSet(db_accessor.property("conceals"), args.at(2));
v.PropsSet(db_accessor.property("garment_id"), args.At(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.At(1));
v.PropsSet(db_accessor.property("conceals"), args.At(2));
stream.write_field("g");
stream.write_vertex_record(v);
stream.write_meta("rw");

View File

@ -5,6 +5,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,13 +15,13 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
auto v = db_accessor.insert_vertex();
v.add_label(db_accessor.label("garment"));
v.PropsSet(db_accessor.property("garment_id"), args.at(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.at(1));
v.PropsSet(db_accessor.property("reveals"), args.at(2));
v.PropsSet(db_accessor.property("garment_id"), args.At(0));
v.PropsSet(db_accessor.property("garment_category_id"), args.At(1));
v.PropsSet(db_accessor.property("reveals"), args.At(2));
stream.write_field("g");
stream.write_vertex_record(v);
stream.write_meta("rw");

View File

@ -1,8 +1,11 @@
#include <iostream>
#include <string>
#include "query/backend/cpp/typed_value.hpp"
#include "query/plan_interface.hpp"
#include "query/stripped.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -11,7 +14,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
for (auto v : db_accessor.vertices()) db_accessor.detach_remove_vertex(v);
stream.write_empty_fields();

View File

@ -6,6 +6,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,14 +15,14 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("g");
for (auto vertex : db_accessor.vertices()) {
if (vertex.has_label(db_accessor.label("garment"))) {
TypedValue prop = vertex.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == TypedValue::Type::Null) continue;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
stream.write_vertex_record(vertex);

View File

@ -6,6 +6,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -15,7 +16,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("r");
std::vector<VertexAccessor> g1_set, g2_set;
@ -23,7 +24,7 @@ class CPUPlan : public PlanInterface<Stream> {
if (g1.has_label(db_accessor.label("garment"))) {
TypedValue prop = g1.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == TypedValue::Type::Null) continue;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
g1_set.push_back(g1);
@ -33,7 +34,7 @@ class CPUPlan : public PlanInterface<Stream> {
if (g2.has_label(db_accessor.label("garment"))) {
auto prop = g2.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == PropertyValue::Type::Null) continue;
auto cmp = prop == args.at(1);
auto cmp = prop == args.At(1);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
g2_set.push_back(g2);

View File

@ -4,6 +4,7 @@
#include "match_garment_set_label_general_return.hpp"
#include "query/plan_interface.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -12,7 +13,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, "AA");
}

View File

@ -4,6 +4,7 @@
#include "match_garment_set_label_general_return.hpp"
#include "query/plan_interface.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -12,7 +13,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, "BB");
}

View File

@ -4,6 +4,7 @@
#include "match_garment_set_label_general_return.hpp"
#include "query/plan_interface.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -12,7 +13,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, "CC");
}

View File

@ -4,6 +4,7 @@
#include "match_garment_set_label_general_return.hpp"
#include "query/plan_interface.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -12,7 +13,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, "DD");
}

View File

@ -4,6 +4,7 @@
#include "match_garment_set_label_general_return.hpp"
#include "query/plan_interface.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -12,7 +13,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
return run_general_query(db_accessor, args, stream, "EE");
}

View File

@ -3,6 +3,7 @@
#include "query/backend/cpp/typed_value.hpp"
#include "query/plan_interface.hpp"
#include "query/parameters.hpp"
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
@ -14,14 +15,14 @@ using std::endl;
// RETURN g
bool run_general_query(GraphDbAccessor &db_accessor,
const PropertyValueStore<> &args, Stream &stream,
const Parameters &args, Stream &stream,
const std::string &general_label) {
stream.write_field("g");
for (auto vertex : db_accessor.vertices()) {
if (vertex.has_label(db_accessor.label("garment"))) {
TypedValue prop = vertex.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == TypedValue::Type::Null) continue;
TypedValue cmp = prop == args.at(0);
TypedValue cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
vertex.add_label(db_accessor.label(general_label));

View File

@ -6,6 +6,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -14,20 +15,20 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("p");
for (auto vertex : db_accessor.vertices()) {
if (vertex.has_label(db_accessor.label("profile"))) {
TypedValue prop = vertex.PropsAt(db_accessor.property("profile_id"));
if (prop.type() == TypedValue::Type::Null) continue;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
TypedValue prop2 = vertex.PropsAt(db_accessor.property("partner_id"));
if (prop2.type() == TypedValue::Type::Null) continue;
auto cmp2 = prop2 == args.at(1);
auto cmp2 = prop2 == args.At(1);
if (cmp2.type() != TypedValue::Type::Bool) continue;
if (cmp2.Value<bool>() != true) continue;
stream.write_vertex_record(vertex);

View File

@ -3,9 +3,11 @@
#include "query/backend/cpp/typed_value.hpp"
#include "query/plan_interface.hpp"
#include "query/stripped.hpp"
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -16,26 +18,26 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("s");
auto profile = [&db_accessor, &args](const VertexAccessor &v) -> bool {
TypedValue prop = v.PropsAt(db_accessor.property("profile_id"));
if (prop.type() == TypedValue::Type::Null) return false;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) return false;
if (cmp.Value<bool>() != true) return false;
TypedValue prop2 = v.PropsAt(db_accessor.property("partner_id"));
if (prop2.type() == TypedValue::Type::Null) return false;
auto cmp2 = prop2 == args.at(1);
auto cmp2 = prop2 == args.At(1);
if (cmp2.type() != TypedValue::Type::Bool) return false;
return cmp2.Value<bool>();
};
auto garment = [&db_accessor, &args](const VertexAccessor &v) -> bool {
TypedValue prop = v.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == TypedValue::Type::Null) return false;
auto cmp = prop == args.at(2);
auto cmp = prop == args.At(2);
if (cmp.type() != TypedValue::Type::Bool) return false;
return cmp.Value<bool>();
};

View File

@ -6,6 +6,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -15,7 +16,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("r");
std::vector<VertexAccessor> g1_set, g2_set;
@ -23,13 +24,13 @@ class CPUPlan : public PlanInterface<Stream> {
if (g1.has_label(db_accessor.label("profile"))) {
auto prop = TypedValue(g1.PropsAt(db_accessor.property("profile_id")));
if (prop.type() == TypedValue::Type::Null) continue;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
auto prop2 = TypedValue(g1.PropsAt(db_accessor.property("partner_id")));
if (prop2.type() == TypedValue::Type::Null) continue;
auto cmp2 = prop2 == args.at(1);
auto cmp2 = prop2 == args.At(1);
if (cmp2.type() != TypedValue::Type::Bool) continue;
if (cmp2.Value<bool>() != true) continue;
g1_set.push_back(g1);
@ -39,7 +40,7 @@ class CPUPlan : public PlanInterface<Stream> {
if (g2.has_label(db_accessor.label("garment"))) {
auto prop = TypedValue(g2.PropsAt(db_accessor.property("garment_id")));
if (prop.type() == TypedValue::Type::Null) continue;
auto cmp = prop == args.at(2);
auto cmp = prop == args.At(2);
if (cmp.type() != TypedValue::Type::Bool) continue;
if (cmp.Value<bool>() != true) continue;
g2_set.push_back(g2);
@ -49,7 +50,7 @@ class CPUPlan : public PlanInterface<Stream> {
for (auto g2 : g2_set) {
EdgeAccessor e =
db_accessor.insert_edge(g1, g2, db_accessor.edge_type("score"));
e.PropsSet(db_accessor.property("score"), args.at(3));
e.PropsSet(db_accessor.property("score"), args.At(3));
stream.write_edge_record(e);
}
stream.write_meta("rw");

View File

@ -6,6 +6,7 @@
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
#include "using.hpp"
#include "query/parameters.hpp"
using std::cout;
using std::endl;
@ -16,26 +17,26 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const Parameters &args,
Stream &stream) {
stream.write_field("s");
auto profile = [&db_accessor, &args](const VertexAccessor &v) -> bool {
TypedValue prop = v.PropsAt(db_accessor.property("profile_id"));
if (prop.type() == TypedValue::Type::Null) return false;
auto cmp = prop == args.at(0);
auto cmp = prop == args.At(0);
if (cmp.type() != TypedValue::Type::Bool) return false;
if (cmp.Value<bool>() != true) return false;
TypedValue prop2 = v.PropsAt(db_accessor.property("partner_id"));
if (prop2.type() == TypedValue::Type::Null) return false;
auto cmp2 = prop2 == args.at(1);
auto cmp2 = prop2 == args.At(1);
if (cmp2.type() != TypedValue::Type::Bool) return false;
return cmp2.Value<bool>();
};
auto garment = [&db_accessor, &args](const VertexAccessor &v) -> bool {
TypedValue prop = v.PropsAt(db_accessor.property("garment_id"));
if (prop.type() == TypedValue::Type::Null) return false;
auto cmp = prop == args.at(2);
auto cmp = prop == args.At(2);
if (cmp.type() != TypedValue::Type::Bool) return false;
return cmp.Value<bool>();
};
@ -44,7 +45,7 @@ class CPUPlan : public PlanInterface<Stream> {
auto to = edge.to();
if (edge.edge_type() != db_accessor.edge_type("score")) continue;
if ((profile(from) && garment(to)) || (profile(to) && garment(from))) {
edge.PropsSet(db_accessor.property("score"), args.at(3));
edge.PropsSet(db_accessor.property("score"), args.At(3));
stream.write_edge_record(edge);
}
}

View File

@ -12,7 +12,7 @@ using std::endl;
class CPUPlan : public PlanInterface<Stream> {
public:
bool run(GraphDbAccessor &db_accessor, const PropertyValueStore<> &args,
bool run(GraphDbAccessor &db_accessor, const StrippedArguments &args,
Stream &stream) {
db_accessor.commit();
return true;

View File

@ -36,9 +36,8 @@ int main(int argc, char **argv) {
println("Stripped query: ", preprocessed.query);
println("Query hash: ", preprocessed.hash);
println("Property values:");
for (auto property : preprocessed.arguments) {
println(" ", property.second);
}
for (int i = 0; i < preprocessed.arguments.Size(); ++i)
println(" ", preprocessed.arguments.At(i));
println("");
return 0;

View File

@ -0,0 +1,28 @@
//
// Copyright 2017 Memgraph
// Created by Florijan Stamenkovic on 07.03.17.
//
#include <ctime>
#include <iostream>
#include "query/stripper.hpp"
int main(int argc, const char **a) {
if (argc < 2) {
std::cout << "Provide a query string as input" << std::endl;
return 1;
}
const char *query = a[1];
const int REPEATS = 100;
clock_t begin = clock();
for (int i = 0; i < REPEATS; ++i)
query::Strip(query);
clock_t end = clock();
double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
std::cout << "Performed " << REPEATS << " strip ops, each took "
<< elapsed_secs / REPEATS * 1000 << "ms" << std::endl;
}

View File

@ -0,0 +1,90 @@
//
// Copyright 2017 Memgraph
// Created by Florijan Stamenkovic on 07.03.17.
//
#include "gtest/gtest.h"
#include "query/stripper.hpp"
void EXPECT_PROP_TRUE(const TypedValue& a) {
EXPECT_TRUE(a.type() == TypedValue::Type::Bool && a.Value<bool>());
}
void EXPECT_PROP_EQ(const TypedValue& a, const TypedValue& b) {
EXPECT_PROP_TRUE(a == b);
}
TEST(QueryStripper, NoLiterals) {
StrippedQuery stripped = query::Strip("CREATE (n)");
EXPECT_EQ(stripped.arguments.Size(), 0);
EXPECT_EQ(stripped.query, "create ( n ) ");
}
TEST(QueryStripper, DecimalInteger) {
StrippedQuery stripped = query::Strip("RETURN 42");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_EQ(stripped.arguments.At(0).Value<int>(), 42);
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, OctalInteger) {
StrippedQuery stripped = query::Strip("RETURN 010");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_EQ(stripped.arguments.At(0).Value<int>(), 8);
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, HexInteger) {
StrippedQuery stripped = query::Strip("RETURN 0xa");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_EQ(stripped.arguments.At(0).Value<int>(), 10);
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, RegularDecimal) {
StrippedQuery stripped = query::Strip("RETURN 42.3");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_FLOAT_EQ(stripped.arguments.At(0).Value<double>(), 42.3);
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, ExponentDecimal) {
StrippedQuery stripped = query::Strip("RETURN 4e2");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_FLOAT_EQ(stripped.arguments.At(0).Value<double>(), 4e2);
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, StringLiteral) {
StrippedQuery stripped = query::Strip("RETURN 'something'");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_EQ(stripped.arguments.At(0).Value<std::string>(), "'something'");
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, BoolLiteral) {
StrippedQuery stripped = query::Strip("RETURN true");
EXPECT_EQ(stripped.arguments.Size(), 1);
EXPECT_PROP_EQ(stripped.arguments.At(0), TypedValue(true));
EXPECT_EQ(stripped.query, "return $stripped_arg_0 ");
}
TEST(QueryStripper, ListLiteral) {
StrippedQuery stripped = query::Strip("MATCH (n) RETURN [n, n.prop]");
EXPECT_EQ(stripped.arguments.Size(), 0);
EXPECT_EQ(stripped.query, "match ( n ) return [ n , n . prop ] ");
}
TEST(QueryStripper, MapLiteral) {
StrippedQuery stripped = query::Strip("MATCH (n) RETURN {val: n}");
EXPECT_EQ(stripped.arguments.Size(), 0);
EXPECT_EQ(stripped.query, "match ( n ) return { val : n } ");
}
TEST(QueryStripper, RangeLiteral) {
StrippedQuery stripped = query::Strip("MATCH (n)-[*2..3]-() RETURN n");
EXPECT_EQ(stripped.arguments.Size(), 0);
EXPECT_EQ(stripped.query, "match ( n ) - [ * 2 .. 3 ] - ( ) return n ");
}