Example #1
0
    def __handle_primitive_event_or_nested_structure(self, tree_plan_leaf: TreePlanLeafNode,
                                                     current_operator: PatternStructure,
                                                     sliding_window: timedelta, parent: Node,
                                                     consumption_policy: ConsumptionPolicy):
        """
        Constructs a single leaf node or a subtree with nested structure according to the input parameters.
        """
        if isinstance(current_operator, PrimitiveEventStructure):
            # the current operator is a primitive event - we should simply create a leaf
            event = current_operator
            if consumption_policy is not None and \
                    consumption_policy.should_register_event_type_as_single(False, event.type):
                parent.register_single_event_type(event.type)
            return LeafNode(sliding_window, tree_plan_leaf.event_index, event, parent)

        if isinstance(current_operator, UnaryStructure):
            # the current operator is a unary operator hiding a nested pattern structure
            unary_node = self.__create_internal_node_by_operator(current_operator, sliding_window, parent)
            nested_operator = current_operator.arg
            child = self.__construct_tree(nested_operator, Tree.__create_nested_structure(nested_operator),
                                          Tree.__get_operator_arg_list(nested_operator), sliding_window, unary_node,
                                          consumption_policy)
            unary_node.set_subtree(child)
            return unary_node

        # the current operator is a nested binary operator
        return self.__construct_tree(current_operator, Tree.__create_nested_structure(current_operator),
                                     current_operator.args, sliding_window, parent, consumption_policy)
Example #2
0
    def handle_new_partial_match(self, partial_match_source: Node):
        """
        Internal node's update for a new partial match in one of the subtrees.
        """
        if partial_match_source == self._left_subtree:
            other_subtree = self._right_subtree
        elif partial_match_source == self._right_subtree:
            other_subtree = self._left_subtree
        else:
            raise Exception()  # should never happen

        new_partial_match = partial_match_source.get_last_unhandled_partial_match_by_parent(
            self)
        new_pm_key = partial_match_source.get_storage_unit().get_key_function()
        first_event_defs = partial_match_source.get_event_definitions_by_parent(
            self)
        other_subtree.clean_expired_partial_matches(
            new_partial_match.last_timestamp)
        partial_matches_to_compare = other_subtree.get_partial_matches(
            new_pm_key(new_partial_match))
        second_event_defs = other_subtree.get_event_definitions_by_parent(self)

        self.clean_expired_partial_matches(new_partial_match.last_timestamp)

        # given a partial match from one subtree, for each partial match
        # in the other subtree we check for new partial matches in this node.
        self._try_create_new_matches(new_partial_match,
                                     partial_matches_to_compare,
                                     first_event_defs, second_event_defs)
Example #3
0
 def replace_subtree(self, old_node: Node, new_node: Node):
     """
     Replaces the child of this node provided as old_node with new_node.
     """
     left = self.get_left_subtree()
     right = self.get_right_subtree()
     if left == old_node:
         self.set_subtrees(new_node, right)
     elif right == old_node:
         self.set_subtrees(left, new_node)
     else:
         raise Exception("old_node must contain one of this node's children")
     new_node.add_parent(self)
Example #4
0
 def __find_and_merge_node_into_subtree(self, root: Node, node: Node):
     """
     This method is trying to find node in the subtree of root (or an equivalent node).
     If such a node is found, it merges the equivalent nodes.
     """
     if root.is_equivalent(node):
         self.__merge_nodes(root, node)
         return True
     elif isinstance(root, BinaryNode):
         return self.__find_and_merge_node_into_subtree(root.get_left_subtree(), node) or \
                self.__find_and_merge_node_into_subtree(root.get_right_subtree(), node)
     elif isinstance(root, UnaryNode):
         return self.__find_and_merge_node_into_subtree(root.get_child(), node)
     return False
Example #5
0
 def __try_to_share_and_merge_nodes(self, root: Node, node: Node):
     """
     This method is trying to share the node (and its subtree) and the tree of root.
     If the root and node are not equivalent, trying to share the children of node and root.
     """
     if self.__find_and_merge_node_into_subtree(root, node):
         return True
     if isinstance(node, BinaryNode):
         left_merge = self.__try_to_share_and_merge_nodes(root, node.get_left_subtree())
         if left_merge:
             return True
         return self.__try_to_share_and_merge_nodes(root, node.get_right_subtree())
     if isinstance(node, UnaryNode):
         return self.__try_to_share_and_merge_nodes(root, node.get_child())
     return False
Example #6
0
 def _validate_new_match(self, events_for_new_match: List[Event]):
     """
     Validates the condition stored in this node on the given set of events.
     """
     if not Node._validate_new_match(self, events_for_new_match):
         return False
     return self._condition.eval([e.payload for e in events_for_new_match])
Example #7
0
 def set_subtree(self, child: Node):
     """
     Sets the child node of this node.
     """
     self._child = child
     # only the positive child definitions should be applied on this node
     self._event_defs = child.get_positive_event_definitions()
Example #8
0
    def __handle_primitive_event(self, tree_plan_leaf: TreePlanLeafNode,
                                 primitive_event_structure: PatternStructure,
                                 pattern_params: PatternParameters,
                                 parent: Node,
                                 consumption_policy: ConsumptionPolicy):
        """
        Creates a leaf node for a primitive events.
        """
        # this is a temporary hack used until the procedure is modified to extract event details from tree plan leaves
        if isinstance(primitive_event_structure, NegationOperator):
            primitive_event_structure = primitive_event_structure.arg

        if not isinstance(primitive_event_structure, PrimitiveEventStructure):
            raise Exception("Illegal operator for a tree leaf: %s" %
                            (primitive_event_structure, ))
        if consumption_policy is not None and \
                consumption_policy.should_register_event_type_as_single(False, primitive_event_structure.type):
            parent.register_single_event_type(primitive_event_structure.type)
        return LeafNode(pattern_params, tree_plan_leaf.event_index,
                        primitive_event_structure, parent)
Example #9
0
    def flush_pending_matches(self, last_timestamp: datetime = None):
        """
        Releases the partial matches in the pending matches buffer. If the timestamp is provided, only releases
        expired matches.
        """
        if last_timestamp is not None:
            self.__pending_partial_matches = sorted(
                self.__pending_partial_matches,
                key=lambda x: x.first_timestamp)
            count = find_partial_match_by_timestamp(
                self.__pending_partial_matches,
                last_timestamp - self._sliding_window)
            matches_to_flush = self.__pending_partial_matches[:count]
            self.__pending_partial_matches = self.__pending_partial_matches[
                count:]
        else:
            matches_to_flush = self.__pending_partial_matches

        # since matches_to_flush could be expired, we need to temporarily disable timestamp checks
        Node._toggle_enable_partial_match_expiration(False)
        for partial_match in matches_to_flush:
            super()._add_partial_match(partial_match)
        Node._toggle_enable_partial_match_expiration(True)
Example #10
0
    def handle_new_partial_match(self, partial_match_source: Node):
        """
        For positive partial matches, activates the flow of the superclass. For negative partial matches, does nothing
        for bounded events (as nothing should be done in this case), otherwise checks whether existing positive matches
        must be invalidated and handles them accordingly.
        """
        if partial_match_source == self._positive_subtree:
            # a new positive partial match has arrived
            super().handle_new_partial_match(partial_match_source)
            return
        # a new negative partial match has arrived
        if not self.__is_unbounded:
            # no unbounded negatives - there is nothing to do
            return

        # this partial match contains unbounded negative events
        first_unbounded_node = self.get_first_unbounded_negative_node()
        positive_event_defs = first_unbounded_node.get_positive_event_definitions(
        )

        unbounded_negative_partial_match = partial_match_source.get_last_unhandled_partial_match_by_parent(
            self)
        negative_event_defs = partial_match_source.get_event_definitions_by_parent(
            self)

        matches_to_keep = []
        for positive_partial_match in first_unbounded_node.__pending_partial_matches:
            combined_event_list = self._merge_events_for_new_match(
                positive_event_defs, negative_event_defs,
                positive_partial_match.events,
                unbounded_negative_partial_match.events)
            if not self._validate_new_match(combined_event_list):
                # this positive match should still be kept
                matches_to_keep.append(positive_partial_match)

        first_unbounded_node.__pending_partial_matches = matches_to_keep
Example #11
0
 def __merge_nodes(self, node: Node, other: Node):
     """
     Merge two nodes, and update all the required information
     """
     # merges other into node
     if node.get_sliding_window() < other.get_sliding_window():
         node.propagate_sliding_window(other.get_sliding_window())
     node.add_pattern_ids(other.get_pattern_ids())
     other_parents = other.get_parents()
     if other_parents is not None:
         for parent in other_parents:
             if isinstance(parent, UnaryNode):
                 parent.replace_subtree(node)
             elif isinstance(parent, BinaryNode):
                 parent.replace_subtree(other, node)
     else:
         # other is an output node in it's tree. the new output node of the old_tree is node
         if not node.is_output_node():
             node.set_is_output_node(True)
             self.__output_nodes.append(node)
             # other is already in self.__output_nodes, therefore we need to remove it
         self.__output_nodes.remove(other)
         other_id = list(other.get_pattern_ids())[0]
         self.__pattern_to_output_node_dict[other_id] = node
Example #12
0
 def replace_subtree(self, child: Node):
     """
     Replaces the child of this node with the given node.
     """
     self.set_subtree(child)
     child.add_parent(self)