# Copyright 2021 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. # -*- coding: utf-8 -*- def parse(el, ignore_order): """ Function used to parse result element. Result element can be node, list, path, relationship, map or string. Function returns same string for two same elements, etc. if properties of two nodes are inverted, but labels and properties are the same, function will return the same string for them. For two differenet elements function will not return the same strings. @param el: string, element to parse. @param ignore_order: bool, ignore order inside of lists, etc. lists [1, 2, 3] and [2, 3, 1] are the same if ignore_order is true, else false. @return: Parsed string of element. """ if el.startswith('(') and el.endswith(')'): return parse_node(el, ignore_order) if el.startswith('<') and el.endswith('>'): return parse_path(el, ignore_order) if el.startswith('{') and el.endswith('}'): return parse_map(el, ignore_order) if el.startswith('[') and el.endswith(']'): if is_list(el): return parse_list(el, ignore_order) else: return parse_rel(el, ignore_order) return el def is_list(el): """ Function returns true if string el is a list, else false. @param el: string, element to check. @return: true if el is list. """ if el[1] == ':': return False return True def parse_path(path, ignore_order): """ Function used to parse path. @param path: string representing path @return: parsed path """ parsed_path = '<' dif_open_closed_brackets = 0 for i in range(1, len(path) - 1): if path[i] == '(' or path[i] == '{' or path[i] == '[': dif_open_closed_brackets += 1 if dif_open_closed_brackets == 1: start = i if path[i] == ')' or path[i] == '}' or path[i] == ']': dif_open_closed_brackets -= 1 if dif_open_closed_brackets == 0: parsed_path += parse(path[start:(i + 1)], ignore_order) elif dif_open_closed_brackets == 0: parsed_path += path[i] parsed_path += '>' return parsed_path def parse_node(node_str, ignore_order): """ Function used to parse node. @param node: string representing node @return: parsed node """ label = '' labels = [] props_start = None for i in range(1, len(node_str)): if node_str[i] == ':' or node_str[i] == ')' or node_str[i] == '{': if label.startswith(':'): labels.append(label) label = '' label += node_str[i] if node_str[i] == '{': props_start = i break labels.sort() parsed_node = '(' for label in labels: parsed_node += label if props_start is not None: parsed_node += parse_map( node_str[props_start:len(node_str) - 1], ignore_order) parsed_node += ')' return parsed_node def parse_map(props, ignore_order): """ Function used to parse map. @param props: string representing map @return: parsed map """ dif_open_closed_brackets = 0 prop = '' list_props = [] for i in range(1, len(props) - 1): if props[i] == ',' and dif_open_closed_brackets == 0: list_props.append(prop_to_str(prop, ignore_order)) prop = '' else: prop += props[i] if props[i] == '(' or props[i] == '{' or props[i] == '[': dif_open_closed_brackets += 1 elif props[i] == ')' or props[i] == '}' or props[i] == ']': dif_open_closed_brackets -= 1 if prop != '': list_props.append(prop_to_str(prop, ignore_order)) list_props.sort() return '{' + ','.join(list_props) + '}' def prop_to_str(prop, ignore_order): """ Function used to parse one pair of key, value in format 'key:value'. Value must be parsed, key is string. @param prop: string, pair key:value to parse @return: parsed prop """ key = prop.split(':', 1)[0] val = prop.split(':', 1)[1] return key + ":" + parse(val, ignore_order) def parse_list(l, ignore_order): """ Function used to parse list. @param l: string representing list @return: parsed list """ dif_open_closed_brackets = 0 el = '' list_el = [] for i in range(1, len(l) - 1): if l[i] == ',' and dif_open_closed_brackets == 0: list_el.append(parse(el, ignore_order)) el = '' else: el += l[i] if l[i] == '(' or l[i] == '{' or l[i] == '[': dif_open_closed_brackets += 1 elif l[i] == ')' or l[i] == '}' or l[i] == ']': dif_open_closed_brackets -= 1 if el != '': list_el.append(parse(el, ignore_order)) if ignore_order: list_el.sort() return '[' + ','.join(list_el) + ']' def parse_rel(rel, ignore_order): """ Function used to parse relationship. @param rel: string representing relationship @return: parsed relationship """ label = '' labels = [] props_start = None for i in range(1, len(rel)): if rel[i] == ':' or rel[i] == ']' or rel[i] == '{': if label.startswith(':'): labels.append(label) label = '' label += rel[i] if rel[i] == '{': props_start = i break labels.sort() parsed_rel = '[' for label in labels: parsed_rel += label if props_start is not None: parsed_rel += parse_map(rel[props_start:len(rel) - 1], ignore_order) parsed_rel += ']' return parsed_rel