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:
parent
6bb40a7f49
commit
85b8ce9101
@ -21,6 +21,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "expr/typed_value_exception.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/fnv.hpp"
|
||||
@ -32,16 +33,6 @@
|
||||
|
||||
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?
|
||||
/**
|
||||
* Stores a query runtime value and its type.
|
||||
|
26
src/expr/typed_value_exception.hpp
Normal file
26
src/expr/typed_value_exception.hpp
Normal 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
|
@ -202,46 +202,8 @@ Value ToBoltValue(msgs::Value 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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!");
|
||||
case msgs::Value::Type::Edge: {
|
||||
throw utils::BasicException("Vertex and Edge not supported!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,9 @@
|
||||
|
||||
#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_INJECTED_NAMESPACE_NAME memgraph::query::v2 // NOLINT(cppcoreguidelines-macro-usage)
|
||||
|
@ -50,8 +50,6 @@ inline TypedValue ValueToTypedValue(const msgs::Value &value) {
|
||||
return TypedValue(accessors::VertexAccessor(value.vertex_v, {}));
|
||||
case Value::Type::Edge:
|
||||
return TypedValue(accessors::EdgeAccessor(value.edge_v, {}));
|
||||
case Value::Type::Path:
|
||||
break;
|
||||
}
|
||||
throw std::runtime_error("Incorrect type in conversion");
|
||||
}
|
||||
@ -91,7 +89,10 @@ inline msgs::Value TypedValueToValue(const TypedValue &value) {
|
||||
case TypedValue::Type::Edge:
|
||||
return Value(value.ValueEdge().GetEdge());
|
||||
case TypedValue::Type::Path:
|
||||
default:
|
||||
case TypedValue::Type::LocalTime:
|
||||
case TypedValue::Type::LocalDateTime:
|
||||
case TypedValue::Type::Date:
|
||||
case TypedValue::Type::Duration:
|
||||
break;
|
||||
}
|
||||
throw std::runtime_error("Incorrect type in conversion");
|
||||
|
@ -22,9 +22,9 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "expr/semantic/symbol.hpp"
|
||||
#include "query/v2/common.hpp"
|
||||
#include "query/v2/frontend/ast/ast.hpp"
|
||||
#include "expr/semantic/symbol.hpp"
|
||||
#include "query/v2/bindings/typed_value.hpp"
|
||||
#include "query/v2/bindings/frame.hpp"
|
||||
#include "query/v2/bindings/symbol_table.hpp"
|
||||
|
@ -76,16 +76,6 @@ struct Vertex {
|
||||
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 Value {
|
||||
@ -135,13 +125,9 @@ struct Value {
|
||||
case Type::Map:
|
||||
std::destroy_at(&map_v);
|
||||
return;
|
||||
|
||||
case Type::Vertex:
|
||||
std::destroy_at(&vertex_v);
|
||||
return;
|
||||
case Type::Path:
|
||||
std::destroy_at(&path_v);
|
||||
return;
|
||||
case Type::Edge:
|
||||
std::destroy_at(&edge_v);
|
||||
}
|
||||
@ -175,9 +161,6 @@ struct Value {
|
||||
case Type::Edge:
|
||||
new (&edge_v) Edge(other.edge_v);
|
||||
return;
|
||||
case Type::Path:
|
||||
new (&path_v) Path(other.path_v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,9 +192,6 @@ struct Value {
|
||||
case Type::Edge:
|
||||
new (&edge_v) Edge(std::move(other.edge_v));
|
||||
break;
|
||||
case Type::Path:
|
||||
new (&path_v) Path(std::move(other.path_v));
|
||||
break;
|
||||
}
|
||||
|
||||
other.DestroyValue();
|
||||
@ -251,9 +231,6 @@ struct Value {
|
||||
case Type::Edge:
|
||||
new (&edge_v) Edge(other.edge_v);
|
||||
break;
|
||||
case Type::Path:
|
||||
new (&path_v) Path(other.path_v);
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -292,9 +269,6 @@ struct Value {
|
||||
case Type::Edge:
|
||||
new (&edge_v) Edge(std::move(other.edge_v));
|
||||
break;
|
||||
case Type::Path:
|
||||
new (&path_v) Path(std::move(other.path_v));
|
||||
break;
|
||||
}
|
||||
|
||||
other.DestroyValue();
|
||||
@ -302,7 +276,7 @@ struct Value {
|
||||
|
||||
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};
|
||||
union {
|
||||
Null null_v;
|
||||
@ -314,7 +288,6 @@ struct Value {
|
||||
std::map<std::string, Value> map_v;
|
||||
Vertex vertex_v;
|
||||
Edge edge_v;
|
||||
Path path_v;
|
||||
};
|
||||
|
||||
friend bool operator==(const Value &lhs, const Value &rhs) {
|
||||
@ -340,8 +313,6 @@ struct Value {
|
||||
return lhs.vertex_v == rhs.vertex_v;
|
||||
case Value::Type::Edge:
|
||||
return lhs.edge_v == rhs.edge_v;
|
||||
case Value::Type::Path:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -381,18 +352,22 @@ struct ScanVerticesRequest {
|
||||
Hlc transaction_id;
|
||||
VertexId start_id;
|
||||
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;
|
||||
StorageView storage_view{StorageView::NEW};
|
||||
|
||||
std::optional<Label> label;
|
||||
std::optional<std::pair<PropertyId, std::string>> property_expression_pair;
|
||||
std::optional<std::vector<std::string>> filter_expressions;
|
||||
};
|
||||
|
||||
struct ScanResultRow {
|
||||
Vertex vertex;
|
||||
// empty() is no properties returned
|
||||
std::vector<std::pair<PropertyId, Value>> props;
|
||||
std::vector<Value> evaluated_vertex_expressions;
|
||||
};
|
||||
|
||||
struct ScanVerticesResponse {
|
||||
|
@ -11,5 +11,9 @@
|
||||
|
||||
#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_INJECTED_NAMESPACE_NAME memgraph::storage::v3 // NOLINT(cppcoreguidelines-macro-usage)
|
||||
|
@ -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
|
@ -9,7 +9,7 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "expr/typed_value.hpp"
|
||||
#include "expr/typed_value_exception.hpp"
|
||||
#include "query/v2/requests.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "utils/memory.hpp"
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <cstddef>
|
||||
#include <ranges>
|
||||
|
||||
#include "common/types.hpp"
|
||||
#include "storage/v3/schemas.hpp"
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
@ -78,6 +79,39 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
|
||||
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(
|
||||
const LabelId primary_label, const PropertyId property_id) const {
|
||||
// Verify existence of schema on primary label
|
||||
@ -103,6 +137,8 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
|
||||
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)
|
||||
: schema_validator{&schema_validator}, primary_label_{primary_label} {}
|
||||
|
||||
|
@ -29,6 +29,7 @@ struct SchemaViolation {
|
||||
VERTEX_UPDATE_PRIMARY_KEY,
|
||||
VERTEX_UPDATE_PRIMARY_LABEL,
|
||||
VERTEX_SECONDARY_LABEL_IS_PRIMARY,
|
||||
VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
|
||||
};
|
||||
|
||||
SchemaViolation(ValidationStatus status, LabelId label);
|
||||
@ -50,15 +51,21 @@ class SchemaValidator {
|
||||
public:
|
||||
explicit SchemaValidator(Schemas &schemas);
|
||||
|
||||
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate(
|
||||
[[deprecated]] std::optional<SchemaViolation> ValidateVertexCreate(
|
||||
LabelId primary_label, const std::vector<LabelId> &labels,
|
||||
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,
|
||||
PropertyId property_id) const;
|
||||
|
||||
[[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const;
|
||||
|
||||
const Schemas::Schema *GetSchema(LabelId label) const;
|
||||
|
||||
private:
|
||||
Schemas &schemas_;
|
||||
};
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "storage/v3/shard.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@ -392,15 +393,9 @@ ResultSchema<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
|
||||
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
|
||||
OOMExceptionEnabler oom_exception;
|
||||
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 =
|
||||
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties_ordered);
|
||||
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties);
|
||||
if (maybe_schema_violation) {
|
||||
return {std::move(*maybe_schema_violation)};
|
||||
}
|
||||
|
@ -9,18 +9,31 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "parser/opencypher/parser.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/property_value.hpp"
|
||||
#include "storage/v3/schemas.hpp"
|
||||
#include "storage/v3/shard_rsm.hpp"
|
||||
#include "storage/v3/storage.hpp"
|
||||
#include "storage/v3/value_conversions.hpp"
|
||||
#include "storage/v3/vertex_accessor.hpp"
|
||||
#include "storage/v3/view.hpp"
|
||||
|
||||
using memgraph::msgs::Label;
|
||||
using memgraph::msgs::PropertyId;
|
||||
@ -30,8 +43,11 @@ using memgraph::msgs::VertexId;
|
||||
|
||||
using memgraph::storage::conversions::ConvertPropertyVector;
|
||||
using memgraph::storage::conversions::ConvertValueVector;
|
||||
using memgraph::storage::conversions::FromPropertyValueToValue;
|
||||
using memgraph::storage::conversions::ToPropertyValue;
|
||||
using memgraph::storage::conversions::ToValue;
|
||||
using memgraph::storage::v3::View;
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
namespace {
|
||||
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;
|
||||
ret.reserve(properties.size());
|
||||
|
||||
for (auto &[key, value] : properties) {
|
||||
ret.emplace_back(std::make_pair(key, ToPropertyValue(std::move(value))));
|
||||
}
|
||||
std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()),
|
||||
std::back_inserter(ret), [](std::pair<PropertyId, Value> &&property) {
|
||||
return std::make_pair(property.first, ToPropertyValue(std::move(property.second)));
|
||||
});
|
||||
|
||||
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;
|
||||
ret.reserve(properties.size());
|
||||
|
||||
for (const auto &[key, value] : properties) {
|
||||
ret.emplace_back(std::make_pair(key, value));
|
||||
}
|
||||
std::transform(properties.begin(), properties.end(), std::back_inserter(ret),
|
||||
[](const auto &property) { return std::make_pair(property.first, property.second); });
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(
|
||||
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;
|
||||
|
||||
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");
|
||||
return std::nullopt;
|
||||
}
|
||||
ret.emplace(std::make_pair(prop, ToValue(value)));
|
||||
ret.emplace(std::make_pair(prop, FromPropertyValueToValue(value)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -89,9 +105,11 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
|
||||
spdlog::debug("Encountered an error while trying to get vertex properties.");
|
||||
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);
|
||||
if (maybe_pk.HasError()) {
|
||||
@ -101,31 +119,217 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
|
||||
const auto pk = maybe_pk.GetValue();
|
||||
MG_ASSERT(schema->second.size() == pk.size(), "PrimaryKey size does not match schema!");
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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());
|
||||
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
|
||||
auto vertex_labels = acc.Labels(view).GetValue();
|
||||
std::vector<Label> value_labels;
|
||||
for (const auto &label : vertex_labels) {
|
||||
Label l = {.id = label};
|
||||
value_labels.push_back(l);
|
||||
}
|
||||
std::vector<memgraph::msgs::Label> value_labels;
|
||||
value_labels.reserve(vertex_labels.size());
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
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) {
|
||||
// TODO(gvolfing) This should be checked only once and handled accordingly.
|
||||
if (req.edge_types.empty()) {
|
||||
@ -143,19 +347,21 @@ struct LocalError {};
|
||||
std::optional<memgraph::msgs::Vertex> FillUpSourceVertex(
|
||||
const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc, memgraph::msgs::ExpandOneRequest &req,
|
||||
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()) {
|
||||
spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}",
|
||||
req.transaction_id.logical_id);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto &sec_labels = secondary_labels.GetValue();
|
||||
memgraph::msgs::Vertex source_vertex;
|
||||
source_vertex.id = src_vertex;
|
||||
source_vertex.labels.reserve(secondary_labels.GetValue().size());
|
||||
for (auto label_id : secondary_labels.GetValue()) {
|
||||
source_vertex.labels.emplace_back(memgraph::msgs::Label{.id = label_id});
|
||||
}
|
||||
source_vertex.labels.reserve(sec_labels.size());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -164,7 +370,7 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
|
||||
std::map<PropertyId, Value> 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()) {
|
||||
spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}",
|
||||
req.transaction_id.logical_id);
|
||||
@ -172,16 +378,18 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
|
||||
}
|
||||
|
||||
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()) {
|
||||
// NOOP
|
||||
} else {
|
||||
for (const auto &prop : req.src_vertex_properties.value()) {
|
||||
const auto &prop_val = v_acc->GetProperty(prop, memgraph::storage::v3::View::OLD);
|
||||
src_vertex_properties.insert(std::make_pair(prop, ToValue(prop_val.GetValue())));
|
||||
}
|
||||
auto &vertex_props = req.src_vertex_properties.value();
|
||||
std::transform(vertex_props.begin(), vertex_props.end(),
|
||||
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;
|
||||
@ -194,7 +402,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
|
||||
|
||||
switch (req.direction) {
|
||||
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()) {
|
||||
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
|
||||
req.transaction_id.logical_id);
|
||||
@ -204,7 +412,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
|
||||
break;
|
||||
}
|
||||
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()) {
|
||||
spdlog::debug(
|
||||
"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;
|
||||
}
|
||||
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()) {
|
||||
spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction 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());
|
||||
|
||||
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()) {
|
||||
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
|
||||
req.transaction_id.logical_id);
|
||||
@ -315,7 +523,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
|
||||
if (!req.edge_properties) {
|
||||
get_edge_properties = [&req](const memgraph::storage::v3::EdgeAccessor &edge) -> EdgeProperties {
|
||||
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()) {
|
||||
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction 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()) {
|
||||
ret.insert(std::make_pair(prop_key, ToValue(prop_val)));
|
||||
ret.insert(std::make_pair(prop_key, FromPropertyValueToValue(prop_val)));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
@ -334,14 +542,14 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
|
||||
ret.reserve(req.edge_properties.value().size());
|
||||
for (const auto &edge_prop : req.edge_properties.value()) {
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
|
||||
/// 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);
|
||||
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_specific_properties = std::move(edges_with_specific_properties)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace memgraph::storage::v3 {
|
||||
|
||||
}; // namespace
|
||||
msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) {
|
||||
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.
|
||||
std::vector<memgraph::storage::v3::LabelId> converted_label_ids;
|
||||
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
|
||||
// cannot map id -> value
|
||||
PrimaryKey transformed_pk;
|
||||
@ -719,6 +924,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
|
||||
uint64_t sample_counter = 0;
|
||||
|
||||
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) {
|
||||
const auto &vertex = *it;
|
||||
@ -728,6 +934,27 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
.props = FromMap(found_props.value())});
|
||||
.props = FromMap(found_props.value()),
|
||||
.evaluated_vertex_expressions = std::move(expression_results)});
|
||||
|
||||
++sample_counter;
|
||||
if (req.batch_limit && sample_counter == req.batch_limit) {
|
||||
|
@ -60,13 +60,12 @@ inline memgraph::storage::v3::PropertyValue ToPropertyValue(Value value) {
|
||||
// These are not PropertyValues
|
||||
case Value::Type::Vertex:
|
||||
case Value::Type::Edge:
|
||||
case Value::Type::Path:
|
||||
MG_ASSERT(false, "Not PropertyValue");
|
||||
}
|
||||
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;
|
||||
|
||||
switch (pv.type()) {
|
||||
@ -80,7 +79,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
|
||||
std::vector<Value> list;
|
||||
list.reserve(pv.ValueList().size());
|
||||
for (const auto &elem : pv.ValueList()) {
|
||||
list.emplace_back(ToValue(elem));
|
||||
list.emplace_back(FromPropertyValueToValue(elem));
|
||||
}
|
||||
|
||||
return Value(list);
|
||||
@ -89,7 +88,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
|
||||
std::map<std::string, Value> map;
|
||||
for (const auto &[key, val] : pv.ValueMap()) {
|
||||
// maybe use std::make_pair once the && issue is resolved.
|
||||
map.emplace(key, ToValue(val));
|
||||
map.emplace(key, FromPropertyValueToValue(val));
|
||||
}
|
||||
|
||||
return Value(map);
|
||||
@ -122,7 +121,7 @@ inline std::vector<Value> ConvertValueVector(const std::vector<memgraph::storage
|
||||
ret.reserve(vec.size());
|
||||
|
||||
for (const auto &elem : vec) {
|
||||
ret.push_back(ToValue(elem));
|
||||
ret.push_back(FromPropertyValueToValue(elem));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "storage/v3/vertex_accessor.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "storage/v3/conversions.hpp"
|
||||
@ -21,6 +22,7 @@
|
||||
#include "storage/v3/mvcc.hpp"
|
||||
#include "storage/v3/property_value.hpp"
|
||||
#include "storage/v3/schema_validator.hpp"
|
||||
#include "storage/v3/shard.hpp"
|
||||
#include "storage/v3/vertex.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/memory_tracker.hpp"
|
||||
@ -378,6 +380,32 @@ Result<PropertyValue> VertexAccessor::GetProperty(View view, PropertyId property
|
||||
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 {
|
||||
bool exists = true;
|
||||
bool deleted = false;
|
||||
@ -385,7 +413,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
deleted = vertex_->deleted;
|
||||
value = vertex_->properties.GetProperty(property);
|
||||
value = GetPropertyValue(property, view);
|
||||
delta = vertex_->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) {
|
||||
|
@ -133,6 +133,8 @@ class VertexAccessor final {
|
||||
/// @throw std::bad_alloc
|
||||
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
|
||||
|
||||
PropertyValue GetPropertyValue(PropertyId property, View view) const;
|
||||
|
||||
Result<void> CheckVertexExistence(View view) const;
|
||||
|
||||
Vertex *vertex_;
|
||||
|
@ -78,10 +78,10 @@ uint64_t GetUniqueInteger() {
|
||||
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() {
|
||||
return {.property_id = PropertyId::FromUint(0), .type = common::SchemaType::INT};
|
||||
constexpr SchemaProperty get_schema_property() {
|
||||
return {.property_id = PropertyId::FromUint(2), .type = common::SchemaType::INT};
|
||||
}
|
||||
|
||||
msgs::PrimaryKey GetPrimaryKey(int64_t value) {
|
||||
@ -92,7 +92,7 @@ msgs::PrimaryKey GetPrimaryKey(int64_t value) {
|
||||
|
||||
msgs::NewVertex GetNewVertex(int64_t value) {
|
||||
// Specify Labels.
|
||||
msgs::Label label1 = {.id = LabelId::FromUint(1)};
|
||||
msgs::Label label1 = {.id = LabelId::FromUint(3)};
|
||||
std::vector<msgs::Label> label_ids = {label1};
|
||||
|
||||
// Specify primary key.
|
||||
@ -100,14 +100,14 @@ msgs::NewVertex GetNewVertex(int64_t value) {
|
||||
|
||||
// Specify properties
|
||||
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 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?
|
||||
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};
|
||||
|
||||
@ -185,7 +185,7 @@ bool AttemptToUpdateVertex(ShardClient &client, int64_t value) {
|
||||
auto vertex_id = GetValuePrimaryKeysWithValue(value)[0];
|
||||
|
||||
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{};
|
||||
vertex_prop.primary_key = vertex_id;
|
||||
@ -362,7 +362,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithoutBatchLi
|
||||
msgs::VertexId start_id) {
|
||||
msgs::ScanVerticesRequest scan_req{};
|
||||
scan_req.batch_limit = {};
|
||||
scan_req.filter_expressions = std::nullopt;
|
||||
scan_req.filter_expressions.clear();
|
||||
scan_req.props_to_return = std::nullopt;
|
||||
scan_req.start_id = start_id;
|
||||
scan_req.storage_view = msgs::StorageView::OLD;
|
||||
@ -388,7 +388,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
|
||||
uint64_t batch_limit) {
|
||||
msgs::ScanVerticesRequest scan_req{};
|
||||
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.start_id = start_id;
|
||||
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) {
|
||||
// Source vertex
|
||||
msgs::Label label = {.id = get_primary_label()};
|
||||
@ -721,6 +756,9 @@ void TestScanAllOneGo(ShardClient &client) {
|
||||
|
||||
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_without_batch, next_id_without_batch] = AttemptToScanAllWithoutBatchLimit(client, v_id);
|
||||
|
||||
@ -835,14 +873,15 @@ int TestMessages() {
|
||||
PropertyValue max_pk(static_cast<int64_t>(10000000));
|
||||
std::vector<PropertyValue> max_prim_key = {max_pk};
|
||||
|
||||
std::vector<SchemaProperty> schema = {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);
|
||||
std::vector<SchemaProperty> schema_prop = {get_schema_property()};
|
||||
|
||||
shard_ptr1->CreateSchema(get_primary_label(), schema);
|
||||
shard_ptr2->CreateSchema(get_primary_label(), schema);
|
||||
shard_ptr3->CreateSchema(get_primary_label(), schema);
|
||||
auto shard_ptr1 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
|
||||
auto shard_ptr2 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
|
||||
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_2{shard_server_1_address, shard_server_3_address};
|
||||
|
@ -192,11 +192,18 @@ TEST_F(SchemaValidatorTest, TestSchemaValidateVertexCreate) {
|
||||
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);
|
||||
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY,
|
||||
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
|
||||
{
|
||||
const auto schema_violation =
|
||||
|
Loading…
Reference in New Issue
Block a user