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 <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.
|
||||||
|
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)};
|
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!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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");
|
||||||
|
@ -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"
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
// 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"
|
||||||
|
@ -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} {}
|
||||||
|
|
||||||
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
@ -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)};
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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_;
|
||||||
|
@ -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};
|
||||||
|
@ -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 =
|
||||||
|
Loading…
Reference in New Issue
Block a user