<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def search(self, files=None, defined_fields=None, **kwargs):
 """Search files in the layout by metadata fields. Args: files (list):
 Optional list of names of files to search. If None, all files in the layout are scanned. defined_fields (list):
 Optional list of names of fields that must be defined in the JSON sidecar in order to consider the file a match, but which don't need to match any particular value. kwargs: Optional keyword arguments defining search constraints; keys are names of metadata fields, and values are the values to match those fields against (e.g., SliceTiming=0.017) would return all files that have a SliceTiming value of 0.071 in metadata. Returns: A list of filenames that match all constraints. """


        if defined_fields is None:
            defined_fields = []

        all_keys = set(defined_fields) | set(kwargs.keys())
        if not all_keys:
            raise ValueError("At least one field to search on must be passed.")

        # If no list of files is passed, use all files in layout
        if files is None:
            files = set(self.layout.files.keys())

        # Index metadata for any previously unseen files
        for f in files:
            self.index_file(f)

        # Get file intersection of all kwargs keys--this is fast
        filesets = [set(self.key_index.get(k, [])) for k in all_keys]
        matches = reduce(lambda x, y: x & y, filesets)

        if files is not None:
            matches &= set(files)

        if not matches:
            return []

        def check_matches(f, key, val):
            if isinstance(val, six.string_types) and '*' in val:
                val = ('^%s$' % val).replace('*', ".*")
                return re.search(str(self.file_index[f][key]), val) is not None
            else:
                return val == self.file_index[f][key]

        # Serially check matches against each pattern, with early termination
        for k, val in kwargs.items():
            matches = list(filter(lambda x: check_matches(x, k, val), matches))
            if not matches:
                return []

        return matches