def neighbors(
        self,
        link_types: List[str] = [],
        target_types: List[str] = [],
        direction: str = "any",
        graph: str = "links",
        filter: List["GraphFilter"] = [],
        include_original: bool = False,
        min_hops: int = 1,
        max_hops: int = 1,
        offset: int = 0,
        count: int = 0,
        sorting: List[tuple[str, bool]] = [],
        user: "user.User | None" = None,
        include_tags: bool = True,
    ) -> tuple[
        dict[
            str,
            "observable.ObservableTypes | entity.EntityTypes | indicator.IndicatorTypes | tag.Tag",
        ],
        List[List["RelationshipTypes"]],
        int,
    ]:
        """Fetches neighbors of the YetiObject.

        Args:
            link_types: The types of link.
            target_types: The types of the target objects (as specified in the
                'type' field).
            direction: outbound, inbound, or any.
            include_original: Whether the original object is to be included in the
                result or not.
            min_hops: The minumum number of nodes to go through (defaults to 1:
                direct neighbors)
            max_hops: The maximum number of nodes to go through (defaults to 1:
                direct neighbors)
            offset: The number of results to skip.
            count: The number of results to return.
            sorting: A list of tuples containing the field to sort on and a boolean
                    indicating if it should be sorted in ascending order.
            user: The user requesting the data; used to take ACLs into account.
            include_tags: Whether to include tags in the result.

        Returns:
          Tuple[dict, list, int]:
            - the neighbors (vertices),
            - the relationships (edges),
            - total neighbor (vertices) count
        """
        query_filter = ""
        args = {
            "extended_id": self.extended_id,
            "@graph": graph,
        }
        sorts = []
        for field, asc in sorting:
            sorts.append(f"p.edges[0].{field} {'ASC' if asc else 'DESC'}")
        sorting_aql = f"SORT {', '.join(sorts)}" if sorts else ""

        if link_types:
            args["link_types"] = link_types
            query_filter = "FILTER e.type IN @link_types"
        if target_types:
            args["target_types"] = target_types
            query_filter = (
                "FILTER (v.type IN @target_types OR v.root_type IN @target_types)"
            )
        if filter:
            filters = []
            for i, f in enumerate(filter):
                if f.pathcompare.lower() not in {"any", "all", "none"}:
                    f.pathcompare = ""

                if f.operator.lower() not in {"=~", "==", "in"}:
                    f.operator = "=="

                # =~ not compatible with path operators
                if f.operator == "=~":
                    f.pathcompare = ""

                if f.operator in {"=~", "=="}:
                    filters.append(
                        f"(p.edges[*].@filter_key{i} {f.pathcompare} {f.operator} @filter_value{i} OR p.vertices[*].@filter_key{i} {f.pathcompare} {f.operator} @filter_value{i})"
                    )
                if f.operator == "in":
                    filters.append(
                        f"""COUNT(
                              FOR arr IN p.vertices[*].@filter_key{i}
                              FILTER COUNT(
                                FOR item in arr || []
                                FILTER REGEX_TEST(item, @filter_value{i}, true) RETURN arr
                              ) > 0
                              RETURN arr
                            ) > 0"""
                    )
                args[f"filter_key{i}"] = f.key
                args[f"filter_value{i}"] = f.value
            query_filter += f"FILTER {' OR '.join(filters)}"

        limit = ""
        if count != 0:
            limit += "LIMIT @offset, @count"
            args["offset"] = offset
            args["count"] = count

        args["min_hops"] = min_hops
        args["max_hops"] = max_hops
        if direction not in {"any", "inbound", "outbound"}:
            direction = "any"

        acl_query = ""
        if user and RBAC_ENABLED and not user.admin:
            acl_query = "LET acl = FIRST(FOR aclv in 1..2 inbound v acls FILTER aclv.username == @username RETURN true) or false\n\nfilter acl"
            args["username"] = user.username

        aql = f"""
        WITH observables, entities, dfiq, indicators

        FOR v, e, p IN @min_hops..@max_hops {direction} @extended_id @@graph
          OPTIONS {{ uniqueVertices: "path" }}
          {query_filter}
          {acl_query}
          LET vertices = p['vertices']
          {limit}
          {sorting_aql}
          RETURN {{ vertices: vertices, g: p }}
        """
        neighbors = self._db.aql.execute(
            aql, bind_vars=args, count=True, full_count=True
        )
        total = neighbors.statistics().get("fullCount", count)
        paths = []  # type: list[list[Relationship]]
        vertices = {}  # type: dict[str, ArangoYetiConnector]
        for path in neighbors:
            paths.append(self._build_edges(path["g"]["edges"]))
            self._build_vertices(vertices, path["vertices"])
        if not include_original:
            vertices.pop(self.extended_id, None)
        return vertices, paths, total or 0
Fetches neighbors of the YetiObject.

        Args:
            link_types: The types of link.
            target_types: The types of the target objects (as specified in the
                'type' field).
            direction: outbound, inbound, or any.
            include_original: Whether the original object is to be included in the
                result or not.
            min_hops: The minumum number of nodes to go through (defaults to 1:
                direct neighbors)
            max_hops: The maximum number of nodes to go through (defaults to 1:
                direct neighbors)
            offset: The number of results to skip.
            count: The number of results to return.
            sorting: A list of tuples containing the field to sort on and a boolean
                    indicating if it should be sorted in ascending order.
            user: The user requesting the data; used to take ACLs into account.
            include_tags: Whether to include tags in the result.

        Returns:
          Tuple[dict, list, int]:
            - the neighbors (vertices),
            - the relationships (edges),
            - total neighbor (vertices) count
        
core/database_arango.py