#pragma once #include #include #include #include #include #include "cypher/cypher.h" #include "logging/loggable.hpp" #include "query/language/cypher/tokenizer/cypher_lexer.hpp" #include "query/strip/stripped.hpp" #include "storage/model/properties/all.hpp" #include "utils/hashing/fnv.hpp" #include "utils/string/transform.hpp" #include "utils/variadic/variadic.hpp" // TODO: Maybe std::move(v) is faster, but it must be cheked for validity. template void store_query_param(plan_args_t &arguments, V &&v) { arguments.emplace_back(Property(T(std::move(v)), T::type)); } template class QueryStripper : public Loggable { public: QueryStripper(Ts &&... strip_types) : Loggable("QueryStripper"), strip_types(std::make_tuple(std::forward(strip_types)...)), lexer(std::make_unique()) { } QueryStripper(QueryStripper &other) = delete; QueryStripper(QueryStripper &&other) : Loggable("QueryStripper"), strip_types(std::move(other.strip_types)), lexer(std::move(other.lexer)) { } auto strip_space(const std::string &query) { return strip(query, " "); } auto strip(const std::string &query, const std::string &separator = "") { // ------------------------------------------------------------------- // TODO: write speed tests and then optimize, because this // function is called before every query execution ! // ------------------------------------------------------------------- // TODO write this more optimal (resplace string // concatenation with something smarter) // TODO: in place substring replacement auto tokenizer = lexer->tokenize(query); // TMP size of supported token types constexpr auto size = std::tuple_size::value; int counter = 0; plan_args_t stripped_arguments; std::string stripped_query; stripped_query.reserve(query.size()); while (auto token = tokenizer.lookup()) { if (_or(token.id, strip_types, std::make_index_sequence{})) { auto index = counter++; switch (token.id) { case TK_LONG: store_query_param(stripped_arguments, std::stol(token.value)); break; case TK_STR: // TODO: remove quotes view lexertl token.value.erase(0, 1); token.value.erase(token.value.length() - 1, 1); // TODO: remove store_query_param(stripped_arguments, token.value); break; case TK_BOOL: { bool value = token.value[0] == 'T' || token.value[0] == 't'; store_query_param(stripped_arguments, value); break; } case TK_FLOAT: store_query_param(stripped_arguments, std::stof(token.value)); break; default: // TODO: other properties assert(false); } stripped_query += std::to_string(index) + separator; } else { // if token is keyword then lowercase because query hash // should be the same // TODO: probably we shoud do the lowercase before // or during the tokenization (SPEED TESTS) if (token.id == TK_OR || token.id == TK_AND || token.id == TK_NOT || token.id == TK_WITH || token.id == TK_SET || token.id == TK_CREATE || token.id == TK_MERGE || token.id == TK_MATCH || token.id == TK_DELETE || token.id == TK_DETACH || token.id == TK_WHERE || token.id == TK_RETURN || token.id == TK_DISTINCT || token.id == TK_COUNT || token.id == TK_LABELS) { std::transform(token.value.begin(), token.value.end(), token.value.begin(), ::tolower); } stripped_query += token.value + separator; } } // TODO: hash function should be a template parameter auto hash = fnv(stripped_query); return QueryStripped(std::move(stripped_query), std::move(stripped_arguments), hash); } private: std::tuple strip_types; CypherLexer::uptr lexer; template bool _or(Value &&value, Tuple &&tuple, std::index_sequence) { return utils::or_vargs(std::forward(value), std::get(std::forward(tuple))...); } }; template decltype(auto) make_query_stripper(Ts &&... ts) { return QueryStripper(std::forward(ts)...); }