def create_dangling(name: str, vault_name: str, all_communities: [str]) -> Node: n = Node(CAT_DANGLING, name=escape_cypher(name), community=all_communities.index(CAT_DANGLING), obsidian_url=escape_cypher(obsidian_url(name, vault_name))) n[PROP_VAULT] = vault_name return n
def add_rels_between_nodes(rels, src_node, trgt_node, subgraph: [Relationship]): # Adds all relations between src node and trgt node as described in rels to subgraph for rel in rels: properties = {} for property, value in rel.properties.items(): properties[property] = escape_cypher(str(value)) subgraph.append( Relationship(src_node, escape_cypher(rel.type), trgt_node, **properties))
def add_rels_between_nodes(rels, src_node, trgt_node, subgraph): # Adds all relations between src node and trgt node as described in rels to subgraph for rel in rels: properties = {} for property, value in rel.properties.items(): properties[property] = escape_cypher(str(value)) rel = Relationship(src_node, escape_cypher(rel.type), trgt_node, **properties) if subgraph is None: subgraph = rel else: subgraph = subgraph | rel return subgraph
def _on_deleted(event): name = note_name(event.src_path) node = self.nodes.match(name=name).first() in_rels = self.relationships.match([None, node]) if len(in_rels) > 0: # If there are still active incoming links, keep the node as a reference node.clear() node.clear_labels() node.add_label(CAT_DANGLING) node.name = escape_cypher(name) node.obsidian_url = escape_cypher(obsidian_url(name, self.vault_name)) self._clear_outgoing(node) else: self.graph.delete(node)
def _process_node_on_graph(self, note: Note): in_graph = self.nodes.match(name=note.name) if len(in_graph) == 0: # Create new node node = node_from_note(note) self.graph.create(node) return # Update node = in_graph.first() # Update labels node.clear_labels() note_tags = map(escape_cypher, note.tags) node.update_labels(note_tags) for tag in note_tags: if tag not in self.tags: properties = ['name', 'aliases'] # TODO: Is this too slow? if True: properties.append("content") create_index(self.graph, tag, properties) self.tags.add(tag) # Update properties node.clear() escaped_properties = {} for key, value in note.properties.items(): escaped_properties[key] = escape_cypher(str(value)) node.update(escaped_properties) self.graph.push(node) # Delete active relations self._clear_outgoing(node) # Insert up-to-date relations subgraph = None for trgt, rels in note.out_rels.items(): trgt_node = self.nodes.match(name=trgt) if len(trgt_node) == 0: trgt_node = Node(CAT_DANGLING, name=escape_cypher(trgt), obsidian_url=escape_cypher(obsidian_url(trgt, self.vault_name))) if subgraph is None: subgraph = trgt_node else: subgraph = subgraph | trgt_node else: trgt_node = trgt_node.first() # Possibly refactor this with subgraph = add_rels_between_nodes(rels, node, trgt_node, subgraph) if subgraph is not None: self.graph.create(subgraph)
def node_from_note(note: Note) -> Node: tags = [CAT_NO_TAGS] if note.tags: tags = list(map(escape_cypher, note.tags)) properties = {} for property, value in note.properties.items(): properties[property] = escape_cypher(str(value)) return Node(*tags, **properties)
def _on_deleted(event): if smdc.DEBUG: print("On deleted", event.src_path, flush=True) name = note_name(event.src_path) node = self.nodes.match(name=name).first() node_id = node.identity in_rels = self.relationships.match([None, node]) if len(in_rels) > 0: # If there are still active incoming links, keep the node as a reference node.clear() node.clear_labels() node.add_label(CAT_DANGLING) node.name = escape_cypher(name) node.obsidian_url = escape_cypher( obsidian_url(name, self.vault_name)) self._clear_outgoing(node) else: self.graph.delete(node) print(f"onSMDDeletedEvent/{node_id}", flush=True)
def get_community(note: Note, communities: [str], community_type: str): if community_type == "tags": if note.tags: community = escape_cypher(note.tags[0]) else: community = CAT_NO_TAGS elif community_type == "folders": community = str(Path(note.properties[PROP_PATH]).parent) if community not in communities: communities.append(community) return communities.index(community)
def node_from_note(note: Note, all_tags: [str], all_communities: [str], community_type: str) -> Node: tags = [CAT_NO_TAGS] if note.tags: tags = list(map(escape_cypher, note.tags)) for tag in tags: if tag not in all_tags: all_tags.append(tag) properties = {} for property, value in note.properties.items(): properties[property] = escape_cypher(str(value)) properties[PROP_COMMUNITY] = get_community(note, all_communities, community_type) return Node(*tags, **properties)
def _process_node_on_graph(self, note: Note): if smdc.DEBUG: print(note, flush=True) in_graph = self.nodes.match(**{ 'name': note.name, PROP_VAULT: self.vault_name }) if len(in_graph) == 0: # Create new node node = node_from_note(note, self.tags, self.communities, self.args.community) if smdc.DEBUG: print("creating") print(node, flush=True) self.graph.create(node) return # Update node = in_graph.first() if smdc.DEBUG: print("updating") print(node, flush=True) # Update labels node.clear_labels() note_tags = [CAT_NO_TAGS] if note.tags: note_tags = list(map(escape_cypher, note.tags)) node.update_labels(note_tags) for tag in note_tags: if tag not in self.tags: create_index(self.graph, tag) self.tags.append(tag) # Update properties node.clear() escaped_properties = {} for key, value in note.properties.items(): escaped_properties[key] = escape_cypher(str(value)) escaped_properties[PROP_COMMUNITY] = get_community( note, self.communities, self.args.community) node.update(escaped_properties) self.graph.push(node) # # Delete active relations # self._clear_outgoing(node) # Insert up-to-date relations rels_to_create = [] nodes_to_create = [] not_matched_active_rels = list( map(lambda r: r.identity, self.relationships.match([node, None]))) for trgt, rels in note.out_rels.items(): trgt_node = self.nodes.match(**{ 'name': trgt, PROP_VAULT: self.vault_name }) if len(trgt_node) == 0: trgt_node = create_dangling(trgt, self.vault_name, self.tags) nodes_to_create.append(trgt_node) else: trgt_node = trgt_node.first() # Possibly refactor this with for i, rel in enumerate(rels): properties = {} for property, value in rel.properties.items(): properties[property] = escape_cypher(str(value)) rel_type = escape_cypher(rel.type) found_rel = False active_rels = list( filter(lambda r: r.identity in not_matched_active_rels, self.relationships.match([node, None]))) # Update instead of removing makes sure the relationship has a persistent id for active_rel in active_rels: walks = list(walk(active_rel)) if type(active_rel ).__name__ == rel_type and walks[2] == trgt_node: # Maybe this can leave dangling properties? But that'' an edge case. Not sure how to clear properties. active_rel.clear() active_rel.update(properties) self.graph.push(active_rel) found_rel = True not_matched_active_rels.remove(active_rel.identity) break if not found_rel: rels_to_create.append( Relationship(node, rel_type, trgt_node, **properties)) if rels_to_create or nodes_to_create: self.graph.create( Subgraph(nodes=nodes_to_create, relationships=rels_to_create)) if len(not_matched_active_rels) > 0: rels = list( filter(lambda r: r.identity in not_matched_active_rels, self.relationships.match([node, None]))) if len(rels) > 0: self.graph.separate(Subgraph(relationships=rels)) print("onSMDRelDeletedEvent/" + "/".join(map(str, not_matched_active_rels)))