Implement filtering capabilities for ScanAll (#578)

ScanVerticesrequest was not able to utilize filtering capabilities
before. With these modification it is now able to filter the scanned
vertices based on the filter_expressions field in the
ScanVerticesRequest message type.
This commit is contained in:
gvolfing 2022-10-19 16:09:00 +02:00 committed by GitHub
parent 6bb40a7f49
commit 85b8ce9101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 490 additions and 167 deletions

View File

@ -21,6 +21,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "expr/typed_value_exception.hpp"
#include "utils/algorithm.hpp" #include "utils/algorithm.hpp"
#include "utils/exceptions.hpp" #include "utils/exceptions.hpp"
#include "utils/fnv.hpp" #include "utils/fnv.hpp"
@ -32,16 +33,6 @@
namespace memgraph::expr { namespace memgraph::expr {
/**
* An exception raised by the TypedValue system. Typically when
* trying to perform operations (such as addition) on TypedValues
* of incompatible Types.
*/
class TypedValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
// TODO: Neo4j does overflow checking. Should we also implement it? // TODO: Neo4j does overflow checking. Should we also implement it?
/** /**
* Stores a query runtime value and its type. * Stores a query runtime value and its type.

View File

@ -0,0 +1,26 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include "utils/exceptions.hpp"
namespace memgraph::expr {
/**
* An exception raised by the TypedValue system. Typically when
* trying to perform operations (such as addition) on TypedValues
* of incompatible Types.
*/
class TypedValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
} // namespace memgraph::expr

View File

@ -202,46 +202,8 @@ Value ToBoltValue(msgs::Value value) {
return Value{std::move(map)}; return Value{std::move(map)};
} }
case msgs::Value::Type::Vertex: case msgs::Value::Type::Vertex:
case msgs::Value::Type::Edge: case msgs::Value::Type::Edge: {
case msgs::Value::Type::Path: { throw utils::BasicException("Vertex and Edge not supported!");
throw utils::BasicException("Vertex, Edge and Path are not supported!");
}
}
}
Value ToBoltValue(msgs::Value value, const coordinator::ShardMap & /*shard_map*/, storage::v3::View /*view*/) {
switch (value.type) {
case msgs::Value::Type::Null:
return {};
case msgs::Value::Type::Bool:
return {value.bool_v};
case msgs::Value::Type::Int64:
return {value.int_v};
case msgs::Value::Type::Double:
return {value.double_v};
case msgs::Value::Type::String:
return {std::string(value.string_v)};
case msgs::Value::Type::List: {
std::vector<Value> values;
values.reserve(value.list_v.size());
for (const auto &v : value.list_v) {
auto maybe_value = ToBoltValue(v);
values.emplace_back(std::move(maybe_value));
}
return Value{std::move(values)};
}
case msgs::Value::Type::Map: {
std::map<std::string, Value> map;
for (const auto &kv : value.map_v) {
auto maybe_value = ToBoltValue(kv.second);
map.emplace(kv.first, std::move(maybe_value));
}
return Value{std::move(map)};
}
case msgs::Value::Type::Vertex:
case msgs::Value::Type::Edge:
case msgs::Value::Type::Path: {
throw utils::BasicException("Vertex, Edge and Path are not supported!");
} }
} }
} }

View File

@ -11,5 +11,9 @@
#pragma once #pragma once
#ifdef MG_AST_INCLUDE_PATH
#error You are probably trying to include files from expr from both the storage and query engines! You will have a rough time kid!
#endif
#define MG_AST_INCLUDE_PATH "query/v2/frontend/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage) #define MG_AST_INCLUDE_PATH "query/v2/frontend/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage)
#define MG_INJECTED_NAMESPACE_NAME memgraph::query::v2 // NOLINT(cppcoreguidelines-macro-usage) #define MG_INJECTED_NAMESPACE_NAME memgraph::query::v2 // NOLINT(cppcoreguidelines-macro-usage)

View File

@ -50,8 +50,6 @@ inline TypedValue ValueToTypedValue(const msgs::Value &value) {
return TypedValue(accessors::VertexAccessor(value.vertex_v, {})); return TypedValue(accessors::VertexAccessor(value.vertex_v, {}));
case Value::Type::Edge: case Value::Type::Edge:
return TypedValue(accessors::EdgeAccessor(value.edge_v, {})); return TypedValue(accessors::EdgeAccessor(value.edge_v, {}));
case Value::Type::Path:
break;
} }
throw std::runtime_error("Incorrect type in conversion"); throw std::runtime_error("Incorrect type in conversion");
} }
@ -91,7 +89,10 @@ inline msgs::Value TypedValueToValue(const TypedValue &value) {
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
return Value(value.ValueEdge().GetEdge()); return Value(value.ValueEdge().GetEdge());
case TypedValue::Type::Path: case TypedValue::Type::Path:
default: case TypedValue::Type::LocalTime:
case TypedValue::Type::LocalDateTime:
case TypedValue::Type::Date:
case TypedValue::Type::Duration:
break; break;
} }
throw std::runtime_error("Incorrect type in conversion"); throw std::runtime_error("Incorrect type in conversion");

View File

@ -22,9 +22,9 @@
#include <variant> #include <variant>
#include <vector> #include <vector>
#include "expr/semantic/symbol.hpp"
#include "query/v2/common.hpp" #include "query/v2/common.hpp"
#include "query/v2/frontend/ast/ast.hpp" #include "query/v2/frontend/ast/ast.hpp"
#include "expr/semantic/symbol.hpp"
#include "query/v2/bindings/typed_value.hpp" #include "query/v2/bindings/typed_value.hpp"
#include "query/v2/bindings/frame.hpp" #include "query/v2/bindings/frame.hpp"
#include "query/v2/bindings/symbol_table.hpp" #include "query/v2/bindings/symbol_table.hpp"

View File

@ -76,16 +76,6 @@ struct Vertex {
friend bool operator==(const Vertex &lhs, const Vertex &rhs) { return lhs.id == rhs.id; } friend bool operator==(const Vertex &lhs, const Vertex &rhs) { return lhs.id == rhs.id; }
}; };
struct PathPart {
Vertex dst;
Gid edge;
};
struct Path {
Vertex src;
std::vector<PathPart> parts;
};
struct Null {}; struct Null {};
struct Value { struct Value {
@ -135,13 +125,9 @@ struct Value {
case Type::Map: case Type::Map:
std::destroy_at(&map_v); std::destroy_at(&map_v);
return; return;
case Type::Vertex: case Type::Vertex:
std::destroy_at(&vertex_v); std::destroy_at(&vertex_v);
return; return;
case Type::Path:
std::destroy_at(&path_v);
return;
case Type::Edge: case Type::Edge:
std::destroy_at(&edge_v); std::destroy_at(&edge_v);
} }
@ -175,9 +161,6 @@ struct Value {
case Type::Edge: case Type::Edge:
new (&edge_v) Edge(other.edge_v); new (&edge_v) Edge(other.edge_v);
return; return;
case Type::Path:
new (&path_v) Path(other.path_v);
return;
} }
} }
@ -209,9 +192,6 @@ struct Value {
case Type::Edge: case Type::Edge:
new (&edge_v) Edge(std::move(other.edge_v)); new (&edge_v) Edge(std::move(other.edge_v));
break; break;
case Type::Path:
new (&path_v) Path(std::move(other.path_v));
break;
} }
other.DestroyValue(); other.DestroyValue();
@ -251,9 +231,6 @@ struct Value {
case Type::Edge: case Type::Edge:
new (&edge_v) Edge(other.edge_v); new (&edge_v) Edge(other.edge_v);
break; break;
case Type::Path:
new (&path_v) Path(other.path_v);
break;
} }
return *this; return *this;
@ -292,9 +269,6 @@ struct Value {
case Type::Edge: case Type::Edge:
new (&edge_v) Edge(std::move(other.edge_v)); new (&edge_v) Edge(std::move(other.edge_v));
break; break;
case Type::Path:
new (&path_v) Path(std::move(other.path_v));
break;
} }
other.DestroyValue(); other.DestroyValue();
@ -302,7 +276,7 @@ struct Value {
return *this; return *this;
} }
enum class Type : uint8_t { Null, Bool, Int64, Double, String, List, Map, Vertex, Edge, Path }; enum class Type : uint8_t { Null, Bool, Int64, Double, String, List, Map, Vertex, Edge };
Type type{Type::Null}; Type type{Type::Null};
union { union {
Null null_v; Null null_v;
@ -314,7 +288,6 @@ struct Value {
std::map<std::string, Value> map_v; std::map<std::string, Value> map_v;
Vertex vertex_v; Vertex vertex_v;
Edge edge_v; Edge edge_v;
Path path_v;
}; };
friend bool operator==(const Value &lhs, const Value &rhs) { friend bool operator==(const Value &lhs, const Value &rhs) {
@ -340,8 +313,6 @@ struct Value {
return lhs.vertex_v == rhs.vertex_v; return lhs.vertex_v == rhs.vertex_v;
case Value::Type::Edge: case Value::Type::Edge:
return lhs.edge_v == rhs.edge_v; return lhs.edge_v == rhs.edge_v;
case Value::Type::Path:
return true;
} }
} }
}; };
@ -381,18 +352,22 @@ struct ScanVerticesRequest {
Hlc transaction_id; Hlc transaction_id;
VertexId start_id; VertexId start_id;
std::optional<std::vector<PropertyId>> props_to_return; std::optional<std::vector<PropertyId>> props_to_return;
// expression that determines if vertex is returned or not
std::vector<std::string> filter_expressions;
// expression whose result is returned for every vertex
std::vector<std::string> vertex_expressions;
std::optional<size_t> batch_limit; std::optional<size_t> batch_limit;
StorageView storage_view{StorageView::NEW}; StorageView storage_view{StorageView::NEW};
std::optional<Label> label; std::optional<Label> label;
std::optional<std::pair<PropertyId, std::string>> property_expression_pair; std::optional<std::pair<PropertyId, std::string>> property_expression_pair;
std::optional<std::vector<std::string>> filter_expressions;
}; };
struct ScanResultRow { struct ScanResultRow {
Vertex vertex; Vertex vertex;
// empty() is no properties returned // empty() is no properties returned
std::vector<std::pair<PropertyId, Value>> props; std::vector<std::pair<PropertyId, Value>> props;
std::vector<Value> evaluated_vertex_expressions;
}; };
struct ScanVerticesResponse { struct ScanVerticesResponse {

View File

@ -11,5 +11,9 @@
#pragma once #pragma once
#ifdef MG_AST_INCLUDE_PATH
#error You are probably trying to include some files of mg-expr from both the storage and query engines! You will have a rough time kid!
#endif
#define MG_AST_INCLUDE_PATH "storage/v3/bindings/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage) #define MG_AST_INCLUDE_PATH "storage/v3/bindings/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage)
#define MG_INJECTED_NAMESPACE_NAME memgraph::storage::v3 // NOLINT(cppcoreguidelines-macro-usage) #define MG_INJECTED_NAMESPACE_NAME memgraph::storage::v3 // NOLINT(cppcoreguidelines-macro-usage)

View File

@ -0,0 +1,19 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include "storage/v3/bindings/bindings.hpp"
#include "expr/ast/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/typed_value.hpp"
namespace memgraph::storage::v3 {} // namespace memgraph::storage::v3

View File

@ -9,7 +9,7 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#include "expr/typed_value.hpp" #include "expr/typed_value_exception.hpp"
#include "query/v2/requests.hpp" #include "query/v2/requests.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "utils/memory.hpp" #include "utils/memory.hpp"

View File

@ -15,6 +15,7 @@
#include <cstddef> #include <cstddef>
#include <ranges> #include <ranges>
#include "common/types.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
@ -78,6 +79,39 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
return std::nullopt; return std::nullopt;
} }
[[nodiscard]] std::optional<SchemaViolation> SchemaValidator::ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const {
// Schema on primary label
const auto *schema = schemas_.GetSchema(primary_label);
if (schema == nullptr) {
return SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, primary_label);
}
// Is there another primary label among secondary labels
for (const auto &secondary_label : labels) {
if (schemas_.GetSchema(secondary_label)) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, secondary_label);
}
}
// Quick size check
if (schema->second.size() != primary_properties.size()) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label);
}
// Check only properties defined by schema
for (size_t i{0}; i < schema->second.size(); ++i) {
// Check schema property type
if (auto property_schema_type = PropertyTypeToSchemaType(primary_properties[i]);
property_schema_type && *property_schema_type != schema->second[i].type) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label,
schema->second[i], primary_properties[i]);
}
}
return std::nullopt;
}
[[nodiscard]] std::optional<SchemaViolation> SchemaValidator::ValidatePropertyUpdate( [[nodiscard]] std::optional<SchemaViolation> SchemaValidator::ValidatePropertyUpdate(
const LabelId primary_label, const PropertyId property_id) const { const LabelId primary_label, const PropertyId property_id) const {
// Verify existence of schema on primary label // Verify existence of schema on primary label
@ -103,6 +137,8 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
return std::nullopt; return std::nullopt;
} }
const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_.GetSchema(label); }
VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label) VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label)
: schema_validator{&schema_validator}, primary_label_{primary_label} {} : schema_validator{&schema_validator}, primary_label_{primary_label} {}

View File

@ -29,6 +29,7 @@ struct SchemaViolation {
VERTEX_UPDATE_PRIMARY_KEY, VERTEX_UPDATE_PRIMARY_KEY,
VERTEX_UPDATE_PRIMARY_LABEL, VERTEX_UPDATE_PRIMARY_LABEL,
VERTEX_SECONDARY_LABEL_IS_PRIMARY, VERTEX_SECONDARY_LABEL_IS_PRIMARY,
VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
}; };
SchemaViolation(ValidationStatus status, LabelId label); SchemaViolation(ValidationStatus status, LabelId label);
@ -50,15 +51,21 @@ class SchemaValidator {
public: public:
explicit SchemaValidator(Schemas &schemas); explicit SchemaValidator(Schemas &schemas);
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate( [[deprecated]] std::optional<SchemaViolation> ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels, LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) const; const std::vector<std::pair<PropertyId, PropertyValue>> &properties) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const;
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(LabelId primary_label, [[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(LabelId primary_label,
PropertyId property_id) const; PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const; [[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const;
const Schemas::Schema *GetSchema(LabelId label) const;
private: private:
Schemas &schemas_; Schemas &schemas_;
}; };

View File

@ -12,6 +12,7 @@
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
@ -392,15 +393,9 @@ ResultSchema<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) { const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
OOMExceptionEnabler oom_exception; OOMExceptionEnabler oom_exception;
const auto schema = shard_->GetSchema(shard_->primary_label_)->second; const auto schema = shard_->GetSchema(shard_->primary_label_)->second;
std::vector<std::pair<PropertyId, PropertyValue>> primary_properties_ordered;
// TODO(jbajic) Maybe react immediately and send Violation
MG_ASSERT("PrimaryKey is invalid size");
for (auto i{0}; i < schema.size(); ++i) {
primary_properties_ordered.emplace_back(schema[i].property_id, primary_properties[i]);
}
auto maybe_schema_violation = auto maybe_schema_violation =
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties_ordered); GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties);
if (maybe_schema_violation) { if (maybe_schema_violation) {
return {std::move(*maybe_schema_violation)}; return {std::move(*maybe_schema_violation)};
} }

View File

@ -9,18 +9,31 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#include <algorithm>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <utility> #include <utility>
#include "parser/opencypher/parser.hpp" #include "parser/opencypher/parser.hpp"
#include "query/v2/requests.hpp" #include "query/v2/requests.hpp"
#include "storage/v3/bindings/ast/ast.hpp"
#include "storage/v3/bindings/cypher_main_visitor.hpp"
#include "storage/v3/bindings/db_accessor.hpp"
#include "storage/v3/bindings/eval.hpp"
#include "storage/v3/bindings/frame.hpp"
#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/symbol_generator.hpp"
#include "storage/v3/bindings/symbol_table.hpp"
#include "storage/v3/bindings/typed_value.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/key_store.hpp" #include "storage/v3/key_store.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
#include "storage/v3/shard_rsm.hpp" #include "storage/v3/shard_rsm.hpp"
#include "storage/v3/storage.hpp"
#include "storage/v3/value_conversions.hpp" #include "storage/v3/value_conversions.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
#include "storage/v3/view.hpp"
using memgraph::msgs::Label; using memgraph::msgs::Label;
using memgraph::msgs::PropertyId; using memgraph::msgs::PropertyId;
@ -30,8 +43,11 @@ using memgraph::msgs::VertexId;
using memgraph::storage::conversions::ConvertPropertyVector; using memgraph::storage::conversions::ConvertPropertyVector;
using memgraph::storage::conversions::ConvertValueVector; using memgraph::storage::conversions::ConvertValueVector;
using memgraph::storage::conversions::FromPropertyValueToValue;
using memgraph::storage::conversions::ToPropertyValue; using memgraph::storage::conversions::ToPropertyValue;
using memgraph::storage::conversions::ToValue; using memgraph::storage::v3::View;
namespace memgraph::storage::v3 {
namespace { namespace {
std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ConvertPropertyMap( std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ConvertPropertyMap(
@ -39,9 +55,10 @@ std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::
std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ret; std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ret;
ret.reserve(properties.size()); ret.reserve(properties.size());
for (auto &[key, value] : properties) { std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()),
ret.emplace_back(std::make_pair(key, ToPropertyValue(std::move(value)))); std::back_inserter(ret), [](std::pair<PropertyId, Value> &&property) {
} return std::make_pair(property.first, ToPropertyValue(std::move(property.second)));
});
return ret; return ret;
} }
@ -51,16 +68,15 @@ std::vector<std::pair<memgraph::storage::v3::PropertyId, Value>> FromMap(
std::vector<std::pair<memgraph::storage::v3::PropertyId, Value>> ret; std::vector<std::pair<memgraph::storage::v3::PropertyId, Value>> ret;
ret.reserve(properties.size()); ret.reserve(properties.size());
for (const auto &[key, value] : properties) { std::transform(properties.begin(), properties.end(), std::back_inserter(ret),
ret.emplace_back(std::make_pair(key, value)); [](const auto &property) { return std::make_pair(property.first, property.second); });
}
return ret; return ret;
} }
std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor( std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(
const memgraph::storage::v3::VertexAccessor &acc, const std::vector<memgraph::storage::v3::PropertyId> &props, const memgraph::storage::v3::VertexAccessor &acc, const std::vector<memgraph::storage::v3::PropertyId> &props,
memgraph::storage::v3::View view) { View view) {
std::map<PropertyId, Value> ret; std::map<PropertyId, Value> ret;
for (const auto &prop : props) { for (const auto &prop : props) {
@ -74,7 +90,7 @@ std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor
spdlog::debug("The specified property does not exist but it should"); spdlog::debug("The specified property does not exist but it should");
return std::nullopt; return std::nullopt;
} }
ret.emplace(std::make_pair(prop, ToValue(value))); ret.emplace(std::make_pair(prop, FromPropertyValueToValue(value)));
} }
return ret; return ret;
@ -89,9 +105,11 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
spdlog::debug("Encountered an error while trying to get vertex properties."); spdlog::debug("Encountered an error while trying to get vertex properties.");
return std::nullopt; return std::nullopt;
} }
for (const auto &[prop_key, prop_val] : props.GetValue()) {
ret.emplace(prop_key, ToValue(prop_val)); const auto &properties = props.GetValue();
} std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()), [](const auto &property) {
return std::make_pair(property.first, FromPropertyValueToValue(property.second));
});
auto maybe_pk = acc.PrimaryKey(view); auto maybe_pk = acc.PrimaryKey(view);
if (maybe_pk.HasError()) { if (maybe_pk.HasError()) {
@ -101,31 +119,217 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
const auto pk = maybe_pk.GetValue(); const auto pk = maybe_pk.GetValue();
MG_ASSERT(schema->second.size() == pk.size(), "PrimaryKey size does not match schema!"); MG_ASSERT(schema->second.size() == pk.size(), "PrimaryKey size does not match schema!");
for (size_t i{0}; i < schema->second.size(); ++i) { for (size_t i{0}; i < schema->second.size(); ++i) {
ret.emplace(schema->second[i].property_id, ToValue(pk[i])); ret.emplace(schema->second[i].property_id, FromPropertyValueToValue(pk[i]));
} }
return ret; return ret;
} }
Value ConstructValueVertex(const memgraph::storage::v3::VertexAccessor &acc, memgraph::storage::v3::View view) { memgraph::msgs::Value ConstructValueVertex(const memgraph::storage::v3::VertexAccessor &acc, View view) {
// Get the vertex id // Get the vertex id
auto prim_label = acc.PrimaryLabel(view).GetValue(); auto prim_label = acc.PrimaryLabel(view).GetValue();
Label value_label{.id = prim_label}; memgraph::msgs::Label value_label{.id = prim_label};
auto prim_key = ConvertValueVector(acc.PrimaryKey(view).GetValue()); auto prim_key = ConvertValueVector(acc.PrimaryKey(view).GetValue());
VertexId vertex_id = std::make_pair(value_label, prim_key); memgraph::msgs::VertexId vertex_id = std::make_pair(value_label, prim_key);
// Get the labels // Get the labels
auto vertex_labels = acc.Labels(view).GetValue(); auto vertex_labels = acc.Labels(view).GetValue();
std::vector<Label> value_labels; std::vector<memgraph::msgs::Label> value_labels;
for (const auto &label : vertex_labels) { value_labels.reserve(vertex_labels.size());
Label l = {.id = label};
value_labels.push_back(l); std::transform(vertex_labels.begin(), vertex_labels.end(), std::back_inserter(value_labels),
} [](const auto &label) { return msgs::Label{.id = label}; });
return Value({.id = vertex_id, .labels = value_labels}); return Value({.id = vertex_id, .labels = value_labels});
} }
Value ConstructValueEdge(const memgraph::storage::v3::EdgeAccessor &acc, View view) {
memgraph::msgs::EdgeType type = {.id = acc.EdgeType().AsUint()};
memgraph::msgs::EdgeId gid = {.gid = acc.Gid().AsUint()};
Label src_prim_label = {.id = acc.FromVertex().primary_label};
memgraph::msgs::VertexId src_vertex =
std::make_pair(src_prim_label, ConvertValueVector(acc.FromVertex().primary_key));
Label dst_prim_label = {.id = acc.ToVertex().primary_label};
memgraph::msgs::VertexId dst_vertex = std::make_pair(dst_prim_label, ConvertValueVector(acc.ToVertex().primary_key));
std::optional<std::vector<std::pair<PropertyId, Value>>> properties_opt = {};
const auto &properties = acc.Properties(view);
if (properties.HasValue()) {
const auto &props = properties.GetValue();
std::vector<std::pair<PropertyId, Value>> present_properties;
present_properties.reserve(props.size());
std::transform(props.begin(), props.end(), std::back_inserter(present_properties),
[](const auto &prop) { return std::make_pair(prop.first, FromPropertyValueToValue(prop.second)); });
properties_opt = std::move(present_properties);
}
return Value({.src = src_vertex, .dst = dst_vertex, .properties = properties_opt, .id = gid, .type = type});
}
Value FromTypedValueToValue(memgraph::storage::v3::TypedValue &&tv) {
using memgraph::storage::v3::TypedValue;
switch (tv.type()) {
case TypedValue::Type::Bool:
return Value(tv.ValueBool());
case TypedValue::Type::Double:
return Value(tv.ValueDouble());
case TypedValue::Type::Int:
return Value(tv.ValueInt());
case TypedValue::Type::List: {
std::vector<Value> list;
auto &tv_list = tv.ValueList();
list.reserve(tv_list.size());
std::transform(tv_list.begin(), tv_list.end(), std::back_inserter(list),
[](auto &elem) { return FromTypedValueToValue(std::move(elem)); });
return Value(list);
}
case TypedValue::Type::Map: {
std::map<std::string, Value> map;
for (auto &[key, val] : tv.ValueMap()) {
map.emplace(key, FromTypedValueToValue(std::move(val)));
}
return Value(map);
}
case TypedValue::Type::Null:
return Value{};
case TypedValue::Type::String:
return Value((std::string(tv.ValueString())));
case TypedValue::Type::Vertex:
return ConstructValueVertex(tv.ValueVertex(), View::OLD);
case TypedValue::Type::Edge:
return ConstructValueEdge(tv.ValueEdge(), View::OLD);
// TBD -> we need to specify temporal types, not a priority.
case TypedValue::Type::Date:
case TypedValue::Type::LocalTime:
case TypedValue::Type::LocalDateTime:
case TypedValue::Type::Duration:
case TypedValue::Type::Path: {
MG_ASSERT(false, "This conversion between TypedValue and Value is not implemented yet!");
break;
}
}
return Value{};
}
std::vector<Value> ConvertToValueVectorFromTypedValueVector(std::vector<memgraph::storage::v3::TypedValue> &&vec) {
std::vector<Value> ret;
ret.reserve(vec.size());
std::transform(vec.begin(), vec.end(), std::back_inserter(ret),
[](auto &elem) { return FromTypedValueToValue(std::move(elem)); });
return ret;
}
std::vector<PropertyId> NamesToProperties(const std::vector<std::string> &property_names, DbAccessor &dba) {
std::vector<PropertyId> properties;
properties.reserve(property_names.size());
for (const auto &name : property_names) {
properties.push_back(dba.NameToProperty(name));
}
return properties;
}
std::vector<memgraph::storage::v3::LabelId> NamesToLabels(const std::vector<std::string> &label_names,
DbAccessor &dba) {
std::vector<memgraph::storage::v3::LabelId> labels;
labels.reserve(label_names.size());
for (const auto &name : label_names) {
labels.push_back(dba.NameToLabel(name));
}
return labels;
}
template <class TExpression>
auto Eval(TExpression *expr, EvaluationContext &ctx, AstStorage &storage,
memgraph::storage::v3::ExpressionEvaluator &eval, DbAccessor &dba) {
ctx.properties = NamesToProperties(storage.properties_, dba);
ctx.labels = NamesToLabels(storage.labels_, dba);
auto value = expr->Accept(eval);
return value;
}
std::any ParseExpression(const std::string &expr, memgraph::expr::AstStorage &storage) {
memgraph::frontend::opencypher::Parser<memgraph::frontend::opencypher::ParserOpTag::EXPRESSION> parser(expr);
ParsingContext pc;
CypherMainVisitor visitor(pc, &storage);
auto *ast = parser.tree();
return visitor.visit(ast);
}
TypedValue ComputeExpression(DbAccessor &dba, const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc,
const std::optional<memgraph::storage::v3::EdgeAccessor> &e_acc,
const std::string &expression, std::string_view node_name, std::string_view edge_name) {
AstStorage storage;
Frame frame{1 + 1}; // 1 for the node_identifier, 1 for the edge_identifier
SymbolTable symbol_table;
EvaluationContext ctx;
ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, View::OLD};
auto expr = ParseExpression(expression, storage);
auto node_identifier = Identifier(std::string(node_name), false);
auto edge_identifier = Identifier(std::string(edge_name), false);
std::vector<Identifier *> identifiers;
identifiers.push_back(&node_identifier);
identifiers.push_back(&edge_identifier);
expr::SymbolGenerator symbol_generator(&symbol_table, identifiers);
(std::any_cast<Expression *>(expr))->Accept(symbol_generator);
if (node_identifier.symbol_pos_ != -1) {
MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(),
[&node_name](const std::pair<int32_t, Symbol> &position_symbol_pair) {
return position_symbol_pair.second.name() == node_name;
}) != symbol_table.table().end());
frame[symbol_table.at(node_identifier)] = *v_acc;
}
if (edge_identifier.symbol_pos_ != -1) {
MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(),
[&edge_name](const std::pair<int32_t, Symbol> &position_symbol_pair) {
return position_symbol_pair.second.name() == edge_name;
}) != symbol_table.table().end());
frame[symbol_table.at(edge_identifier)] = *e_acc;
}
return Eval(std::any_cast<Expression *>(expr), ctx, storage, eval, dba);
}
bool FilterOnVertex(DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc,
const std::vector<std::string> &filters, const std::string_view node_name) {
return std::ranges::all_of(filters, [&node_name, &dba, &v_acc](const auto &filter_expr) {
auto res = ComputeExpression(dba, v_acc, std::nullopt, filter_expr, node_name, "");
return res.IsBool() && res.ValueBool();
});
}
std::vector<memgraph::storage::v3::TypedValue> EvaluateVertexExpressions(
DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &expressions,
std::string_view node_name) {
std::vector<memgraph::storage::v3::TypedValue> evaluated_expressions;
evaluated_expressions.reserve(expressions.size());
std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions),
[&dba, &v_acc, &node_name](const auto &expression) {
return ComputeExpression(dba, v_acc, std::nullopt, expression, node_name, "");
});
return evaluated_expressions;
}
bool DoesEdgeTypeMatch(const memgraph::msgs::ExpandOneRequest &req, const memgraph::storage::v3::EdgeAccessor &edge) { bool DoesEdgeTypeMatch(const memgraph::msgs::ExpandOneRequest &req, const memgraph::storage::v3::EdgeAccessor &edge) {
// TODO(gvolfing) This should be checked only once and handled accordingly. // TODO(gvolfing) This should be checked only once and handled accordingly.
if (req.edge_types.empty()) { if (req.edge_types.empty()) {
@ -143,19 +347,21 @@ struct LocalError {};
std::optional<memgraph::msgs::Vertex> FillUpSourceVertex( std::optional<memgraph::msgs::Vertex> FillUpSourceVertex(
const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc, memgraph::msgs::ExpandOneRequest &req, const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc, memgraph::msgs::ExpandOneRequest &req,
memgraph::msgs::VertexId src_vertex) { memgraph::msgs::VertexId src_vertex) {
auto secondary_labels = v_acc->Labels(memgraph::storage::v3::View::OLD); auto secondary_labels = v_acc->Labels(View::OLD);
if (secondary_labels.HasError()) { if (secondary_labels.HasError()) {
spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}", spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
return std::nullopt; return std::nullopt;
} }
auto &sec_labels = secondary_labels.GetValue();
memgraph::msgs::Vertex source_vertex; memgraph::msgs::Vertex source_vertex;
source_vertex.id = src_vertex; source_vertex.id = src_vertex;
source_vertex.labels.reserve(secondary_labels.GetValue().size()); source_vertex.labels.reserve(sec_labels.size());
for (auto label_id : secondary_labels.GetValue()) {
source_vertex.labels.emplace_back(memgraph::msgs::Label{.id = label_id}); std::transform(sec_labels.begin(), sec_labels.end(), std::back_inserter(source_vertex.labels),
} [](auto label_id) { return memgraph::msgs::Label{.id = label_id}; });
return source_vertex; return source_vertex;
} }
@ -164,7 +370,7 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
std::map<PropertyId, Value> src_vertex_properties; std::map<PropertyId, Value> src_vertex_properties;
if (!req.src_vertex_properties) { if (!req.src_vertex_properties) {
auto props = v_acc->Properties(memgraph::storage::v3::View::OLD); auto props = v_acc->Properties(View::OLD);
if (props.HasError()) { if (props.HasError()) {
spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}", spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
@ -172,16 +378,18 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
} }
for (auto &[key, val] : props.GetValue()) { for (auto &[key, val] : props.GetValue()) {
src_vertex_properties.insert(std::make_pair(key, ToValue(val))); src_vertex_properties.insert(std::make_pair(key, FromPropertyValueToValue(val)));
} }
} else if (req.src_vertex_properties.value().empty()) { } else if (req.src_vertex_properties.value().empty()) {
// NOOP // NOOP
} else { } else {
for (const auto &prop : req.src_vertex_properties.value()) { auto &vertex_props = req.src_vertex_properties.value();
const auto &prop_val = v_acc->GetProperty(prop, memgraph::storage::v3::View::OLD); std::transform(vertex_props.begin(), vertex_props.end(),
src_vertex_properties.insert(std::make_pair(prop, ToValue(prop_val.GetValue()))); std::inserter(src_vertex_properties, src_vertex_properties.begin()), [&v_acc](const auto &prop) {
} const auto &prop_val = v_acc->GetProperty(prop, View::OLD);
return std::make_pair(prop, FromPropertyValueToValue(prop_val.GetValue()));
});
} }
return src_vertex_properties; return src_vertex_properties;
@ -194,7 +402,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
switch (req.direction) { switch (req.direction) {
case memgraph::msgs::EdgeDirection::OUT: { case memgraph::msgs::EdgeDirection::OUT: {
auto out_edges_result = v_acc->OutEdges(memgraph::storage::v3::View::OLD); auto out_edges_result = v_acc->OutEdges(View::OLD);
if (out_edges_result.HasError()) { if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
@ -204,7 +412,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
break; break;
} }
case memgraph::msgs::EdgeDirection::IN: { case memgraph::msgs::EdgeDirection::IN: {
auto in_edges_result = v_acc->InEdges(memgraph::storage::v3::View::OLD); auto in_edges_result = v_acc->InEdges(View::OLD);
if (in_edges_result.HasError()) { if (in_edges_result.HasError()) {
spdlog::debug( spdlog::debug(
"Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id "Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id
@ -215,7 +423,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
break; break;
} }
case memgraph::msgs::EdgeDirection::BOTH: { case memgraph::msgs::EdgeDirection::BOTH: {
auto in_edges_result = v_acc->InEdges(memgraph::storage::v3::View::OLD); auto in_edges_result = v_acc->InEdges(View::OLD);
if (in_edges_result.HasError()) { if (in_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}", spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
@ -223,7 +431,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
} }
in_edges = std::move(in_edges_result.GetValue()); in_edges = std::move(in_edges_result.GetValue());
auto out_edges_result = v_acc->OutEdges(memgraph::storage::v3::View::OLD); auto out_edges_result = v_acc->OutEdges(View::OLD);
if (out_edges_result.HasError()) { if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
@ -315,7 +523,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
if (!req.edge_properties) { if (!req.edge_properties) {
get_edge_properties = [&req](const memgraph::storage::v3::EdgeAccessor &edge) -> EdgeProperties { get_edge_properties = [&req](const memgraph::storage::v3::EdgeAccessor &edge) -> EdgeProperties {
std::map<PropertyId, memgraph::msgs::Value> ret; std::map<PropertyId, memgraph::msgs::Value> ret;
auto property_results = edge.Properties(memgraph::storage::v3::View::OLD); auto property_results = edge.Properties(View::OLD);
if (property_results.HasError()) { if (property_results.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id); req.transaction_id.logical_id);
@ -323,7 +531,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
} }
for (const auto &[prop_key, prop_val] : property_results.GetValue()) { for (const auto &[prop_key, prop_val] : property_results.GetValue()) {
ret.insert(std::make_pair(prop_key, ToValue(prop_val))); ret.insert(std::make_pair(prop_key, FromPropertyValueToValue(prop_val)));
} }
return ret; return ret;
}; };
@ -334,14 +542,14 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
ret.reserve(req.edge_properties.value().size()); ret.reserve(req.edge_properties.value().size());
for (const auto &edge_prop : req.edge_properties.value()) { for (const auto &edge_prop : req.edge_properties.value()) {
// TODO(gvolfing) maybe check for the absence of certain properties // TODO(gvolfing) maybe check for the absence of certain properties
ret.emplace_back(ToValue(edge.GetProperty(edge_prop, memgraph::storage::v3::View::OLD).GetValue())); ret.emplace_back(FromPropertyValueToValue(edge.GetProperty(edge_prop, View::OLD).GetValue()));
} }
return ret; return ret;
}; };
} }
/// Fill up source vertex /// Fill up source vertex
auto v_acc = acc.FindVertex(ConvertPropertyVector(std::move(src_vertex.second)), memgraph::storage::v3::View::OLD); auto v_acc = acc.FindVertex(ConvertPropertyVector(std::move(src_vertex.second)), View::OLD);
auto source_vertex = FillUpSourceVertex(v_acc, req, src_vertex); auto source_vertex = FillUpSourceVertex(v_acc, req, src_vertex);
if (!source_vertex) { if (!source_vertex) {
@ -412,11 +620,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
.edges_with_all_properties = std::move(edges_with_all_properties), .edges_with_all_properties = std::move(edges_with_all_properties),
.edges_with_specific_properties = std::move(edges_with_specific_properties)}; .edges_with_specific_properties = std::move(edges_with_specific_properties)};
} }
}; // namespace
} // namespace
namespace memgraph::storage::v3 {
msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) { msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) {
auto acc = shard_->Access(req.transaction_id); auto acc = shard_->Access(req.transaction_id);
@ -435,9 +639,10 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) {
// TODO(gvolfing) make sure if this conversion is actually needed. // TODO(gvolfing) make sure if this conversion is actually needed.
std::vector<memgraph::storage::v3::LabelId> converted_label_ids; std::vector<memgraph::storage::v3::LabelId> converted_label_ids;
converted_label_ids.reserve(new_vertex.label_ids.size()); converted_label_ids.reserve(new_vertex.label_ids.size());
for (const auto &label_id : new_vertex.label_ids) {
converted_label_ids.emplace_back(label_id.id); std::transform(new_vertex.label_ids.begin(), new_vertex.label_ids.end(), std::back_inserter(converted_label_ids),
} [](const auto &label_id) { return label_id.id; });
// TODO(jbajic) sending primary key as vector breaks validation on storage side // TODO(jbajic) sending primary key as vector breaks validation on storage side
// cannot map id -> value // cannot map id -> value
PrimaryKey transformed_pk; PrimaryKey transformed_pk;
@ -719,6 +924,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
uint64_t sample_counter = 0; uint64_t sample_counter = 0;
const auto start_ids = ConvertPropertyVector(std::move(req.start_id.second)); const auto start_ids = ConvertPropertyVector(std::move(req.start_id.second));
auto dba = DbAccessor{&acc};
for (auto it = vertex_iterable.begin(); it != vertex_iterable.end(); ++it) { for (auto it = vertex_iterable.begin(); it != vertex_iterable.end(); ++it) {
const auto &vertex = *it; const auto &vertex = *it;
@ -728,6 +934,27 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
} }
if (did_reach_starting_point) { if (did_reach_starting_point) {
std::vector<Value> expression_results;
// TODO(gvolfing) it should be enough to check these only once.
if (vertex.Properties(View(req.storage_view)).HasError()) {
action_successful = false;
spdlog::debug("Could not retrive properties from VertexAccessor. Transaction id: {}",
req.transaction_id.logical_id);
break;
}
if (!req.filter_expressions.empty()) {
// NOTE - DbAccessor might get removed in the future.
const bool eval = FilterOnVertex(dba, vertex, req.filter_expressions, expr::identifier_node_symbol);
if (!eval) {
continue;
}
}
if (!req.vertex_expressions.empty()) {
expression_results = ConvertToValueVectorFromTypedValueVector(
EvaluateVertexExpressions(dba, vertex, req.vertex_expressions, expr::identifier_node_symbol));
}
std::optional<std::map<PropertyId, Value>> found_props; std::optional<std::map<PropertyId, Value>> found_props;
const auto *schema = shard_->GetSchema(shard_->PrimaryLabel()); const auto *schema = shard_->GetSchema(shard_->PrimaryLabel());
@ -746,7 +973,8 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
} }
results.emplace_back(msgs::ScanResultRow{.vertex = ConstructValueVertex(vertex, view).vertex_v, results.emplace_back(msgs::ScanResultRow{.vertex = ConstructValueVertex(vertex, view).vertex_v,
.props = FromMap(found_props.value())}); .props = FromMap(found_props.value()),
.evaluated_vertex_expressions = std::move(expression_results)});
++sample_counter; ++sample_counter;
if (req.batch_limit && sample_counter == req.batch_limit) { if (req.batch_limit && sample_counter == req.batch_limit) {

View File

@ -60,13 +60,12 @@ inline memgraph::storage::v3::PropertyValue ToPropertyValue(Value value) {
// These are not PropertyValues // These are not PropertyValues
case Value::Type::Vertex: case Value::Type::Vertex:
case Value::Type::Edge: case Value::Type::Edge:
case Value::Type::Path:
MG_ASSERT(false, "Not PropertyValue"); MG_ASSERT(false, "Not PropertyValue");
} }
return ret; return ret;
} }
inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) { inline Value FromPropertyValueToValue(const memgraph::storage::v3::PropertyValue &pv) {
using memgraph::storage::v3::PropertyValue; using memgraph::storage::v3::PropertyValue;
switch (pv.type()) { switch (pv.type()) {
@ -80,7 +79,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
std::vector<Value> list; std::vector<Value> list;
list.reserve(pv.ValueList().size()); list.reserve(pv.ValueList().size());
for (const auto &elem : pv.ValueList()) { for (const auto &elem : pv.ValueList()) {
list.emplace_back(ToValue(elem)); list.emplace_back(FromPropertyValueToValue(elem));
} }
return Value(list); return Value(list);
@ -89,7 +88,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
std::map<std::string, Value> map; std::map<std::string, Value> map;
for (const auto &[key, val] : pv.ValueMap()) { for (const auto &[key, val] : pv.ValueMap()) {
// maybe use std::make_pair once the && issue is resolved. // maybe use std::make_pair once the && issue is resolved.
map.emplace(key, ToValue(val)); map.emplace(key, FromPropertyValueToValue(val));
} }
return Value(map); return Value(map);
@ -122,7 +121,7 @@ inline std::vector<Value> ConvertValueVector(const std::vector<memgraph::storage
ret.reserve(vec.size()); ret.reserve(vec.size());
for (const auto &elem : vec) { for (const auto &elem : vec) {
ret.push_back(ToValue(elem)); ret.push_back(FromPropertyValueToValue(elem));
} }
return ret; return ret;

View File

@ -11,6 +11,7 @@
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
#include <cstddef>
#include <memory> #include <memory>
#include "storage/v3/conversions.hpp" #include "storage/v3/conversions.hpp"
@ -21,6 +22,7 @@
#include "storage/v3/mvcc.hpp" #include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/shard.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/memory_tracker.hpp" #include "utils/memory_tracker.hpp"
@ -378,6 +380,32 @@ Result<PropertyValue> VertexAccessor::GetProperty(View view, PropertyId property
return GetProperty(property, view).GetValue(); return GetProperty(property, view).GetValue();
} }
PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) const {
PropertyValue value;
const auto primary_label = PrimaryLabel(view);
if (primary_label.HasError()) {
return value;
}
const auto *schema = vertex_validator_->schema_validator->GetSchema(*primary_label);
if (!schema) {
return value;
}
// Find PropertyId index in keystore
size_t property_index{0};
for (; property_index < schema->second.size(); ++property_index) {
if (schema->second[property_index].property_id == property) {
break;
}
}
value = vertex_->keys.GetKey(property_index);
if (value.IsNull()) {
value = vertex_->properties.GetProperty(property);
}
return value;
}
Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const { Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
@ -385,7 +413,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
deleted = vertex_->deleted; deleted = vertex_->deleted;
value = vertex_->properties.GetProperty(property); value = GetPropertyValue(property, view);
delta = vertex_->delta; delta = vertex_->delta;
} }
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) { ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) {

View File

@ -133,6 +133,8 @@ class VertexAccessor final {
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value); Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
PropertyValue GetPropertyValue(PropertyId property, View view) const;
Result<void> CheckVertexExistence(View view) const; Result<void> CheckVertexExistence(View view) const;
Vertex *vertex_; Vertex *vertex_;

View File

@ -78,10 +78,10 @@ uint64_t GetUniqueInteger() {
return prop_val_val++; return prop_val_val++;
} }
LabelId get_primary_label() { return LabelId::FromUint(0); } constexpr LabelId get_primary_label() { return LabelId::FromUint(1); }
SchemaProperty get_schema_property() { constexpr SchemaProperty get_schema_property() {
return {.property_id = PropertyId::FromUint(0), .type = common::SchemaType::INT}; return {.property_id = PropertyId::FromUint(2), .type = common::SchemaType::INT};
} }
msgs::PrimaryKey GetPrimaryKey(int64_t value) { msgs::PrimaryKey GetPrimaryKey(int64_t value) {
@ -92,7 +92,7 @@ msgs::PrimaryKey GetPrimaryKey(int64_t value) {
msgs::NewVertex GetNewVertex(int64_t value) { msgs::NewVertex GetNewVertex(int64_t value) {
// Specify Labels. // Specify Labels.
msgs::Label label1 = {.id = LabelId::FromUint(1)}; msgs::Label label1 = {.id = LabelId::FromUint(3)};
std::vector<msgs::Label> label_ids = {label1}; std::vector<msgs::Label> label_ids = {label1};
// Specify primary key. // Specify primary key.
@ -100,14 +100,14 @@ msgs::NewVertex GetNewVertex(int64_t value) {
// Specify properties // Specify properties
auto val1 = msgs::Value(static_cast<int64_t>(value)); auto val1 = msgs::Value(static_cast<int64_t>(value));
auto prop1 = std::make_pair(PropertyId::FromUint(1), val1); auto prop1 = std::make_pair(PropertyId::FromUint(4), val1);
auto val3 = msgs::Value(static_cast<int64_t>(value)); auto val3 = msgs::Value(static_cast<int64_t>(value));
auto prop3 = std::make_pair(PropertyId::FromUint(2), val3); auto prop3 = std::make_pair(PropertyId::FromUint(5), val3);
//(VERIFY) does the schema has to be specified with the properties or the primarykey? //(VERIFY) does the schema has to be specified with the properties or the primarykey?
auto val2 = msgs::Value(static_cast<int64_t>(value)); auto val2 = msgs::Value(static_cast<int64_t>(value));
auto prop2 = std::make_pair(PropertyId::FromUint(0), val2); auto prop2 = std::make_pair(PropertyId::FromUint(6), val2);
std::vector<std::pair<PropertyId, msgs::Value>> properties{prop1, prop2, prop3}; std::vector<std::pair<PropertyId, msgs::Value>> properties{prop1, prop2, prop3};
@ -185,7 +185,7 @@ bool AttemptToUpdateVertex(ShardClient &client, int64_t value) {
auto vertex_id = GetValuePrimaryKeysWithValue(value)[0]; auto vertex_id = GetValuePrimaryKeysWithValue(value)[0];
std::vector<std::pair<PropertyId, msgs::Value>> property_updates; std::vector<std::pair<PropertyId, msgs::Value>> property_updates;
auto property_update = std::make_pair(PropertyId::FromUint(2), msgs::Value(static_cast<int64_t>(10000))); auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value(static_cast<int64_t>(10000)));
auto vertex_prop = msgs::UpdateVertexProp{}; auto vertex_prop = msgs::UpdateVertexProp{};
vertex_prop.primary_key = vertex_id; vertex_prop.primary_key = vertex_id;
@ -362,7 +362,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithoutBatchLi
msgs::VertexId start_id) { msgs::VertexId start_id) {
msgs::ScanVerticesRequest scan_req{}; msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = {}; scan_req.batch_limit = {};
scan_req.filter_expressions = std::nullopt; scan_req.filter_expressions.clear();
scan_req.props_to_return = std::nullopt; scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id; scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::OLD; scan_req.storage_view = msgs::StorageView::OLD;
@ -388,7 +388,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
uint64_t batch_limit) { uint64_t batch_limit) {
msgs::ScanVerticesRequest scan_req{}; msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = batch_limit; scan_req.batch_limit = batch_limit;
scan_req.filter_expressions = std::nullopt; scan_req.filter_expressions.clear();
scan_req.props_to_return = std::nullopt; scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id; scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::OLD; scan_req.storage_view = msgs::StorageView::OLD;
@ -409,6 +409,41 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
} }
} }
std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithExpression(ShardClient &client,
msgs::VertexId start_id,
uint64_t batch_limit,
uint64_t prop_val_to_check_against) {
std::string filter_expr1 = "MG_SYMBOL_NODE.property = " + std::to_string(prop_val_to_check_against);
std::vector<std::string> filter_expressions = {filter_expr1};
std::string regular_expr1 = "2+2";
std::vector<std::string> vertex_expressions = {regular_expr1};
msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = batch_limit;
scan_req.filter_expressions = filter_expressions;
scan_req.vertex_expressions = vertex_expressions;
scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::NEW;
scan_req.transaction_id.logical_id = GetTransactionId();
while (true) {
auto read_res = client.SendReadRequest(scan_req);
if (read_res.HasError()) {
continue;
}
auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success);
MG_ASSERT(!write_response.results.empty(), "There are no results!");
MG_ASSERT(write_response.results[0].evaluated_vertex_expressions[0].int_v == 4);
return {write_response.results.size(), write_response.next_start_id};
}
}
void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_vertex_val, uint64_t edge_type_id) { void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_vertex_val, uint64_t edge_type_id) {
// Source vertex // Source vertex
msgs::Label label = {.id = get_primary_label()}; msgs::Label label = {.id = get_primary_label()};
@ -721,6 +756,9 @@ void TestScanAllOneGo(ShardClient &client) {
msgs::VertexId v_id = {prim_label, prim_key}; msgs::VertexId v_id = {prim_label, prim_key};
auto [result_size_2, next_id_2] = AttemptToScanAllWithExpression(client, v_id, 5, unique_prop_val_2);
MG_ASSERT(result_size_2 == 1);
auto [result_size_with_batch, next_id_with_batch] = AttemptToScanAllWithBatchLimit(client, v_id, 5); auto [result_size_with_batch, next_id_with_batch] = AttemptToScanAllWithBatchLimit(client, v_id, 5);
auto [result_size_without_batch, next_id_without_batch] = AttemptToScanAllWithoutBatchLimit(client, v_id); auto [result_size_without_batch, next_id_without_batch] = AttemptToScanAllWithoutBatchLimit(client, v_id);
@ -835,14 +873,15 @@ int TestMessages() {
PropertyValue max_pk(static_cast<int64_t>(10000000)); PropertyValue max_pk(static_cast<int64_t>(10000000));
std::vector<PropertyValue> max_prim_key = {max_pk}; std::vector<PropertyValue> max_prim_key = {max_pk};
std::vector<SchemaProperty> schema = {get_schema_property()}; std::vector<SchemaProperty> schema_prop = {get_schema_property()};
auto shard_ptr1 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
auto shard_ptr2 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
auto shard_ptr3 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
shard_ptr1->CreateSchema(get_primary_label(), schema); auto shard_ptr1 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
shard_ptr2->CreateSchema(get_primary_label(), schema); auto shard_ptr2 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
shard_ptr3->CreateSchema(get_primary_label(), schema); auto shard_ptr3 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
shard_ptr1->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
shard_ptr2->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
shard_ptr3->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
std::vector<Address> address_for_1{shard_server_2_address, shard_server_3_address}; std::vector<Address> address_for_1{shard_server_2_address, shard_server_3_address};
std::vector<Address> address_for_2{shard_server_1_address, shard_server_3_address}; std::vector<Address> address_for_2{shard_server_1_address, shard_server_3_address};

View File

@ -192,11 +192,18 @@ TEST_F(SchemaValidatorTest, TestSchemaValidateVertexCreate) {
label1, schema_prop_string)); label1, schema_prop_string));
} }
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {}); const auto schema_violation =
schema_validator.ValidateVertexCreate(label2, {}, std::vector<std::pair<PropertyId, PropertyValue>>{});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_NE(schema_violation, std::nullopt);
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY, EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY,
label2, schema_prop_string)); label2, schema_prop_string));
} }
{
const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, std::vector<PropertyValue>{});
ASSERT_NE(schema_violation, std::nullopt);
EXPECT_EQ(*schema_violation,
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, label2));
}
// Validate wrong secondary label // Validate wrong secondary label
{ {
const auto schema_violation = const auto schema_violation =