Return list of objects which match all properties that are set
        (``not None``) using AND operator to all of them.

        Example:
            result = storage_handler.search_objects(
                DBPublication(isbn="azgabash")
            )

        Args:
            query (obj): Object implementing proper interface with some of the
                  properties set.

        Returns:
            list: List of matching objects or ``[]`` if no match was found.

        Raises:
            InvalidType: When the `query` doesn't implement required
                properties.

def search_objects(self, query):
        """
        Return list of objects which match all properties that are set
        (``not None``) using AND operator to all of them.

        Example:
            result = storage_handler.search_objects(
                DBPublication(isbn="azgabash")
            )

        Args:
            query (obj): Object implementing proper interface with some of the
                  properties set.

        Returns:
            list: List of matching objects or ``[]`` if no match was found.

        Raises:
            InvalidType: When the `query` doesn't implement required
                properties.
        """
        self._check_obj_properties(query, "query")

        # AND operator between results
        final_result = None
        for result in self._get_subset_matches(query):
            if final_result is None:
                final_result = result
                continue

            final_result = intersection(final_result, result)

        # if no result is found, `final_result` is None, and I want []
        if not final_result:
            return []

        return list(final_result)