def find_data(self, subject_ids=None, visit_ids=None, **kwargs):
        """
        Find all filesets, fields and provenance records within an XNAT project

        Parameters
        ----------
        subject_ids : list(str)
            List of subject IDs with which to filter the tree with. If
            None all are returned
        visit_ids : list(str)
            List of visit IDs with which to filter the tree with. If
            None all are returned

        Returns
        -------
        filesets : list[Fileset]
            All the filesets found in the repository
        fields : list[Field]
            All the fields found in the repository
        records : list[Record]
            The provenance records found in the repository
        """
        subject_ids = self.convert_subject_ids(subject_ids)
        # Add derived visit IDs to list of visit ids to filter
        all_filesets = []
        all_fields = []
        all_records = []
        # Note we prefer the use of raw REST API calls here for performance
        # reasons over using XnatPy's data structures.
        with self:
            # Get map of internal subject IDs to subject labels in project
            subject_xids_to_labels = {
                s['ID']: s['label'] for s in self._login.get_json(
                    '/data/projects/{}/subjects'.format(self.project_id))[
                        'ResultSet']['Result']}
            # Get list of all sessions within project
            session_xids = [
                s['ID'] for s in self._login.get_json(
                    '/data/projects/{}/experiments'.format(self.project_id))[
                        'ResultSet']['Result']
                if (self.session_filter is None or
                    self.session_filter.match(s['label']))]
            for session_xid in session_xids:
                session_json = self._login.get_json(
                    '/data/projects/{}/experiments/{}'.format(
                        self.project_id, session_xid))['items'][0]
                subject_xid = session_json['data_fields']['subject_ID']
                subject_id = subject_xids_to_labels[subject_xid]
                session_label = session_json['data_fields']['label']
                session_uri = (
                    '/data/archive/projects/{}/subjects/{}/experiments/{}'
                    .format(self.project_id, subject_xid, session_xid))
                # Get field values. We do this first so we can check for the
                # DERIVED_FROM_FIELD to determine the correct session label and
                # study name
                field_values = {}
                try:
                    fields_json = next(
                        c['items'] for c in session_json['children']
                        if c['field'] == 'fields/field')
                except StopIteration:
                    pass
                else:
                    for js in fields_json:
                        try:
                            value = js['data_fields']['field']
                        except KeyError:
                            pass
                        else:
                            field_values[js['data_fields']['name']] = value
                # Extract study name and derived-from session
                if self.DERIVED_FROM_FIELD in field_values:
                    df_sess_label = field_values.pop(self.DERIVED_FROM_FIELD)
                    from_study = session_label[len(df_sess_label) + 1:]
                    session_label = df_sess_label
                else:
                    from_study = None
                # Strip subject ID from session label if required
                if session_label.startswith(subject_id + '_'):
                    visit_id = session_label[len(subject_id) + 1:]
                else:
                    visit_id = session_label
                # Strip project ID from subject ID if required
                if subject_id.startswith(self.project_id + '_'):
                    subject_id = subject_id[len(self.project_id) + 1:]
                # Check subject is summary or not and whether it is to be
                # filtered
                if subject_id == XnatRepo.SUMMARY_NAME:
                    subject_id = None
                elif not (subject_ids is None or subject_id in subject_ids):
                    continue
                # Check visit is summary or not and whether it is to be
                # filtered
                if visit_id == XnatRepo.SUMMARY_NAME:
                    visit_id = None
                elif not (visit_ids is None or visit_id in visit_ids):
                    continue
                # Determine frequency
                if (subject_id, visit_id) == (None, None):
                    frequency = 'per_study'
                elif visit_id is None:
                    frequency = 'per_subject'
                elif subject_id is None:
                    frequency = 'per_visit'
                else:
                    frequency = 'per_session'
                # Append fields
                for name, value in field_values.items():
                    value = value.replace('&quot;', '"')
                    all_fields.append(Field(
                        name=name, value=value, repository=self,
                        frequency=frequency,
                        subject_id=subject_id,
                        visit_id=visit_id,
                        from_study=from_study,
                        **kwargs))
                # Extract part of JSON relating to files
                try:
                    scans_json = next(
                        c['items'] for c in session_json['children']
                        if c['field'] == 'scans/scan')
                except StopIteration:
                    scans_json = []
                for scan_json in scans_json:
                    scan_id = scan_json['data_fields']['ID']
                    scan_type = scan_json['data_fields'].get('type', '')
                    scan_uri = '{}/scans/{}'.format(session_uri, scan_id)
                    try:
                        resources_json = next(
                            c['items'] for c in scan_json['children']
                            if c['field'] == 'file')
                    except StopIteration:
                        resources = {}
                    else:
                        resources = {js['data_fields']['label']:
                                     js['data_fields'].get('format', None)
                                     for js in resources_json}
                    # Remove auto-generated snapshots directory
                    resources.pop('SNAPSHOTS', None)
                    if scan_type == self.PROV_SCAN:
                        # Download provenance JSON files and parse into
                        # records
                        temp_dir = tempfile.mkdtemp()
                        try:
                            with tempfile.TemporaryFile() as temp_zip:
                                self._login.download_stream(
                                    scan_uri + '/files', temp_zip,
                                    format='zip')
                                with ZipFile(temp_zip) as zip_file:
                                    zip_file.extractall(temp_dir)
                            for base_dir, _, fnames in os.walk(temp_dir):
                                for fname in fnames:
                                    if fname.endswith('.json'):
                                        pipeline_name = fname[:-len('.json')]
                                        json_path = op.join(base_dir, fname)
                                        all_records.append(
                                            Record.load(
                                                pipeline_name, frequency,
                                                subject_id, visit_id,
                                                from_study, json_path))
                        finally:
                            shutil.rmtree(temp_dir, ignore_errors=True)
                    else:
                        for resource in resources:
                            all_filesets.append(Fileset(
                                scan_type, id=scan_id, uri=scan_uri,
                                repository=self, frequency=frequency,
                                subject_id=subject_id, visit_id=visit_id,
                                from_study=from_study,
                                resource_name=resource, **kwargs))
                logger.debug("Found node {}:{} on {}:{}".format(
                    subject_id, visit_id, self.server, self.project_id))
        return all_filesets, all_fields, all_records