Prototype suggested changes and polish PR

This commit is contained in:
Kostas Kyrimis 2022-11-30 14:59:00 +02:00
parent 07a8ac0db8
commit 9621532d3d
5 changed files with 360 additions and 275 deletions

View File

@ -368,16 +368,12 @@ struct ScanVerticesResponse {
std::vector<ScanResultRow> results;
};
struct VertexAndEdgeId {
VertexId vertex;
std::optional<EdgeTypeId> edge;
};
struct GetPropertiesRequest {
Hlc transaction_id;
std::vector<VertexAndEdgeId> vertices_and_edges;
std::vector<VertexId> vertex_ids;
std::vector<std::pair<VertexId, EdgeId>> vertices_and_edges;
std::vector<PropertyId> property_ids;
std::optional<std::vector<PropertyId>> property_ids;
std::vector<std::string> expressions;
std::vector<OrderBy> order_by;
@ -388,22 +384,16 @@ struct GetPropertiesRequest {
std::optional<std::string> filter;
};
struct PropIdValue {
std::vector<PropertyId> ids;
std::vector<Value> properties;
};
struct GetPropertiesResultRow {
VertexAndEdgeId vertex_and_edge;
VertexId vertex;
std::optional<EdgeId> edge;
PropIdValue properies_and_ids;
std::vector<std::pair<PropertyId, Value>> props;
std::vector<Value> evaluated_expressions;
};
struct GetPropertiesResponse {
std::vector<GetPropertiesResultRow> result_row;
enum RequestResult : uint16_t { OUT_OF_SHARD_RANGE, SUCCESS, FAILURE };
RequestResult result;
std::optional<ShardError> error;
};

View File

@ -14,6 +14,7 @@
#include <iterator>
#include <vector>
#include "pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/db_accessor.hpp"
#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/expr.hpp"
@ -221,30 +222,39 @@ std::vector<TypedValue> EvaluateVertexExpressions(DbAccessor &dba, const VertexA
return evaluated_expressions;
}
std::vector<TypedValue> EvaluateEdgeExpressions(DbAccessor &dba, const VertexAccessor &v_acc, const EdgeAccessor &e_acc,
const std::vector<std::string> &expressions) {
std::vector<TypedValue> evaluated_expressions;
evaluated_expressions.reserve(expressions.size());
std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions),
[&dba, &v_acc, &e_acc](const auto &expression) {
return ComputeExpression(dba, v_acc, e_acc, expression, expr::identifier_node_symbol,
expr::identifier_edge_symbol);
});
return evaluated_expressions;
}
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
const Schemas::Schema &schema) {
std::map<PropertyId, Value> ret;
auto props = acc.Properties(view);
if (props.HasError()) {
spdlog::debug("Encountered an error while trying to get vertex properties.");
return props.GetError();
auto ret = impl::CollectAllPropertiesImpl<VertexAccessor>(acc, view);
if (ret.HasError()) {
return ret.GetError();
}
auto &properties = props.GetValue();
std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()),
[](std::pair<const PropertyId, PropertyValue> &pair) {
return std::make_pair(pair.first, FromPropertyValueToValue(std::move(pair.second)));
});
properties.clear();
auto pks = PrimaryKeysFromAccessor(acc, view, schema);
if (pks) {
ret.merge(*pks);
ret.GetValue().merge(*pks);
}
return ret;
}
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view) {
return impl::CollectAllPropertiesImpl(acc, view);
}
EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows) {
// Functions to select connecting edges based on uniquness
EdgeUniquenessFunction maybe_filter_based_on_edge_uniquness;
@ -351,15 +361,19 @@ EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) {
return edge_filler;
}
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &filters,
const std::string_view node_name, const std::optional<EdgeAccessor> &e_acc) {
return std::ranges::all_of(filters, [&node_name, &dba, &v_acc, &e_acc](const auto &filter_expr) {
TypedValue result;
if (e_acc) {
result = ComputeExpression(dba, v_acc, e_acc, filter_expr, "", node_name);
} else {
result = ComputeExpression(dba, v_acc, e_acc, filter_expr, node_name, "");
}
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc,
const std::vector<std::string> &filters) {
return std::ranges::all_of(filters, [&dba, &v_acc](const auto &filter_expr) {
const auto result = ComputeExpression(dba, v_acc, std::nullopt, filter_expr, expr::identifier_node_symbol, "");
return result.IsBool() && result.ValueBool();
});
}
bool FilterOnEdge(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const EdgeAccessor &e_acc,
const std::vector<std::string> &filters) {
return std::ranges::all_of(filters, [&dba, &v_acc, &e_acc](const auto &filter_expr) {
const auto result =
ComputeExpression(dba, v_acc, e_acc, filter_expr, expr::identifier_node_symbol, expr::identifier_edge_symbol);
return result.IsBool() && result.ValueBool();
});
}
@ -444,61 +458,6 @@ ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
return result_row;
}
std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_by,
std::vector<GetPropElement> &&vertices) {
std::vector<Ordering> ordering;
ordering.reserve(order_by.size());
for (const auto &order : order_by) {
switch (order.direction) {
case memgraph::msgs::OrderingDirection::ASCENDING: {
ordering.push_back(Ordering::ASC);
break;
}
case memgraph::msgs::OrderingDirection::DESCENDING: {
ordering.push_back(Ordering::DESC);
break;
}
}
}
struct PropElement {
std::vector<TypedValue> properties_order_by;
VertexAccessor vertex_acc;
GetPropElement *original_element;
};
std::vector<PropElement> ordered;
auto compare_typed_values = TypedValueVectorCompare(ordering);
for (auto &vertex : vertices) {
std::vector<TypedValue> properties;
properties.reserve(order_by.size());
const auto *symbol = (vertex.edge_acc) ? expr::identifier_edge_symbol : expr::identifier_node_symbol;
for (const auto &order : order_by) {
TypedValue val;
if (vertex.edge_acc) {
val = ComputeExpression(dba, vertex.vertex_acc, vertex.edge_acc, order.expression.expression, "", symbol);
} else {
val = ComputeExpression(dba, vertex.vertex_acc, vertex.edge_acc, order.expression.expression, symbol, "");
}
properties.push_back(std::move(val));
}
ordered.push_back({std::move(properties), vertex.vertex_acc, &vertex});
}
std::sort(ordered.begin(), ordered.end(), [&compare_typed_values](const auto &lhs, const auto &rhs) {
return compare_typed_values(lhs.properties_order_by, rhs.properties_order_by);
});
std::vector<GetPropElement> results_ordered;
results_ordered.reserve(ordered.size());
for (auto &elem : ordered) {
results_ordered.push_back(std::move(*elem.original_element));
}
return results_ordered;
}
VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable,
const std::vector<PropertyValue> &primary_key, const View view) {
auto it = vertex_iterable.begin();
@ -584,4 +543,36 @@ std::vector<Element<EdgeAccessor>> OrderByEdges(DbAccessor &dba, std::vector<Edg
return ordered;
}
std::vector<Element<std::pair<VertexAccessor, EdgeAccessor>>> OrderByEdges(
DbAccessor &dba, std::vector<EdgeAccessor> &iterable, std::vector<msgs::OrderBy> &order_by_edges,
const std::vector<VertexAccessor> &vertex_acc) {
MG_ASSERT(vertex_acc.size() == iterable.size());
std::vector<Ordering> ordering;
ordering.reserve(order_by_edges.size());
std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(ordering),
[](const auto &order_by) { return ConvertMsgsOrderByToOrdering(order_by.direction); });
std::vector<Element<std::pair<VertexAccessor, EdgeAccessor>>> ordered;
VertexAccessor current = vertex_acc.front();
size_t id = 0;
for (auto it = iterable.begin(); it != iterable.end(); it++, id++) {
current = vertex_acc[id];
std::vector<TypedValue> properties_order_by;
properties_order_by.reserve(order_by_edges.size());
std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(properties_order_by),
[&dba, it, current](const auto &order_by) {
return ComputeExpression(dba, current, *it, order_by.expression.expression,
expr::identifier_node_symbol, expr::identifier_edge_symbol);
});
ordered.push_back({std::move(properties_order_by), {current, *it}});
}
auto compare_typed_values = TypedValueVectorCompare(ordering);
std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) {
return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by);
});
return ordered;
}
} // namespace memgraph::storage::v3

View File

@ -20,6 +20,7 @@
#include "storage/v3/edge_accessor.hpp"
#include "storage/v3/expr.hpp"
#include "storage/v3/shard.hpp"
#include "storage/v3/value_conversions.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/template_utils.hpp"
@ -31,7 +32,7 @@ using EdgeFiller =
using msgs::Value;
template <typename T>
concept ObjectAccessor = utils::SameAsAnyOf<T, VertexAccessor, EdgeAccessor>;
concept ObjectAccessor = utils::SameAsAnyOf<T, VertexAccessor, EdgeAccessor, std::pair<VertexAccessor, EdgeAccessor>>;
inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
// in ordering null comes after everything else
@ -131,13 +132,6 @@ struct Element {
TObjectAccessor object_acc;
};
struct GetPropElement {
std::vector<TypedValue> properties_order_by;
std::vector<PropertyId> ids;
VertexAccessor vertex_acc;
std::optional<EdgeAccessor> edge_acc;
};
template <typename T>
concept VerticesIt = utils::SameAsAnyOf<T, VerticesIterable, std::vector<VertexAccessor>>;
@ -170,12 +164,16 @@ std::vector<Element<VertexAccessor>> OrderByVertices(DbAccessor &dba, TIterable
return ordered;
}
template <typename T>
concept EdgeObjectAccessor = utils::SameAsAnyOf<T, EdgeAccessor, std::pair<VertexAccessor, EdgeAccessor>>;
std::vector<Element<EdgeAccessor>> OrderByEdges(DbAccessor &dba, std::vector<EdgeAccessor> &iterable,
std::vector<msgs::OrderBy> &order_by_edges,
const VertexAccessor &vertex_acc);
std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_bys,
std::vector<GetPropElement> &&vertices);
std::vector<Element<std::pair<VertexAccessor, EdgeAccessor>>> OrderByEdges(
DbAccessor &dba, std::vector<EdgeAccessor> &iterable, std::vector<msgs::OrderBy> &order_by_edges,
const std::vector<VertexAccessor> &vertex_acc);
VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable,
const std::vector<PropertyValue> &primary_key, View view);
@ -187,19 +185,65 @@ std::vector<Element<VertexAccessor>>::const_iterator GetStartOrderedElementsIter
std::array<std::vector<EdgeAccessor>, 2> GetEdgesFromVertex(const VertexAccessor &vertex_accessor,
msgs::EdgeDirection direction);
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &filters,
const std::string_view node_name, const std::optional<EdgeAccessor> &e_acc = std::nullopt);
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &filters);
bool FilterOnEdge(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const EdgeAccessor &e_acc,
const std::vector<std::string> &filters);
std::vector<TypedValue> EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc,
const std::vector<std::string> &expressions,
std::string_view node_name);
ShardResult<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc,
std::vector<TypedValue> EvaluateEdgeExpressions(DbAccessor &dba, const VertexAccessor &v_acc, const EdgeAccessor &e_acc,
const std::vector<std::string> &expressions);
template <typename T>
concept TAccessor = utils::SameAsAnyOf<T, VertexAccessor, EdgeAccessor>;
template <typename TAccessor>
ShardResult<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(const TAccessor &acc,
const std::vector<PropertyId> &props,
View view);
View view) {
std::map<PropertyId, Value> ret;
for (const auto &prop : props) {
auto result = acc.GetProperty(prop, view);
if (result.HasError()) {
spdlog::debug("Encountered an Error while trying to get a vertex property.");
return result.GetError();
}
auto &value = result.GetValue();
ret.emplace(std::make_pair(prop, FromPropertyValueToValue(std::move(value))));
}
return ret;
}
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
const Schemas::Schema &schema);
namespace impl {
template <typename TAccessor>
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesImpl(const TAccessor &acc, View view) {
std::map<PropertyId, Value> ret;
auto props = acc.Properties(view);
if (props.HasError()) {
spdlog::debug("Encountered an error while trying to get vertex properties.");
return props.GetError();
}
auto &properties = props.GetValue();
std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()),
[](std::pair<const PropertyId, PropertyValue> &pair) {
return std::make_pair(pair.first, conversions::FromPropertyValueToValue(std::move(pair.second)));
});
return ret;
}
} // namespace impl
template <typename TAccessor>
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const TAccessor &acc, View view) {
return impl::CollectAllPropertiesImpl<TAccessor>(acc, view);
}
EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows);

View File

@ -10,6 +10,8 @@
// licenses/APL.txt.
#include <algorithm>
#include <exception>
#include <experimental/source_location>
#include <functional>
#include <iterator>
#include <optional>
@ -19,7 +21,6 @@
#include "common/errors.hpp"
#include "parser/opencypher/parser.hpp"
#include "pretty_print_ast_to_original_expression.hpp"
#include "query/v2/requests.hpp"
#include "storage/v2/vertex.hpp"
#include "storage/v2/view.hpp"
@ -330,7 +331,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
std::vector<Value> expression_results;
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);
const bool eval = FilterOnVertex(dba, vertex, req.filter_expressions);
if (!eval) {
return;
}
@ -435,7 +436,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ExpandOneRequest &&req) {
}
if (!req.filters.empty()) {
// NOTE - DbAccessor might get removed in the future.
const bool eval = FilterOnVertex(dba, src_vertex_acc_opt.value(), req.filters, expr::identifier_node_symbol);
const bool eval = FilterOnVertex(dba, src_vertex_acc_opt.value(), req.filters);
if (!eval) {
continue;
}
@ -515,124 +516,184 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CommitRequest &&req) {
};
msgs::ReadResponses ShardRsm::HandleRead(msgs::GetPropertiesRequest &&req) {
if (req.vertices_and_edges.empty()) {
return msgs::GetPropertiesResponse{.result = msgs::GetPropertiesResponse::FAILURE};
if (!req.vertex_ids.empty() && !req.vertices_and_edges.empty()) {
auto error = CreateErrorResponse(
{common::ErrorCode::VERTEX_HAS_EDGES, std::experimental::source_location::current()}, req.transaction_id, "");
return msgs::GetPropertiesResponse{.error = {}};
}
auto shard_acc = shard_->Access(req.transaction_id);
auto dba = DbAccessor{&shard_acc};
const auto view = storage::v3::View::NEW;
auto collect_props = [](const VertexAccessor &acc, const std::vector<PropertyId> &props, View view,
const std::optional<EdgeAccessor> &e_acc) mutable -> std::optional<GetPropElement> {
std::vector<TypedValue> properties;
std::vector<PropertyId> ids;
for (const auto &prop : props) {
ShardResult<PropertyValue> result{PropertyValue()};
if (e_acc) {
result = e_acc->GetProperty(prop, view);
} else {
result = acc.GetProperty(prop, view);
}
if (result.HasError() && result.GetError() == common::ErrorCode::NONEXISTENT_OBJECT) {
continue;
}
if (result.HasError()) {
spdlog::debug("Encountered an Error while trying to get a vertex property.");
return std::nullopt;
}
properties.push_back(PropertyToTypedValue<TypedValue>(result.GetValue()));
ids.push_back(prop);
auto transform_props = [](std::map<PropertyId, Value> &&value) {
std::vector<std::pair<PropertyId, Value>> result;
result.reserve(value.size());
for (auto &[id, val] : value) {
result.push_back({id, std::move(val)});
}
GetPropElement element{std::move(properties), std::move(ids), acc, e_acc};
return {std::move(element)};
return result;
};
auto find_edge = [](const VertexAccessor &v, const EdgeTypeId &e) -> std::optional<EdgeAccessor> {
auto in = v.InEdges(view, {e});
auto collect_props = [&req](const VertexAccessor &v_acc, const std::optional<EdgeAccessor> &e_acc) {
if (req.property_ids) {
if (e_acc) {
return CollectAllPropertiesFromAccessor(*e_acc, view);
}
return CollectAllPropertiesFromAccessor(v_acc, view);
}
if (e_acc) {
return CollectSpecificPropertiesFromAccessor(v_acc, *req.property_ids, view);
}
return CollectSpecificPropertiesFromAccessor(*e_acc, *req.property_ids, view);
};
auto find_edge = [](const VertexAccessor &v, msgs::EdgeId e) -> std::optional<EdgeAccessor> {
auto in = v.InEdges(view);
MG_ASSERT(in.HasValue());
for (auto &edge : in.GetValue()) {
if (edge.EdgeType() == e) {
if (edge.Gid().AsUint() == e.gid) {
return edge;
}
}
auto out = v.OutEdges(view, {e});
auto out = v.OutEdges(view);
MG_ASSERT(out.HasValue());
for (auto &edge : out.GetValue()) {
if (edge.EdgeType() == e) {
if (edge.Gid().AsUint() == e.gid) {
return edge;
}
}
return std::nullopt;
};
std::vector<GetPropElement> elements;
for (const auto &[vertex, maybe_edge] : req.vertices_and_edges) {
const auto &[label, pk_v] = vertex;
auto pk = ConvertPropertyVector(pk_v);
auto v_acc = dba.FindVertex(pk, view);
if (!v_acc) {
return msgs::GetPropertiesResponse{.result = msgs::GetPropertiesResponse::OUT_OF_SHARD_RANGE};
}
std::optional<EdgeAccessor> e_acc;
if (maybe_edge) {
e_acc = find_edge(*v_acc, *maybe_edge);
if (!e_acc) {
return msgs::GetPropertiesResponse{.result = msgs::GetPropertiesResponse::OUT_OF_SHARD_RANGE};
}
}
const auto *symbol = (maybe_edge) ? expr::identifier_edge_symbol : expr::identifier_node_symbol;
if (req.filter && !FilterOnVertex(dba, *v_acc, {*req.filter}, symbol, e_acc)) {
continue;
}
std::optional<GetPropElement> collected_properties;
collected_properties = collect_props(*v_acc, req.property_ids, view, e_acc);
if (!collected_properties) {
return msgs::GetPropertiesResponse{.result = msgs::GetPropertiesResponse::FAILURE};
}
if (collected_properties->ids.empty()) {
continue;
}
elements.push_back(std::move(*collected_properties));
}
if (!req.order_by.empty()) {
elements = OrderByElements(dba, req.order_by, std::move(elements));
}
std::vector<msgs::GetPropertiesResultRow> results;
results.reserve(elements.size());
const auto has_expr_to_evaluate = !req.expressions.empty();
size_t limit = elements.size();
if (req.limit && *req.limit < elements.size()) {
limit = *req.limit;
}
for (size_t index = 0; index != limit; ++index) {
auto &element = elements.at(index);
const auto id = element.vertex_acc.Id(view).GetValue();
std::optional<EdgeTypeId> e_type =
(element.edge_acc) ? std::make_optional(element.edge_acc->EdgeType()) : std::nullopt;
msgs::VertexId v_id{msgs::Label{id.primary_label}, ConvertValueVector(id.primary_key)};
results.push_back(msgs::GetPropertiesResultRow{
.vertex_and_edge = {.vertex = std::move(v_id), .edge = e_type},
.properies_and_ids = {
.ids = std::move(element.ids),
.properties = ConvertToValueVectorFromTypedValueVector(std::move(element.properties_order_by))}});
if (has_expr_to_evaluate) {
auto expression_results = ConvertToValueVectorFromTypedValueVector(
EvaluateVertexExpressions(dba, element.vertex_acc, req.expressions, expr::identifier_node_symbol));
results.back().evaluated_expressions = std::move(expression_results);
auto emplace_result_row =
[dba, transform_props, collect_props, has_expr_to_evaluate, &req](
const VertexAccessor &v_acc,
const std::optional<EdgeAccessor> e_acc) mutable -> ShardResult<msgs::GetPropertiesResultRow> {
auto maybe_id = v_acc.Id(view);
if (maybe_id.HasError()) {
return {maybe_id.GetError()};
}
const auto &id = maybe_id.GetValue();
std::optional<msgs::EdgeId> e_type;
if (e_acc) {
e_type = msgs::EdgeId{e_acc->Gid().AsUint()};
}
msgs::VertexId v_id{msgs::Label{id.primary_label}, ConvertValueVector(id.primary_key)};
auto maybe_props = collect_props(v_acc, e_acc);
if (maybe_props.HasError()) {
return {maybe_props.GetError()};
}
auto props = transform_props(std::move(maybe_props.GetValue()));
auto result = msgs::GetPropertiesResultRow{.vertex = std::move(v_id), .edge = e_type, .props = std::move(props)};
if (has_expr_to_evaluate) {
std::vector<Value> e_results;
if (e_acc) {
e_results =
ConvertToValueVectorFromTypedValueVector(EvaluateEdgeExpressions(dba, v_acc, *e_acc, req.expressions));
} else {
e_results = ConvertToValueVectorFromTypedValueVector(
EvaluateVertexExpressions(dba, v_acc, req.expressions, expr::identifier_node_symbol));
}
result.evaluated_expressions = std::move(e_results);
}
return {std::move(result)};
};
auto get_limit = [&req](const auto &elements) {
size_t limit = elements.size();
if (req.limit && *req.limit < elements.size()) {
limit = *req.limit;
}
return limit;
};
auto collect_response = [get_limit, &req](auto &elements, auto result_row_functor) {
msgs::GetPropertiesResponse response;
const auto limit = get_limit(elements);
for (size_t index = 0; index != limit; ++index) {
auto result_row = result_row_functor(elements[index]);
if (result_row.HasError()) {
return msgs::GetPropertiesResponse{.error = CreateErrorResponse(result_row.GetError(), req.transaction_id, "")};
}
response.result_row.push_back(std::move(result_row.GetValue()));
}
return response;
};
std::vector<VertexAccessor> vertices;
std::vector<EdgeAccessor> edges;
auto parse_and_filter = [dba, &vertices](auto &cont, auto projection, auto filter, auto maybe_get_edge) mutable {
for (const auto &elem : cont) {
const auto &[label, pk_v] = projection(elem);
auto pk = ConvertPropertyVector(pk_v);
auto v_acc = dba.FindVertex(pk, view);
if (!v_acc || filter(*v_acc, maybe_get_edge(elem))) {
continue;
}
vertices.push_back({*v_acc});
}
};
auto identity = [](auto &elem) { return elem; };
auto filter_vertex = [dba, req](const auto &acc, const auto & /*edge*/) mutable {
if (!req.filter) {
return false;
}
return !FilterOnVertex(dba, acc, {*req.filter});
};
auto filter_edge = [dba, &edges, &req, find_edge](const auto &acc, const auto &edge) mutable {
auto e_acc = find_edge(acc, edge);
if (!req.filter || !e_acc || !FilterOnEdge(dba, acc, *e_acc, {*req.filter})) {
return false;
}
edges.push_back(*e_acc);
return true;
};
// Handler logic here
if (!req.vertex_ids.empty()) {
parse_and_filter(req.vertex_ids, identity, filter_vertex, identity);
} else {
parse_and_filter(
req.vertices_and_edges, [](auto &e) { return e.first; }, filter_edge, [](auto &e) { return e.second; });
}
return msgs::GetPropertiesResponse{std::move(results), msgs::GetPropertiesResponse::SUCCESS};
if (!req.vertex_ids.empty()) {
if (!req.order_by.empty()) {
auto elements = OrderByVertices(dba, vertices, req.order_by);
return collect_response(elements, [emplace_result_row](auto &element) mutable {
return emplace_result_row(element.object_acc, std::nullopt);
});
}
return collect_response(vertices,
[emplace_result_row](auto &acc) mutable { return emplace_result_row(acc, std::nullopt); });
}
if (!req.order_by.empty()) {
auto elements = OrderByEdges(dba, edges, req.order_by, vertices);
return collect_response(elements, [emplace_result_row](auto &element) mutable {
return emplace_result_row(element.object_acc.first, element.object_acc.second);
});
}
struct ZipView {
ZipView(std::vector<VertexAccessor> &v, std::vector<EdgeAccessor> &e) : v(v), e(e) {}
size_t size() const { return v.size(); }
auto operator[](size_t index) { return std::make_pair(v[index], e[index]); }
private:
std::vector<VertexAccessor> &v;
std::vector<EdgeAccessor> &e;
};
ZipView vertices_and_edges(vertices, edges);
return collect_response(vertices_and_edges, [emplace_result_row](const auto &acc) mutable {
return emplace_result_row(acc.first, acc.second);
});
}
// TODO(kostasrim) Handle edges
} // namespace memgraph::storage::v3

View File

@ -482,7 +482,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithExpression
msgs::GetPropertiesResponse AttemptToGetProperties(ShardClient &client, std::vector<PropertyId> properties,
std::vector<msgs::VertexId> vertices,
std::vector<msgs::EdgeTypeId> edges,
std::vector<msgs::EdgeId> edges,
std::optional<size_t> limit = std::nullopt,
std::optional<uint64_t> filter_prop = std::nullopt,
bool edge = false,
@ -508,14 +508,25 @@ msgs::GetPropertiesResponse AttemptToGetProperties(ShardClient &client, std::vec
req.limit = limit;
}
req.expressions = {std::string("5 = 5")};
std::vector<msgs::VertexAndEdgeId> req_v;
std::vector<msgs::VertexId> req_v;
std::vector<msgs::EdgeId> req_e;
for (auto &v : vertices) {
req_v.push_back(msgs::VertexAndEdgeId{.vertex = std::move(v)});
req_v.push_back(std::move(v));
}
for (auto index = 0; index != edges.size(); ++index) {
req_v[index].edge = edges[index];
for (auto &e : edges) {
req_e.push_back(std::move(e));
}
if (!edges.empty()) {
MG_ASSERT(edges.size() == vertices.size());
size_t id = 0;
req.vertices_and_edges.reserve(req_v.size());
for (auto &v : req_v) {
req.vertices_and_edges.push_back({std::move(v), std::move(req_e[id++])});
}
} else {
req.vertex_ids = std::move(req_v);
}
req.vertices_and_edges = std::move(req_v);
while (true) {
auto read_res = client.SendReadRequest(req);
@ -1285,93 +1296,86 @@ void TestGetProperties(ShardClient &client) {
{
// No properties
const auto result = AttemptToGetProperties(client, {}, {v_id, v_id_2}, {}, std::nullopt, unique_prop_val_2);
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.empty());
}
{
// All properties
const auto result = AttemptToGetProperties(client, {prop_id_2, prop_id_4, prop_id_5}, {v_id, v_id_2, v_id_3}, {});
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.error);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(result.result_row.size() == 3);
for (const auto &elem : result.result_row) {
MG_ASSERT(elem.properies_and_ids.ids.size() == 3);
MG_ASSERT(elem.properies_and_ids.properties.size() == 3);
MG_ASSERT(elem.props.size() == 3);
}
}
{
// Two properties from two vertices with a filter on unique_prop_5
const auto result = AttemptToGetProperties(client, {prop_id_2, prop_id_4}, {v_id, v_id_2, v_id_5}, {}, std::nullopt,
unique_prop_val_5);
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 1);
}
{
// One property from three vertices.
const auto result = AttemptToGetProperties(client, {prop_id_2}, {v_id, v_id_2, v_id_3}, {});
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 3);
MG_ASSERT(result.result_row[0].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[1].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[2].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[0].props.size() == 1);
MG_ASSERT(result.result_row[1].props.size() == 1);
MG_ASSERT(result.result_row[2].props.size() == 1);
}
{
// Same as before but with limit of 1 row
const auto result =
AttemptToGetProperties(client, {prop_id_2}, {v_id, v_id_2, v_id_3}, {}, std::make_optional<size_t>(1));
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 1);
}
{
// Same as before but with a limit greater than the elements returned
const auto result =
AttemptToGetProperties(client, {prop_id_2}, {v_id, v_id_2, v_id_3}, {}, std::make_optional<size_t>(5));
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 3);
}
{
// Order by on `prop1` (descending)
const auto result = AttemptToGetProperties(client, {prop_id_2}, {v_id, v_id_2, v_id_3}, {}, std::nullopt,
std::nullopt, false, "prop1");
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 3);
MG_ASSERT(result.result_row[0].vertex_and_edge.vertex == v_id_3);
MG_ASSERT(result.result_row[1].vertex_and_edge.vertex == v_id_2);
MG_ASSERT(result.result_row[2].vertex_and_edge.vertex == v_id);
MG_ASSERT(result.result_row[0].vertex == v_id_3);
MG_ASSERT(result.result_row[1].vertex == v_id_2);
MG_ASSERT(result.result_row[2].vertex == v_id);
}
{
// Order by and filter on >= unique_prop_val_3 && assert result row data members
const auto result = AttemptToGetProperties(client, {prop_id_2}, {v_id, v_id_2, v_id_3, v_id_4, v_id_5}, {},
std::nullopt, unique_prop_val_3, false, "prop1");
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 3);
MG_ASSERT(result.result_row[0].vertex_and_edge.vertex == v_id_5);
MG_ASSERT(result.result_row[0].properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row[0].properies_and_ids.properties.front() == prim_key_5.front());
MG_ASSERT(result.result_row[0].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[0].properies_and_ids.ids.front() == prop_id_2);
MG_ASSERT(result.result_row[0].vertex == v_id_5);
MG_ASSERT(result.result_row[0].props.size() == 1);
MG_ASSERT(result.result_row[0].props.front().second == prim_key_5.front());
MG_ASSERT(result.result_row[0].props.size() == 1);
MG_ASSERT(result.result_row[0].props.front().first == prop_id_2);
MG_ASSERT(result.result_row[0].evaluated_expressions.size() == 1);
MG_ASSERT(result.result_row[0].evaluated_expressions.front() == msgs::Value(true));
MG_ASSERT(result.result_row[1].vertex_and_edge.vertex == v_id_4);
MG_ASSERT(result.result_row[1].properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row[1].properies_and_ids.properties.front() == prim_key_4.front());
MG_ASSERT(result.result_row[1].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[1].properies_and_ids.ids.front() == prop_id_2);
MG_ASSERT(result.result_row[1].vertex == v_id_4);
MG_ASSERT(result.result_row[1].props.size() == 1);
MG_ASSERT(result.result_row[1].props.front().second == prim_key_4.front());
MG_ASSERT(result.result_row[1].props.size() == 1);
MG_ASSERT(result.result_row[1].props.front().first == prop_id_2);
MG_ASSERT(result.result_row[1].evaluated_expressions.size() == 1);
MG_ASSERT(result.result_row[1].evaluated_expressions.front() == msgs::Value(true));
MG_ASSERT(result.result_row[2].vertex_and_edge.vertex == v_id_3);
MG_ASSERT(result.result_row[2].properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row[2].properies_and_ids.properties.front() == prim_key_3.front());
MG_ASSERT(result.result_row[2].properies_and_ids.ids.size() == 1);
MG_ASSERT(result.result_row[2].properies_and_ids.ids.front() == prop_id_2);
MG_ASSERT(result.result_row[2].vertex == v_id_3);
MG_ASSERT(result.result_row[2].props.size() == 1);
MG_ASSERT(result.result_row[2].props.front().second == prim_key_3.front());
MG_ASSERT(result.result_row[2].props.size() == 1);
MG_ASSERT(result.result_row[2].props.front().first == prop_id_2);
MG_ASSERT(result.result_row[2].evaluated_expressions.size() == 1);
MG_ASSERT(result.result_row[2].evaluated_expressions.front() == msgs::Value(true));
}
@ -1388,54 +1392,49 @@ void TestGetProperties(ShardClient &client) {
MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_3, unique_prop_val_4, edge_gid_2,
unique_edge_prop_id, edge_prop_val_2, {edge_type_id}));
const auto edge_prop_id = PropertyId::FromUint(unique_edge_prop_id);
std::vector<msgs::EdgeId> edge_ids = {{edge_gid}, {edge_gid_2}};
// no properties
{
const auto result = AttemptToGetProperties(client, {}, {v_id_2, v_id_3}, {edge_type_id, edge_type_id});
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
const auto result = AttemptToGetProperties(client, {}, {v_id_2, v_id_3}, edge_ids);
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.empty());
}
// properties for two vertices
{
const auto result = AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, {edge_type_id, edge_type_id});
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
const auto result = AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, edge_ids);
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 2);
}
// filter
{
const auto result = AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, {edge_type_id, edge_type_id},
{}, {edge_prop_val}, true);
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
const auto result =
AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, edge_ids, {}, {edge_prop_val}, true);
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 1);
MG_ASSERT(result.result_row.front().vertex_and_edge.edge);
MG_ASSERT(result.result_row.front().vertex_and_edge.edge.value() == edge_type_id);
MG_ASSERT(result.result_row.front().properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row.front().properies_and_ids.properties.front() ==
msgs::Value(static_cast<int64_t>(edge_prop_val)));
MG_ASSERT(result.result_row.front().edge);
MG_ASSERT(result.result_row.front().edge.value().gid == edge_gid);
MG_ASSERT(result.result_row.front().props.size() == 1);
MG_ASSERT(result.result_row.front().props.front().second == msgs::Value(static_cast<int64_t>(edge_prop_val)));
}
// Order by
{
const auto result = AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, {edge_type_id, edge_type_id},
{}, {}, true, "e_prop");
MG_ASSERT(result.result == msgs::GetPropertiesResponse::SUCCESS);
MG_ASSERT(!result.result_row.empty());
const auto result =
AttemptToGetProperties(client, {edge_prop_id}, {v_id_2, v_id_3}, edge_ids, {}, {}, true, "e_prop");
MG_ASSERT(!result.error);
MG_ASSERT(result.result_row.size() == 2);
MG_ASSERT(result.result_row[0].vertex_and_edge.vertex == v_id_3);
MG_ASSERT(result.result_row[0].vertex_and_edge.edge);
MG_ASSERT(result.result_row[0].vertex_and_edge.edge.value() == edge_type_id);
MG_ASSERT(result.result_row[0].properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row[0].properies_and_ids.properties.front() ==
msgs::Value(static_cast<int64_t>(edge_prop_val_2)));
MG_ASSERT(result.result_row[0].vertex == v_id_3);
MG_ASSERT(result.result_row[0].edge);
MG_ASSERT(result.result_row[0].edge.value().gid == edge_gid_2);
MG_ASSERT(result.result_row[0].props.size() == 1);
MG_ASSERT(result.result_row[0].props.front().second == msgs::Value(static_cast<int64_t>(edge_prop_val_2)));
MG_ASSERT(result.result_row[0].evaluated_expressions.size() == 1);
MG_ASSERT(result.result_row[0].evaluated_expressions.front() == msgs::Value(true));
MG_ASSERT(result.result_row[1].vertex_and_edge.vertex == v_id_2);
MG_ASSERT(result.result_row[1].vertex_and_edge.edge);
MG_ASSERT(result.result_row[1].vertex_and_edge.edge.value() == edge_type_id);
MG_ASSERT(result.result_row[1].properies_and_ids.properties.size() == 1);
MG_ASSERT(result.result_row[1].properies_and_ids.properties.front() ==
msgs::Value(static_cast<int64_t>(edge_prop_val)));
MG_ASSERT(result.result_row[1].vertex == v_id_2);
MG_ASSERT(result.result_row[1].edge);
MG_ASSERT(result.result_row[1].edge.value().gid == edge_gid);
MG_ASSERT(result.result_row[1].props.size() == 1);
MG_ASSERT(result.result_row[1].props.front().second == msgs::Value(static_cast<int64_t>(edge_prop_val)));
MG_ASSERT(result.result_row[1].evaluated_expressions.size() == 1);
MG_ASSERT(result.result_row[1].evaluated_expressions.front() == msgs::Value(true));
}