2022-12-15 11:03:54 +00:00
using System ;
2022-09-22 00:18:20 +00:00
using System.Buffers ;
2020-05-26 11:49:59 +00:00
using Microsoft.Search.Interop ;
2020-05-11 13:19:41 +00:00
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
public class QueryConstructor
{
2022-12-15 11:03:54 +00:00
private Settings settings { get ; }
2020-05-11 13:19:41 +00:00
2020-05-11 20:47:45 +00:00
private const string SystemIndex = "SystemIndex" ;
2020-05-11 13:19:41 +00:00
public QueryConstructor ( Settings settings )
{
2022-12-15 11:03:54 +00:00
this . settings = settings ;
2020-05-11 13:19:41 +00:00
}
2022-12-15 11:03:54 +00:00
public CSearchQueryHelper CreateBaseQuery ( )
2020-05-11 20:47:45 +00:00
{
2020-05-18 11:31:28 +00:00
var baseQuery = CreateQueryHelper ( ) ;
2020-05-11 20:47:45 +00:00
// Set the number of results we want. Don't set this property if all results are needed.
2022-12-15 11:03:54 +00:00
baseQuery . QueryMaxResults = settings . MaxResult ;
2020-05-11 20:47:45 +00:00
// Set list of columns we want to display, getting the path presently
2020-08-23 22:21:16 +00:00
baseQuery . QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType" ;
2020-05-11 20:47:45 +00:00
2020-05-18 11:31:28 +00:00
// Filter based on file name
2020-05-11 20:47:45 +00:00
baseQuery . QueryContentProperties = "System.FileName" ;
// Set sorting order
//baseQuery.QuerySorting = "System.ItemType DESC";
return baseQuery ;
}
2022-12-15 11:03:54 +00:00
internal CSearchQueryHelper CreateQueryHelper ( )
2020-05-18 11:31:28 +00:00
{
// This uses the Microsoft.Search.Interop assembly
2022-12-15 11:03:54 +00:00
// Throws COMException if Windows Search service is not running/disabled, this needs to be caught
2020-05-18 11:31:28 +00:00
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 ( ) ;
2022-09-10 15:45:41 +00:00
2020-05-18 11:31:28 +00:00
return queryHelper ;
}
2022-11-04 04:57:47 +00:00
public static string TopLevelDirectoryConstraint ( ReadOnlySpan < char > path ) = > $"directory='file:{path}'" ;
public static string RecursiveDirectoryConstraint ( ReadOnlySpan < char > path ) = > $"scope='file:{path}'" ;
2020-05-27 21:41:14 +00:00
2022-09-22 00:18:20 +00:00
2020-05-18 10:13:31 +00:00
///<summary>
/// Search will be performed on all folders and files on the first level of a specified directory.
///</summary>
2022-09-22 00:18:20 +00:00
public string Directory ( ReadOnlySpan < char > path , ReadOnlySpan < char > searchString = default , bool recursive = false )
2020-05-11 20:50:17 +00:00
{
2022-09-22 00:18:20 +00:00
var queryConstraint = searchString . IsWhiteSpace ( ) ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\" { searchString } * \ "'))" ;
2022-09-10 15:45:41 +00:00
var scopeConstraint = recursive
? RecursiveDirectoryConstraint ( path )
: TopLevelDirectoryConstraint ( path ) ;
2020-05-11 20:50:17 +00:00
2022-12-15 11:03:54 +00:00
var query = $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}" ;
2020-05-11 20:50:17 +00:00
2022-09-10 15:45:41 +00:00
return query ;
2020-05-11 20:50:17 +00:00
}
2020-05-18 10:14:38 +00:00
///<summary>
/// Search will be performed on all folders and files based on user's search keywords.
///</summary>
2022-09-22 00:18:20 +00:00
public string FilesAndFolders ( ReadOnlySpan < char > userSearchString )
2020-05-18 10:14:38 +00:00
{
2022-09-22 00:18:20 +00:00
if ( userSearchString . IsWhiteSpace ( ) )
2022-07-04 00:50:02 +00:00
userSearchString = "*" ;
2022-09-10 15:45:41 +00:00
2020-05-18 11:31:28 +00:00
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
2022-12-15 11:03:54 +00:00
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString.ToString())} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}" ;
2020-05-18 10:14:38 +00:00
}
///<summary>
/// Set the required WHERE clause restriction to search for all files and folders.
///</summary>
2022-09-10 15:45:41 +00:00
public const string RestrictionsForAllFilesAndFoldersSearch = "scope='file:'" ;
2021-01-23 02:53:01 +00:00
2022-09-10 15:45:41 +00:00
/// <summary>
/// Order identifier: file name
/// </summary>
public const string FileName = "System.FileName" ;
2021-01-23 02:53:01 +00:00
2020-07-12 12:43:38 +00:00
///<summary>
/// Search will be performed on all indexed file contents for the specified search keywords.
///</summary>
2022-09-22 00:18:20 +00:00
public string FileContent ( ReadOnlySpan < char > userSearchString )
2020-07-12 12:43:38 +00:00
{
2022-09-10 15:45:41 +00:00
string query =
2022-12-15 11:03:54 +00:00
$"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}" ;
2020-07-12 12:43:38 +00:00
2022-09-10 15:45:41 +00:00
return query ;
2020-07-12 12:43:38 +00:00
}
///<summary>
/// Set the required WHERE clause restriction to search within file content.
///</summary>
2022-09-22 00:18:20 +00:00
public static string RestrictionsForFileContentSearch ( ReadOnlySpan < char > searchQuery ) = > $"FREETEXT('{searchQuery}')" ;
2020-05-11 13:19:41 +00:00
}
}