From 4efce24219e66a3dab71f9b2f0e65e83073d7d55 Mon Sep 17 00:00:00 2001
From: Greg DiCristofaro <gregd@basistech.com>
Date: Wed, 20 Oct 2021 20:55:58 -0400
Subject: [PATCH] integrated updatable caches

---
 .../mainui/datamodel/AnalysisResultDAO.java   |   6 +-
 .../datamodel/FileTypeSizeSearchParams.java   |  25 +-
 .../mainui/datamodel/HashHitSearchParam.java  |   5 +
 .../datamodel/KeywordHitSearchParam.java      |   5 +
 .../autopsy/mainui/datamodel/ViewsDAO.java    | 376 +++++++-----------
 .../mainui/nodes/SearchResultSupport.java     |   6 +-
 .../mainui/datamodel/TableSearchTest.java     |  58 +--
 7 files changed, 211 insertions(+), 270 deletions(-)

diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AnalysisResultDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AnalysisResultDAO.java
index 90c3b69f1e..cddad9b779 100644
--- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AnalysisResultDAO.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/AnalysisResultDAO.java
@@ -183,15 +183,15 @@ protected void onModuleData(ModuleDataEvent evt) {
         caches.forEach((cache) -> cache.invalidate(evt));
     }
 
-    public EventUpdatableCache<AnalysisResultSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getAnalysisResultsCache() {
+    public EventUpdatableCache<AnalysisResultSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getAnalysisResults() {
         return this.analysisResultCache;
     }
 
-    public EventUpdatableCache<HashHitSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getHashHitsCache() {
+    public EventUpdatableCache<HashHitSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getHashsetHits() {
         return this.hashHitCache;
     }
 
-    public EventUpdatableCache<KeywordHitSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getKeywordHitsCache() {
+    public EventUpdatableCache<KeywordHitSearchParam, AnalysisResultTableSearchResultsDTO, ModuleDataEvent> getKeywordHits() {
         return this.keywordHitCache;
     }
 
diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeSizeSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeSizeSearchParams.java
index a07c789f92..1922c08ba2 100755
--- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeSizeSearchParams.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeSizeSearchParams.java
@@ -25,18 +25,27 @@
  */
 public class FileTypeSizeSearchParams extends DataSourceFilteredSearchParams {
 
+    private static final long GB_1 = 1000 * 1000 * 1000;
+    private static final long MB_200 = 200 * 1000 * 1000;
+    private static final long MB_50 = 50 * 1000 * 1000;
+
     public enum FileSizeFilter {
-        SIZE_50_200(0, "SIZE_50_200", "50 - 200MB"), //NON-NLS
-        SIZE_200_1000(1, "SIZE_200_1GB", "200MB - 1GB"), //NON-NLS
-        SIZE_1000_(2, "SIZE_1000+", "1GB+"); //NON-NLS
+        SIZE_50_200(0, "SIZE_50_200", "50 - 200MB", MB_50, MB_200), //NON-NLS
+        SIZE_200_1000(1, "SIZE_200_1GB", "200MB - 1GB", MB_200, GB_1), //NON-NLS
+        SIZE_1000_(2, "SIZE_1000+", "1GB+", GB_1, null); //NON-NLS
         private final int id;
         private final String name;
         private final String displayName;
+        private long lowerBound;
+        private Long upperBound;
+        
 
-        private FileSizeFilter(int id, String name, String displayName) {
+        private FileSizeFilter(int id, String name, String displayName, long lowerBound, Long upperBound) {
             this.id = id;
             this.name = name;
             this.displayName = displayName;
+            this.lowerBound = lowerBound;
+            this.upperBound = upperBound;
         }
 
         public String getName() {
@@ -50,6 +59,14 @@ public int getId() {
         public String getDisplayName() {
             return this.displayName;
         }
+
+        public long getLowerBound() {
+            return lowerBound;
+        }
+
+        public Long getUpperBound() {
+            return upperBound;
+        }
     }
 
     private final FileSizeFilter sizeFilter;
diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HashHitSearchParam.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HashHitSearchParam.java
index 9ef3f98af1..1775b7e361 100644
--- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HashHitSearchParam.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HashHitSearchParam.java
@@ -24,6 +24,11 @@
  * Key for keyword hits in order to retrieve data from DAO.
  */
 public class HashHitSearchParam extends AnalysisResultSetSearchParam {
+    
+    public HashHitSearchParam(Long dataSourceId, String setName) {
+        super(BlackboardArtifact.Type.TSK_HASHSET_HIT, dataSourceId, setName);
+    }
+
     public HashHitSearchParam(Long dataSourceId, String setName, long startItem, Long maxResultsCount) {
         super(BlackboardArtifact.Type.TSK_HASHSET_HIT, dataSourceId, setName, startItem, maxResultsCount);
     }
diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/KeywordHitSearchParam.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/KeywordHitSearchParam.java
index 7a564a04b9..b9c2b43358 100644
--- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/KeywordHitSearchParam.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/KeywordHitSearchParam.java
@@ -24,6 +24,11 @@
  * Key for keyword hits in order to retrieve data from DAO.
  */
 public class KeywordHitSearchParam extends AnalysisResultSetSearchParam {
+
+    public KeywordHitSearchParam(Long dataSourceId, String setName) {
+        super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId, setName);
+    }
+
     public KeywordHitSearchParam(String setName, Long dataSourceId, long startItem, Long maxResultsCount) {
         super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId, setName, startItem, maxResultsCount);
     }
diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java
index 2da9f69c65..32f6b75fc1 100644
--- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java
@@ -18,20 +18,14 @@
  */
 package org.sleuthkit.autopsy.mainui.datamodel;
 
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
-import org.openide.util.NbBundle;
 import org.openide.util.NbBundle.Messages;
 import org.sleuthkit.autopsy.casemodule.Case;
 import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@@ -39,7 +33,6 @@
 import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
 import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
 import org.sleuthkit.autopsy.datamodel.FileTypeExtensions;
-import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
 import org.sleuthkit.autopsy.mainui.datamodel.DataEventListener.DefaultDataEventListener;
 import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.ExtensionMediaType;
 import org.sleuthkit.datamodel.AbstractFile;
@@ -82,11 +75,6 @@
     "ThreePanelViewsDAO.fileColumns.noDescription=No Description"})
 public class ViewsDAO extends DefaultDataEventListener {
 
-    private static final int CACHE_SIZE = 15; // rule of thumb: 5 entries times number of cached SearchParams sub-types
-    private static final long CACHE_DURATION = 2;
-    private static final TimeUnit CACHE_DURATION_UNITS = TimeUnit.MINUTES;
-    private final Cache<DataSourceFilteredSearchParams, SearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build();
-
     private static final String FILE_VIEW_EXT_TYPE_ID = "FILE_VIEW_BY_EXT";
 
     private static final List<ColumnKey> FILE_COLUMNS = Arrays.asList(
@@ -131,7 +119,7 @@ private static ColumnKey getFileColumnKey(String name) {
         return new ColumnKey(name, name, Bundle.ThreePanelViewsDAO_fileColumns_noDescription());
     }
 
-    static ExtensionMediaType getExtensionMediaType(String ext) {
+    private static ExtensionMediaType getExtensionMediaType(String ext) {
         if (StringUtils.isBlank(ext)) {
             return ExtensionMediaType.UNCATEGORIZED;
         } else {
@@ -160,158 +148,47 @@ static ExtensionMediaType getExtensionMediaType(String ext) {
         }
     }
 
-    private SleuthkitCase getCase() throws NoCurrentCaseException {
-        return Case.getCurrentCaseThrows().getSleuthkitCase();
-    }
+    private final FilesByExtensionCache extensionCache = new FilesByExtensionCache();
+    private final FilesByMimeCache mimeCache = new FilesByMimeCache();
+    private final FilesBySizeCache sizeCache = new FilesBySizeCache();
+    private final List<EventUpdatableCacheImpl<?, ?, Content>> caches = ImmutableList.of(extensionCache, mimeCache, sizeCache);
 
-    public SearchResultsDTO getFilesByExtension(FileTypeExtensionsSearchParams key) throws ExecutionException, IllegalArgumentException {
-        if (key.getFilter() == null) {
-            throw new IllegalArgumentException("Must have non-null filter");
-        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-        }
-
-        return searchParamsCache.get(key, () -> fetchExtensionSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
+    public EventUpdatableCache<FileTypeExtensionsSearchParams, SearchResultsDTO, Content> getFileByExtensions() {
+        return this.extensionCache;
     }
 
-    public SearchResultsDTO getFilesByMime(FileTypeMimeSearchParams key) throws ExecutionException, IllegalArgumentException {
-        if (key.getMimeType() == null) {
-            throw new IllegalArgumentException("Must have non-null filter");
-        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-        }
-
-        return searchParamsCache.get(key, () -> fetchMimeSearchResultsDTOs(key.getMimeType(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
-    }
-
-    public SearchResultsDTO getFilesBySize(FileTypeSizeSearchParams key) throws ExecutionException, IllegalArgumentException {
-        if (key.getSizeFilter() == null) {
-            throw new IllegalArgumentException("Must have non-null filter");
-        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-        }
-
-        return searchParamsCache.get(key, () -> fetchSizeSearchResultsDTOs(key.getSizeFilter(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
-    }    
-
-//    private ViewFileTableSearchResultsDTO fetchFilesForTable(ViewFileCacheKey cacheKey) throws NoCurrentCaseException, TskCoreException {
-//
-//    }
-//
-//    public ViewFileTableSearchResultsDTO getFilewViewForTable(BlackboardArtifact.Type artType, Long dataSourceId) throws ExecutionException, IllegalArgumentException {
-//        if (artType == null || artType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
-//            throw new IllegalArgumentException(MessageFormat.format("Illegal data.  "
-//                    + "Artifact type must be non-null and data artifact.  "
-//                    + "Received {0}", artType));
-//        }
-//
-//        ViewFileCacheKey cacheKey = new ViewFileCacheKey(artType, dataSourceId);
-//        return dataArtifactCache.get(cacheKey, () -> fetchFilesForTable(cacheKey));
-//    }
-    private Map<Integer, Long> fetchFileViewCounts(List<FileExtSearchFilter> filters, Long dataSourceId) throws NoCurrentCaseException, TskCoreException {
-        Map<Integer, Long> counts = new HashMap<>();
-        for (FileExtSearchFilter filter : filters) {
-            String whereClause = getFileExtensionWhereStatement(filter, dataSourceId);
-            long count = getCase().countFilesWhere(whereClause);
-            counts.put(filter.getId(), count);
-        }
-
-        return counts;
+    public EventUpdatableCache<FileTypeMimeSearchParams, SearchResultsDTO, Content> getFileByMimeTypes() {
+        return mimeCache;
     }
 
-    private String getFileExtensionWhereStatement(FileExtSearchFilter filter, Long dataSourceId) {
-        String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
-                + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "")
-                + (dataSourceId != null && dataSourceId > 0
-                        ? " AND data_source_obj_id = " + dataSourceId
-                        : " ")
-                + " AND (extension IN (" + filter.getFilter().stream()
-                        .map(String::toLowerCase)
-                        .map(s -> "'" + StringUtils.substringAfter(s, ".") + "'")
-                        .collect(Collectors.joining(", ")) + "))";
-        return whereClause;
+    public EventUpdatableCache<FileTypeSizeSearchParams, SearchResultsDTO, Content> getFilesBySize() {
+        return sizeCache;
     }
 
-    private String getFileMimeWhereStatement(String mimeType, Long dataSourceId) {
-
-        String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
-                + " AND (type IN ("
-                + TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
-                + TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
-                + TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
-                + TskData.TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.ordinal() + ","
-                + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
-                + (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
-                + "))"
-                + (dataSourceId != null && dataSourceId > 0 ? " AND data_source_obj_id = " + dataSourceId : " ")
-                + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "")
-                + " AND mime_type = '" + mimeType + "'";
-
-        return whereClause;
+    private SleuthkitCase getCase() throws NoCurrentCaseException {
+        return Case.getCurrentCaseThrows().getSleuthkitCase();
     }
 
-    private static String getFileSizesWhereStatement(FileTypeSizeSearchParams.FileSizeFilter filter, Long dataSourceId) {
-        String query;
-        switch (filter) {
-            case SIZE_50_200:
-                query = "(size >= 50000000 AND size < 200000000)"; //NON-NLS
-                break;
-            case SIZE_200_1000:
-                query = "(size >= 200000000 AND size < 1000000000)"; //NON-NLS
-                break;
-
-            case SIZE_1000_:
-                query = "(size >= 1000000000)"; //NON-NLS
-                break;
-
-            default:
-                throw new IllegalArgumentException("Unsupported filter type to get files by size: " + filter); //NON-NLS
-        }
-
-        // Ignore unallocated block files.
-        query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
-
-        // hide known files if specified by configuration
-        query += (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : ""); //NON-NLS
-
-        // filter by datasource if indicated in case preferences
-        if (dataSourceId != null && dataSourceId > 0) {
-            query += " AND data_source_obj_id = " + dataSourceId;
+    private void validateDataSourceParams(DataSourceFilteredSearchParams searchParams) throws IllegalArgumentException {
+        if (searchParams == null) {
+            throw new IllegalArgumentException("Search parameters must be non-null");
+        } else if (searchParams.getDataSourceId() != null && searchParams.getDataSourceId() <= 0) {
+            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
         }
-
-        return query;
-    }
-
-    private SearchResultsDTO fetchExtensionSearchResultsDTOs(FileExtSearchFilter filter, Long dataSourceId, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
-        String whereStatement = getFileExtensionWhereStatement(filter, dataSourceId);
-        return fetchFileViewFiles(whereStatement, filter.getDisplayName(), startItem, maxResultCount);
-    }
-
-    @NbBundle.Messages({"FileTypesByMimeType.name.text=By MIME Type"})
-    private SearchResultsDTO fetchMimeSearchResultsDTOs(String mimeType, Long dataSourceId, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
-        String whereStatement = getFileMimeWhereStatement(mimeType, dataSourceId);
-        final String MIME_TYPE_DISPLAY_NAME = Bundle.FileTypesByMimeType_name_text();
-        return fetchFileViewFiles(whereStatement, MIME_TYPE_DISPLAY_NAME, startItem, maxResultCount);
-    }
-
-    private SearchResultsDTO fetchSizeSearchResultsDTOs(FileTypeSizeSearchParams.FileSizeFilter filter, Long dataSourceId, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
-        String whereStatement = getFileSizesWhereStatement(filter, dataSourceId);
-        return fetchFileViewFiles(whereStatement, filter.getDisplayName(), startItem, maxResultCount);
     }
 
     private SearchResultsDTO fetchFileViewFiles(String whereStatement, String displayName, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
         List<AbstractFile> files = getCase().findAllFilesWhere(whereStatement);
-        
+
         Stream<AbstractFile> pagedFileStream = files.stream()
                 .sorted(Comparator.comparing(af -> af.getId()))
                 .skip(startItem);
-        
+
         if (maxResultCount != null) {
             pagedFileStream = pagedFileStream.limit(maxResultCount);
         }
-        
+
         List<AbstractFile> pagedFiles = pagedFileStream.collect(Collectors.toList());
-        
 
         List<RowDTO> fileRows = new ArrayList<>();
         for (AbstractFile file : pagedFiles) {
@@ -371,106 +248,143 @@ private SearchResultsDTO fetchFileViewFiles(String whereStatement, String displa
 
     @Override
     public void dropCache() {
-        searchParamsCache.invalidateAll();
+        caches.forEach((cache) -> cache.invalidateAll());
     }
 
     @Override
     protected void onContentChange(Content content) {
-        if (!(content instanceof AbstractFile)) {
-            return;
+        caches.forEach((cache) -> cache.invalidate(content));
+    }
+
+    private class FilesByExtensionCache extends EventUpdatableCacheImpl<FileTypeExtensionsSearchParams, SearchResultsDTO, Content> {
+
+        private String getFileExtensionWhereStatement(FileExtSearchFilter filter, Long dataSourceId) {
+            String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+                    + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "")
+                    + (dataSourceId != null && dataSourceId > 0
+                            ? " AND data_source_obj_id = " + dataSourceId
+                            : " ")
+                    + " AND (extension IN (" + filter.getFilter().stream()
+                            .map(String::toLowerCase)
+                            .map(s -> "'" + StringUtils.substringAfter(s, ".") + "'")
+                            .collect(Collectors.joining(", ")) + "))";
+            return whereClause;
+        }
+
+        @Override
+        protected SearchResultsDTO fetch(FileTypeExtensionsSearchParams key) throws Exception {
+            String whereStatement = getFileExtensionWhereStatement(key.getFilter(), key.getDataSourceId());
+            return fetchFileViewFiles(whereStatement, key.getFilter().getDisplayName(), key.getStartItem(), key.getMaxResultsCount());
+        }
+
+        @Override
+        public boolean isInvalidatingEvent(FileTypeExtensionsSearchParams key, Content eventData) {
+            if (!(eventData instanceof AbstractFile)) {
+                return false;
+            }
+
+            AbstractFile file = (AbstractFile) eventData;
+            String extension = "." + file.getNameExtension().toLowerCase();
+            return key.getFilter().getFilter().contains(extension);
+        }
+
+        @Override
+        protected void validateCacheKey(FileTypeExtensionsSearchParams key) throws IllegalArgumentException {
+            validateDataSourceParams(key);
+            if (key.getFilter() == null) {
+                throw new IllegalArgumentException("Must have non-null filter");
+            }
+        }
+    }
+
+    private class FilesByMimeCache extends EventUpdatableCacheImpl<FileTypeMimeSearchParams, SearchResultsDTO, Content> {
+
+        private String getFileMimeWhereStatement(String mimeType, Long dataSourceId) {
+            String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+                    + " AND (type IN ("
+                    + TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
+                    + TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
+                    + TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
+                    + TskData.TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.ordinal() + ","
+                    + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
+                    + (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+                    + "))"
+                    + (dataSourceId != null && dataSourceId > 0 ? " AND data_source_obj_id = " + dataSourceId : " ")
+                    + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "")
+                    + " AND mime_type = '" + mimeType + "'";
+
+            return whereClause;
+        }
+
+        @Override
+        @Messages({"FileTypesByMimeType.name.text=By MIME Type"})
+        protected SearchResultsDTO fetch(FileTypeMimeSearchParams key) throws Exception {
+            String whereStatement = getFileMimeWhereStatement(key.getMimeType(), key.getDataSourceId());
+            final String mimeTypeDisplayName = Bundle.FileTypesByMimeType_name_text();
+            return fetchFileViewFiles(whereStatement, mimeTypeDisplayName, key.getStartItem(), key.getMaxResultsCount());
+        }
+
+        @Override
+        public boolean isInvalidatingEvent(FileTypeMimeSearchParams key, Content eventData) {
+            if (!(eventData instanceof AbstractFile)) {
+                return false;
+            }
+
+            AbstractFile file = (AbstractFile) eventData;
+            String mimeType = file.getMIMEType();
+            return key.getMimeType().equalsIgnoreCase(mimeType);
         }
 
-        AbstractFile file = (AbstractFile) content;
-        long dataSourceId = file.getDataSourceObjectId();
-        // GVDTODO rewrite
-//        dropMimeTypeMatches(file.getMIMEType(), dataSourceId);
-//        dropFileExtMatches(file.getNameExtension(), dataSourceId);
+        @Override
+        protected void validateCacheKey(FileTypeMimeSearchParams key) throws IllegalArgumentException {
+            validateDataSourceParams(key);
+            if (key.getMimeType() == null) {
+                throw new IllegalArgumentException("Must have non-null filter");
+            }
+        }
     }
-    
-    
-    
-    private abstract class ViewsDaoCache<K extends DataSourceFilteredSearchParams> extends EventUpdatableCacheImpl<K, SearchResultsDTO, ModuleContentEvent> {
+
+    private class FilesBySizeCache extends EventUpdatableCacheImpl<FileTypeSizeSearchParams, SearchResultsDTO, Content> {
+
+        private String getFileSizesWhereStatement(FileTypeSizeSearchParams.FileSizeFilter filter, Long dataSourceId) {
+            String lowerBound = "size >= " + filter.getLowerBound();
+            String upperBound = filter.getUpperBound() == null ? "" : " AND size < " + filter.getUpperBound();
+            String query = "(" + lowerBound + upperBound + ")";
+
+            // Ignore unallocated block files.
+            query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
+
+            // hide known files if specified by configuration
+            query += (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : ""); //NON-NLS
+
+            // filter by datasource if indicated in case preferences
+            if (dataSourceId != null && dataSourceId > 0) {
+                query += " AND data_source_obj_id = " + dataSourceId;
+            }
+
+            return query;
+        }
 
         @Override
-        protected abstract SearchResultsDTO fetch(K key) throws Exception;
+        protected SearchResultsDTO fetch(FileTypeSizeSearchParams key) throws Exception {
+            String whereStatement = getFileSizesWhereStatement(key.getSizeFilter(), key.getDataSourceId());
+            return fetchFileViewFiles(whereStatement, key.getSizeFilter().getDisplayName(), key.getStartItem(), key.getMaxResultsCount());
+        }
 
         @Override
-        protected void validateCacheKey(K key) throws IllegalArgumentException {
-            super.validateCacheKey(key); //To change body of generated methods, choose Tools | Templates.
+        public boolean isInvalidatingEvent(FileTypeSizeSearchParams key, Content eventData) {
+            long size = eventData.getSize();
+
+            return size >= key.getSizeFilter().getLowerBound()
+                    && (key.getSizeFilter().getUpperBound() == null || size < key.getSizeFilter().getUpperBound());
         }
 
         @Override
-        public boolean isInvalidatingEvent(K key, ModuleDataEvent eventData) {
-            
+        protected void validateCacheKey(FileTypeSizeSearchParams key) throws IllegalArgumentException {
+            validateDataSourceParams(key);
+            if (key.getSizeFilter() == null) {
+                throw new IllegalArgumentException("Must have non-null filter");
+            }
         }
     }
-    
-    
-    
-//    public SearchResultsDTO getFilesByExtension(FileTypeExtensionsSearchParams key) throws ExecutionException, IllegalArgumentException {
-//        if (key.getFilter() == null) {
-//            throw new IllegalArgumentException("Must have non-null filter");
-//        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-//            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-//        }
-//
-//        return searchParamsCache.get(key, () -> fetchExtensionSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
-//    }
-//
-//    public SearchResultsDTO getFilesByMime(FileTypeMimeSearchParams key) throws ExecutionException, IllegalArgumentException {
-//        if (key.getMimeType() == null) {
-//            throw new IllegalArgumentException("Must have non-null filter");
-//        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-//            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-//        }
-//
-//        return searchParamsCache.get(key, () -> fetchMimeSearchResultsDTOs(key.getMimeType(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
-//    }
-//
-//    public SearchResultsDTO getFilesBySize(FileTypeSizeSearchParams key) throws ExecutionException, IllegalArgumentException {
-//        if (key.getSizeFilter() == null) {
-//            throw new IllegalArgumentException("Must have non-null filter");
-//        } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
-//            throw new IllegalArgumentException("Data source id must be greater than 0 or null");
-//        }
-//
-//        return searchParamsCache.get(key, () -> fetchSizeSearchResultsDTOs(key.getSizeFilter(), key.getDataSourceId(), key.getStartItem(), key.getMaxResultsCount()));
-//    }   
-
-//    private void dropFileExtMatches(String extension, long dataSourceId) {
-//        if (extension == null) {
-//            return;
-//        }
-//
-//        String extKey = "." + extension;
-//
-//        Set<FileExtSearchFilter> matchingFilters = Stream.of(
-//                FileExtDocumentFilter.values(),
-//                FileExtExecutableFilter.values(),
-//                FileExtRootFilter.values()
-//        )
-//                .flatMap(arr -> Stream.of(arr))
-//                .filter(filter -> filter.getFilter().contains(extKey))
-//                .collect(Collectors.toSet());
-//
-//        this.searchParamsCache.asMap().replaceAll((k, v) -> {
-//            // if filter applies to this item
-//            return (matchingFilters.contains(k.getFilter())
-//                    // and data source ids are equal
-//                    && (k.getDataSourceId() == null || k.getDataSourceId() == dataSourceId))
-//                    ? v
-//                    : null;
-//        });
-//    }
-//
-//    private void dropMimeTypeMatches(String mimeType, long dataSourceId) {
-//        this.searchParamsCache.asMap().replaceAll((k, v) -> {
-//            // if mime types are equal
-//            return (((mimeType == null && k.getMimeType() == null) || (mimeType != null && mimeType.equals(k.getMimeType())))
-//                    // and data source ids are equal
-//                    && (k.getDataSourceId() == null || k.getDataSourceId() == dataSourceId))
-//                    ? v
-//                    : null;
-//        });
-//    }
 }
diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/SearchResultSupport.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/SearchResultSupport.java
index 110deccf73..49e337722e 100644
--- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/SearchResultSupport.java
+++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/SearchResultSupport.java
@@ -236,7 +236,7 @@ public synchronized SearchResultsDTO setFileExtensions(final FileTypeExtensionsS
                     fileExtParameters.getDataSourceId(),
                     pageIdx * pageSize,
                     (long) pageSize);
-            return dao.getViewsDAO().getFilesByExtension(searchParams);
+            return dao.getViewsDAO().getFileByExtensions().getValue(searchParams);
         };
 
         return fetchResults();
@@ -262,7 +262,7 @@ public synchronized SearchResultsDTO setDataArtifact(final DataArtifactSearchPar
                     dataArtifactParameters.getDataSourceId(),
                     pageIdx * pageSize,
                     (long) pageSize);
-            return dao.getDataArtifactsDAO().getDataArtifactsForTable(searchParams);
+            return dao.getDataArtifactsDAO().getValue(searchParams);
         };
 
         return fetchResults();
@@ -289,7 +289,7 @@ public synchronized SearchResultsDTO setFileMimes(FileTypeMimeSearchParams fileM
                     fileMimeKey.getDataSourceId(),
                     pageIdx * pageSize,
                     (long) pageSize);
-            return dao.getViewsDAO().getFilesByMime(searchParams);
+            return dao.getViewsDAO().getFileByMimeTypes().getValue(searchParams);
         };
 
         return fetchResults();
diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java
index d47b28273a..08807ad752 100644
--- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java
+++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/mainui/datamodel/TableSearchTest.java
@@ -320,28 +320,28 @@ public void dataArtifactSearchTest() {
             DataArtifactSearchParam param = new DataArtifactSearchParam(BlackboardArtifact.Type.TSK_CONTACT, null);
             DataArtifactDAO dataArtifactDAO = MainDAO.getInstance().getDataArtifactsDAO();
 
-            DataArtifactTableSearchResultsDTO results = dataArtifactDAO.getDataArtifactsForTable(param);
+            DataArtifactTableSearchResultsDTO results = dataArtifactDAO.getValue(param);
             assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType());
             assertEquals(2, results.getTotalResultsCount());
             assertEquals(2, results.getItems().size());
             
             // Get contacts from data source 2
             param = new DataArtifactSearchParam(BlackboardArtifact.Type.TSK_CONTACT, dataSource2.getId());
-            results = dataArtifactDAO.getDataArtifactsForTable(param);
+            results = dataArtifactDAO.getValue(param);
             assertEquals(BlackboardArtifact.Type.TSK_CONTACT, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
             // Get bookmarks from data source 2
             param = new DataArtifactSearchParam(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, dataSource2.getId());
-            results = dataArtifactDAO.getDataArtifactsForTable(param);
+            results = dataArtifactDAO.getValue(param);
             assertEquals(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, results.getArtifactType());
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Get all custom artifacts
             param = new DataArtifactSearchParam(customDataArtifactType, null);
-            results = dataArtifactDAO.getDataArtifactsForTable(param);
+            results = dataArtifactDAO.getValue(param);
             assertEquals(customDataArtifactType, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
@@ -387,37 +387,37 @@ public void mimeSearchTest() {
 
             // Get plain text files from data source 1
             FileTypeMimeSearchParams param = new FileTypeMimeSearchParams("text/plain", dataSource1.getId());
-            SearchResultsDTO results = viewsDAO.getFilesByMime(param);
+            SearchResultsDTO results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(2, results.getTotalResultsCount());
             assertEquals(2, results.getItems().size());
 
             // Get jpeg files from data source 1
             param = new FileTypeMimeSearchParams("image/jpeg", dataSource1.getId());
-            results = viewsDAO.getFilesByMime(param);
+            results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
             // Get jpeg files from data source 2
             param = new FileTypeMimeSearchParams("image/jpeg", dataSource2.getId());
-            results = viewsDAO.getFilesByMime(param);
+            results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Search for mime type that should produce no results
             param = new FileTypeMimeSearchParams("blah/blah", null);
-            results = viewsDAO.getFilesByMime(param);
+            results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Get plain text files from all data sources
             param = new FileTypeMimeSearchParams("text/plain", null);
-            results = viewsDAO.getFilesByMime(param);
+            results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(3, results.getTotalResultsCount());
             assertEquals(3, results.getItems().size());
 
             // Get the custom file by MIME type
             param = new FileTypeMimeSearchParams(CUSTOM_MIME_TYPE, null);
-            results = viewsDAO.getFilesByMime(param);
+            results = viewsDAO.getFileByMimeTypes().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
@@ -442,31 +442,31 @@ public void sizeSearchTest() {
 
             // Get "50 - 200MB" files from data source 1
             FileTypeSizeSearchParams param = new FileTypeSizeSearchParams(FileTypeSizeSearchParams.FileSizeFilter.SIZE_50_200, dataSource1.getId());
-            SearchResultsDTO results = viewsDAO.getFilesBySize(param);
+            SearchResultsDTO results = viewsDAO.getFilesBySize().getValue(param);
             assertEquals(2, results.getTotalResultsCount());
             assertEquals(2, results.getItems().size());
 
             // Get "200MB - 1GB" files from data source 1
             param = new FileTypeSizeSearchParams(FileTypeSizeSearchParams.FileSizeFilter.SIZE_200_1000, dataSource1.getId());
-            results = viewsDAO.getFilesBySize(param);
+            results = viewsDAO.getFilesBySize().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Get "200MB - 1GB" files from data source 2
             param = new FileTypeSizeSearchParams(FileTypeSizeSearchParams.FileSizeFilter.SIZE_200_1000, dataSource2.getId());
-            results = viewsDAO.getFilesBySize(param);
+            results = viewsDAO.getFilesBySize().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
             // Get "1GB+" files from all data sources
             param = new FileTypeSizeSearchParams(FileTypeSizeSearchParams.FileSizeFilter.SIZE_1000_, null);
-            results = viewsDAO.getFilesBySize(param);
+            results = viewsDAO.getFilesBySize().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Get "50 - 200MB" files from all data sources
             param = new FileTypeSizeSearchParams(FileTypeSizeSearchParams.FileSizeFilter.SIZE_50_200, null);
-            results = viewsDAO.getFilesBySize(param);
+            results = viewsDAO.getFilesBySize().getValue(param);
             assertEquals(3, results.getTotalResultsCount());
             assertEquals(3, results.getItems().size());
         } catch (ExecutionException ex) {
@@ -484,21 +484,21 @@ public void analysisResultSearchTest() {
             AnalysisResultSearchParam param = new AnalysisResultSearchParam(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, null);
             AnalysisResultDAO analysisResultDAO = MainDAO.getInstance().getAnalysisResultDAO();
             
-            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getAnalysisResultsForTable(param);
+            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getAnalysisResults().getValue(param);
             assertEquals(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, results.getArtifactType());
             assertEquals(3, results.getTotalResultsCount());
             assertEquals(3, results.getItems().size());
             
             // Get encryption detected artifacts from data source 2
             param = new AnalysisResultSearchParam(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, dataSource2.getId());
-            results = analysisResultDAO.getAnalysisResultsForTable(param);
+            results = analysisResultDAO.getAnalysisResults().getValue(param);
             assertEquals(BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
             
             // Get all custom artifacts
             param = new AnalysisResultSearchParam(customAnalysisResultType, null);
-            results = analysisResultDAO.getAnalysisResultsForTable(param);
+            results = analysisResultDAO.getAnalysisResults().getValue(param);
             assertEquals(customAnalysisResultType, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
@@ -538,13 +538,13 @@ private void hashHitSearchTest() {
             // Test hash set hits
             AnalysisResultDAO analysisResultDAO = MainDAO.getInstance().getAnalysisResultDAO();
             HashHitSearchParam hashParam = new HashHitSearchParam(null, HASH_SET_1);
-            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getHashHitsForTable(hashParam);
+            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getHashsetHits().getValue(hashParam);
             assertEquals(BlackboardArtifact.Type.TSK_HASHSET_HIT, results.getArtifactType());
             assertEquals(3, results.getTotalResultsCount());
             assertEquals(3, results.getItems().size());
             
             hashParam = new HashHitSearchParam(dataSource2.getId(), HASH_SET_1);
-            results = analysisResultDAO.getHashHitsForTable(hashParam);
+            results = analysisResultDAO.getHashsetHits().getValue(hashParam);
             assertEquals(BlackboardArtifact.Type.TSK_HASHSET_HIT, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
@@ -577,13 +577,13 @@ private void keywordHitSearchTest() {
             // Test keyword set hits
             AnalysisResultDAO analysisResultDAO = MainDAO.getInstance().getAnalysisResultDAO();
             KeywordHitSearchParam kwParam = new KeywordHitSearchParam(null, KEYWORD_SET_1);
-            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getKeywordHitsForTable(kwParam);
+            AnalysisResultTableSearchResultsDTO results = analysisResultDAO.getKeywordHits().getValue(kwParam);
             assertEquals(BlackboardArtifact.Type.TSK_KEYWORD_HIT, results.getArtifactType());
             assertEquals(2, results.getTotalResultsCount());
             assertEquals(2, results.getItems().size());
             
             kwParam = new KeywordHitSearchParam(dataSource2.getId(), KEYWORD_SET_1);
-            results = analysisResultDAO.getKeywordHitsForTable(kwParam);
+            results = analysisResultDAO.getKeywordHits().getValue(kwParam);
             assertEquals(BlackboardArtifact.Type.TSK_KEYWORD_HIT, results.getArtifactType());
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
@@ -621,43 +621,43 @@ public void extensionSearchTest() {
 
             // Get all text documents from data source 1
             FileTypeExtensionsSearchParams param = new FileTypeExtensionsSearchParams(FileExtRootFilter.TSK_DOCUMENT_FILTER, dataSource1.getId());
-            SearchResultsDTO results = viewsDAO.getFilesByExtension(param);
+            SearchResultsDTO results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(3, results.getTotalResultsCount());
             assertEquals(3, results.getItems().size());
 
             // Get Word documents from data source 1
             param = new FileTypeExtensionsSearchParams(FileExtDocumentFilter.AUT_DOC_OFFICE, dataSource1.getId());
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
             // Get image/jpeg files from data source 1
             param = new FileTypeExtensionsSearchParams(FileExtRootFilter.TSK_IMAGE_FILTER, dataSource1.getId());
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
             // Get text documents from all data sources
             param = new FileTypeExtensionsSearchParams(FileExtRootFilter.TSK_DOCUMENT_FILTER, null);
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(4, results.getTotalResultsCount());
             assertEquals(4, results.getItems().size());
 
             // Get jpeg files from data source 2
             param = new FileTypeExtensionsSearchParams(FileExtRootFilter.TSK_IMAGE_FILTER, dataSource2.getId());
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Search for file extensions that should produce no results
             param = new FileTypeExtensionsSearchParams(CustomRootFilter.EMPTY_RESULT_SET_FILTER, null);
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(0, results.getTotalResultsCount());
             assertEquals(0, results.getItems().size());
 
             // Get the custom file by extension
             param = new FileTypeExtensionsSearchParams(CustomRootFilter.CUSTOM_FILTER, null);
-            results = viewsDAO.getFilesByExtension(param);
+            results = viewsDAO.getFileByExtensions().getValue(param);
             assertEquals(1, results.getTotalResultsCount());
             assertEquals(1, results.getItems().size());
 
-- 
GitLab