Skip to content

Commit c916563

Browse files
authored
Add map procedures (#282)
1 parent 5ca77f5 commit c916563

File tree

66 files changed

+649
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+649
-13
lines changed

Dockerfile

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,6 @@ RUN apt-get update && apt-get install -y \
3535
&& rm memgraph.deb \
3636
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
3737

38-
# Install required packages from Debian Sid
39-
RUN echo 'deb http://deb.debian.org/debian sid main' > /etc/apt/sources.list.d/debian-sid.list
40-
RUN apt update && apt install -y g++-11 -t sid
41-
42-
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 50
43-
4438
ENV LD_LIBRARY_PATH /usr/lib/memgraph/query_modules
4539

4640
# Memgraph listens for Bolt Protocol on this port by default.

Dockerfile.cugraph

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,6 @@ RUN apt-get update && apt-get install -y \
5454
cmake `mage-memgraph` \
5555
--no-install-recommends
5656

57-
# Install required packages from Debian Sid
58-
RUN echo 'deb http://deb.debian.org/debian sid main' > /etc/apt/sources.list.d/debian-sid.list
59-
RUN apt update && apt install -y g++-11 -t sid
60-
61-
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 50
62-
6357
ENV PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/cmake:/usr/lib/cmake
6458

6559
# Memgraph listens for Bolt Protocol on this port by default.

cpp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,6 @@ add_subdirectory(node_similarity_module)
145145
add_subdirectory(distance_calculator)
146146
add_subdirectory(do_module)
147147
add_subdirectory(periodic_module)
148+
add_subdirectory(map_module)
148149
add_subdirectory(collections_module)
149150
add_cugraph_subdirectory(cugraph_module)

cpp/map_module/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(map_module_src
2+
map_module.cpp
3+
algorithm/map.cpp)
4+
5+
add_query_module(map 1 "${map_module_src}")
6+
7+
target_link_libraries(map PRIVATE fmt::fmt)

cpp/map_module/algorithm/map.cpp

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
#include <fmt/format.h>
2+
#include <list>
3+
#include <sstream>
4+
5+
#include "map.hpp"
6+
7+
const auto number_of_elements_in_pair = 2;
8+
9+
void Map::FromNodes(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
10+
mgp::memory = memory;
11+
const auto arguments = mgp::List(args);
12+
const auto record_factory = mgp::RecordFactory(result);
13+
14+
try {
15+
const auto label{arguments[0].ValueString()};
16+
const auto property{arguments[1].ValueString()};
17+
mgp::Map map_result{};
18+
19+
const auto all_nodes = mgp::Graph(memgraph_graph).Nodes();
20+
for (const auto node : all_nodes) {
21+
if (!node.HasLabel(label) || !node.Properties().contains(std::string(property))) continue;
22+
23+
std::ostringstream oss;
24+
oss << node.GetProperty(std::string(property));
25+
const auto key = oss.str();
26+
27+
mgp::Map map{};
28+
map.Update("identity", mgp::Value(node.Id().AsInt()));
29+
30+
mgp::List labels{};
31+
for (const auto &label : node.Labels()) {
32+
labels.AppendExtend(mgp::Value(label));
33+
}
34+
map.Update("labels", mgp::Value(std::move(labels)));
35+
36+
const auto property_map = node.Properties();
37+
mgp::Map properties{};
38+
for (const auto &[key, value] : property_map) {
39+
properties.Insert(key, value);
40+
}
41+
map.Update("properties", mgp::Value(std::move(properties)));
42+
43+
map_result.Update(key, mgp::Value(std::move(map)));
44+
}
45+
46+
auto record = record_factory.NewRecord();
47+
record.Insert(std::string(kResultFromNodes).c_str(), map_result);
48+
49+
} catch (const std::exception &e) {
50+
record_factory.SetErrorMessage(e.what());
51+
return;
52+
}
53+
}
54+
55+
void Map::FromValues(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
56+
mgp::memory = memory;
57+
const auto arguments = mgp::List(args);
58+
const auto record_factory = mgp::RecordFactory(result);
59+
60+
try {
61+
const auto values{arguments[0].ValueList()};
62+
mgp::Map map{};
63+
64+
if (values.Size() % 2) {
65+
throw mgp::ValueException("List needs to have an even number of elements");
66+
}
67+
68+
auto iterator = values.begin();
69+
while (iterator != values.end()) {
70+
std::ostringstream oss;
71+
oss << *iterator;
72+
const auto key = oss.str();
73+
74+
++iterator;
75+
map.Update(key, *iterator);
76+
++iterator;
77+
}
78+
79+
auto record = record_factory.NewRecord();
80+
record.Insert(std::string(kResultFromValues).c_str(), map);
81+
82+
} catch (const std::exception &e) {
83+
record_factory.SetErrorMessage(e.what());
84+
return;
85+
}
86+
}
87+
88+
void Map::SetKey(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
89+
mgp::memory = memory;
90+
const auto arguments = mgp::List(args);
91+
const auto record_factory = mgp::RecordFactory(result);
92+
93+
try {
94+
auto map = arguments[0].ValueMap();
95+
const auto key{arguments[1].ValueString()};
96+
const auto value{arguments[2]};
97+
map.Update(key, std::move(value));
98+
99+
auto record = record_factory.NewRecord();
100+
record.Insert(std::string(kResultSetKey).c_str(), std::move(map));
101+
102+
} catch (const std::exception &e) {
103+
record_factory.SetErrorMessage(e.what());
104+
return;
105+
}
106+
}
107+
108+
void Map::RemoveRecursion(mgp::Map &result, bool recursive, std::string_view key) {
109+
for (auto element : result) {
110+
if (element.key == key) {
111+
result.Erase(element.key);
112+
continue;
113+
}
114+
if (element.value.IsMap() && recursive) {
115+
// TO-DO no need for non_const_value_map in new version of memgraph
116+
mgp::Map non_const_value_map = mgp::Map(std::move(element.value.ValueMap()));
117+
RemoveRecursion(non_const_value_map, recursive, key);
118+
if (non_const_value_map.Empty()) {
119+
result.Erase(element.key);
120+
continue;
121+
}
122+
result.Update(element.key, mgp::Value(std::move(non_const_value_map)));
123+
}
124+
}
125+
}
126+
127+
void Map::RemoveKey(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
128+
mgp::memory = memory;
129+
const auto arguments = mgp::List(args);
130+
const auto record_factory = mgp::RecordFactory(result);
131+
try {
132+
const auto map = arguments[0].ValueMap();
133+
const auto key = arguments[1].ValueString();
134+
const auto recursive = arguments[2].ValueBool();
135+
136+
mgp::Map map_removed = mgp::Map(std::move(map));
137+
138+
RemoveRecursion(map_removed, recursive, key);
139+
140+
auto record = record_factory.NewRecord();
141+
record.Insert(std::string(kResultRemoveKey).c_str(), std::move(map_removed));
142+
143+
} catch (const std::exception &e) {
144+
record_factory.SetErrorMessage(e.what());
145+
return;
146+
}
147+
}
148+
149+
void Map::FromPairs(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
150+
mgp::memory = memory;
151+
const auto arguments = mgp::List(args);
152+
const auto record_factory = mgp::RecordFactory(result);
153+
try {
154+
const auto list = arguments[0].ValueList();
155+
156+
mgp::Map pairs_map;
157+
158+
for (const auto inside_list : list) {
159+
if (inside_list.ValueList().Size() != number_of_elements_in_pair) {
160+
throw mgp::IndexException(
161+
fmt::format("Pairs must consist of {} elements exactly.", number_of_elements_in_pair));
162+
}
163+
if (!inside_list.ValueList()[0].IsString()) {
164+
throw mgp::ValueException("All keys have to be type string.");
165+
}
166+
pairs_map.Update(inside_list.ValueList()[0].ValueString(), std::move(inside_list.ValueList()[1]));
167+
}
168+
169+
auto record = record_factory.NewRecord();
170+
record.Insert(std::string(kResultFromPairs).c_str(), std::move(pairs_map));
171+
172+
} catch (const std::exception &e) {
173+
record_factory.SetErrorMessage(e.what());
174+
return;
175+
}
176+
}
177+
178+
void Map::Merge(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
179+
mgp::memory = memory;
180+
const auto arguments = mgp::List(args);
181+
const auto record_factory = mgp::RecordFactory(result);
182+
try {
183+
const auto map1 = arguments[0].ValueMap();
184+
const auto map2 = arguments[1].ValueMap();
185+
186+
mgp::Map merged_map = mgp::Map(std::move(map2));
187+
188+
for (const auto element : map1) {
189+
if (merged_map.At(element.key).IsNull()) {
190+
merged_map.Insert(element.key, element.value);
191+
}
192+
}
193+
194+
auto record = record_factory.NewRecord();
195+
record.Insert(std::string(kResultMerge).c_str(), std::move(merged_map));
196+
197+
} catch (const std::exception &e) {
198+
record_factory.SetErrorMessage(e.what());
199+
return;
200+
}
201+
}
202+
203+
void Map::FlattenRecursion(mgp::Map &result, const mgp::Map &input, const std::string &key,
204+
const std::string &delimiter) {
205+
for (auto element : input) {
206+
std::string el_key(element.key);
207+
if (element.value.IsMap()) {
208+
FlattenRecursion(result, element.value.ValueMap(), key + el_key + delimiter, delimiter);
209+
} else {
210+
result.Insert(key + el_key, element.value);
211+
}
212+
}
213+
}
214+
215+
void Map::Flatten(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
216+
mgp::memory = memory;
217+
const auto arguments = mgp::List(args);
218+
const auto record_factory = mgp::RecordFactory(result);
219+
try {
220+
const mgp::Map map = arguments[0].ValueMap();
221+
const std::string delimiter(arguments[1].ValueString());
222+
mgp::Map result_map = mgp::Map();
223+
FlattenRecursion(result_map, map, "", delimiter);
224+
auto record = record_factory.NewRecord();
225+
record.Insert(std::string(Map::kReturnValueFlatten).c_str(), std::move(result_map));
226+
227+
} catch (const std::exception &e) {
228+
record_factory.SetErrorMessage(e.what());
229+
return;
230+
}
231+
}
232+
233+
void Map::FromLists(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
234+
mgp::memory = memory;
235+
auto arguments = mgp::List(args);
236+
const auto record_factory = mgp::RecordFactory(result);
237+
try {
238+
mgp::List list1 = arguments[0].ValueList();
239+
mgp::List list2 = arguments[1].ValueList();
240+
241+
const auto expected_list_size = list1.Size();
242+
if (expected_list_size != list2.Size() || expected_list_size == 0) {
243+
throw mgp::ValueException("Lists must be of same size and not empty");
244+
}
245+
mgp::Map result = mgp::Map();
246+
for (size_t i = 0; i < expected_list_size; i++) {
247+
result.Update(std::move(list1[i].ValueString()), std::move(list2[i]));
248+
}
249+
auto record = record_factory.NewRecord();
250+
record.Insert(std::string(Map::kReturnListFromLists).c_str(), std::move(result));
251+
252+
} catch (const std::exception &e) {
253+
record_factory.SetErrorMessage(e.what());
254+
return;
255+
}
256+
}
257+
258+
void Map::RemoveRecursionSet(mgp::Map &result, bool recursive, std::unordered_set<std::string_view> &set) {
259+
for (auto element : result) {
260+
bool inSet = false;
261+
if (set.find(element.key) != set.end()) {
262+
inSet = true;
263+
}
264+
if (inSet) {
265+
result.Erase(element.key);
266+
continue;
267+
}
268+
if (element.value.IsMap() && recursive) {
269+
mgp::Map non_const_value_map = mgp::Map(std::move(element.value.ValueMap()));
270+
RemoveRecursionSet(non_const_value_map, recursive, set);
271+
if (non_const_value_map.Empty()) {
272+
result.Erase(element.key);
273+
continue;
274+
}
275+
result.Update(element.key, mgp::Value(std::move(non_const_value_map)));
276+
}
277+
}
278+
}
279+
280+
void Map::RemoveKeys(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
281+
mgp::memory = memory;
282+
auto arguments = mgp::List(args);
283+
const auto record_factory = mgp::RecordFactory(result);
284+
try {
285+
mgp::Map map = arguments[0].ValueMap();
286+
const mgp::List list = arguments[1].ValueList();
287+
bool recursive = arguments[2].ValueBool();
288+
std::unordered_set<std::string_view> set;
289+
for (auto elem : list) {
290+
set.insert(std::move(elem.ValueString()));
291+
}
292+
RemoveRecursionSet(map, recursive, set);
293+
auto record = record_factory.NewRecord();
294+
record.Insert(std::string(Map::kReturnRemoveKeys).c_str(), std::move(map));
295+
296+
} catch (const std::exception &e) {
297+
record_factory.SetErrorMessage(e.what());
298+
return;
299+
}
300+
}

0 commit comments

Comments
 (0)