mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
1. Rename some method in QueryConstructor.cs by removing prefix and suffix 2. Split the logic of checking '>' to outer SearchManager.cs 3. Use IAsyncEnumerable for potential future improvement.
147 lines
5.8 KiB
C#
147 lines
5.8 KiB
C#
using Microsoft.Search.Interop;
|
|
|
|
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|
{
|
|
public class QueryConstructor
|
|
{
|
|
private Settings Settings { get; }
|
|
|
|
private const string SystemIndex = "SystemIndex";
|
|
|
|
public CSearchQueryHelper BaseQueryHelper { get; }
|
|
|
|
public QueryConstructor(Settings settings)
|
|
{
|
|
Settings = settings;
|
|
BaseQueryHelper = CreateBaseQuery();
|
|
}
|
|
|
|
|
|
private CSearchQueryHelper CreateBaseQuery()
|
|
{
|
|
var baseQuery = CreateQueryHelper();
|
|
|
|
// Set the number of results we want. Don't set this property if all results are needed.
|
|
baseQuery.QueryMaxResults = Settings.MaxResult;
|
|
|
|
// Set list of columns we want to display, getting the path presently
|
|
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
|
|
|
|
// Filter based on file name
|
|
baseQuery.QueryContentProperties = "System.FileName";
|
|
|
|
// Set sorting order
|
|
//baseQuery.QuerySorting = "System.ItemType DESC";
|
|
|
|
return baseQuery;
|
|
}
|
|
|
|
internal static CSearchQueryHelper CreateQueryHelper()
|
|
{
|
|
// This uses the Microsoft.Search.Interop assembly
|
|
var manager = new CSearchManager();
|
|
|
|
// SystemIndex catalog is the default catalog in Windows
|
|
var catalogManager = manager.GetCatalog(SystemIndex);
|
|
|
|
// Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer
|
|
var queryHelper = catalogManager.GetQueryHelper();
|
|
|
|
return queryHelper;
|
|
}
|
|
|
|
private static string TopLevelDirectoryConstraint(string path) => $"directory='file:{path}'";
|
|
private static string RecursiveDirectoryConstraint(string path) => $"scope='file:{path}'";
|
|
|
|
///<summary>
|
|
/// Set the required WHERE clause restriction to search on the first level of a specified directory.
|
|
///</summary>
|
|
public string QueryWhereRestrictionsForTopLevelDirectorySearch(string path)
|
|
{
|
|
return QueryWhereRestrictionsFromLocationPath(path, "directory='file:");
|
|
}
|
|
|
|
///<summary>
|
|
/// Set the required WHERE clause restriction to search all files and subfolders of a specified directory.
|
|
///</summary>
|
|
public string QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(string path)
|
|
{
|
|
return QueryWhereRestrictionsFromLocationPath(path, "directory='scope:");
|
|
}
|
|
|
|
private string QueryWhereRestrictionsFromLocationPath(string path, string searchDepth)
|
|
{
|
|
if (path.EndsWith(Constants.DirectorySeperator))
|
|
return searchDepth + $"{path}'";
|
|
|
|
var indexOfSeparator = path.LastIndexOf(Constants.DirectorySeperator);
|
|
|
|
var itemName = path[(indexOfSeparator + 1)..];
|
|
|
|
if (itemName.StartsWith(Constants.AllFilesFolderSearchWildcard))
|
|
itemName = itemName[1..];
|
|
|
|
var previousLevelDirectory = path.Substring(0, indexOfSeparator);
|
|
|
|
if (string.IsNullOrEmpty(itemName))
|
|
return $"{searchDepth}{previousLevelDirectory}'";
|
|
|
|
return $"(System.FileName LIKE '{itemName}%' OR CONTAINS({FileName},'\"{itemName}*\"',1033)) AND {searchDepth}{previousLevelDirectory}'";
|
|
}
|
|
|
|
///<summary>
|
|
/// Search will be performed on all folders and files on the first level of a specified directory.
|
|
///</summary>
|
|
public string Directory(string path, string searchString = "", bool recursive = false)
|
|
{
|
|
var queryConstraint = searchString is "" ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\"{searchString}*\"'))";
|
|
|
|
var scopeConstraint = recursive
|
|
? RecursiveDirectoryConstraint(path)
|
|
: TopLevelDirectoryConstraint(path);
|
|
|
|
string query = $"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}";
|
|
|
|
return query;
|
|
}
|
|
|
|
///<summary>
|
|
/// Search will be performed on all folders and files based on user's search keywords.
|
|
///</summary>
|
|
public string FilesAndFolders(string userSearchString)
|
|
{
|
|
if (string.IsNullOrEmpty(userSearchString))
|
|
userSearchString = "*";
|
|
|
|
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
|
|
return $"{BaseQueryHelper.GenerateSQLFromUserQuery(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
|
|
}
|
|
|
|
///<summary>
|
|
/// Set the required WHERE clause restriction to search for all files and folders.
|
|
///</summary>
|
|
public const string RestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
|
|
|
|
/// <summary>
|
|
/// Order identifier: file name
|
|
/// </summary>
|
|
public const string FileName = "System.FileName";
|
|
|
|
|
|
///<summary>
|
|
/// Search will be performed on all indexed file contents for the specified search keywords.
|
|
///</summary>
|
|
public string FileContent(string userSearchString)
|
|
{
|
|
string query =
|
|
$"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
|
|
|
|
return query;
|
|
}
|
|
|
|
///<summary>
|
|
/// Set the required WHERE clause restriction to search within file content.
|
|
///</summary>
|
|
public static string RestrictionsForFileContentSearch(string searchQuery) => $"FREETEXT('{searchQuery}')";
|
|
}
|
|
}
|