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-09-10 15:45:41 +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" ;
2022-09-10 15:45:41 +00:00
public CSearchQueryHelper BaseQueryHelper { get ; }
2020-05-11 13:19:41 +00:00
public QueryConstructor ( Settings settings )
{
2022-09-10 15:45:41 +00:00
Settings = settings ;
BaseQueryHelper = CreateBaseQuery ( ) ;
2020-05-11 13:19:41 +00:00
}
2022-09-10 15:45:41 +00:00
private 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-09-10 15:45:41 +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-09-10 15:45:41 +00:00
internal static CSearchQueryHelper CreateQueryHelper ( )
2020-05-18 11:31:28 +00:00
{
// 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 ( ) ;
2022-09-10 15:45:41 +00:00
2020-05-18 11:31:28 +00:00
return queryHelper ;
}
2022-09-10 15:45:41 +00:00
private static string TopLevelDirectoryConstraint ( string path ) = > $"directory='file:{path}'" ;
private static string RecursiveDirectoryConstraint ( string path ) = > $"scope='file:{path}'" ;
2020-05-11 13:19:41 +00:00
///<summary>
2020-05-18 10:13:31 +00:00
/// Set the required WHERE clause restriction to search on the first level of a specified directory.
2020-05-11 13:19:41 +00:00
///</summary>
public string QueryWhereRestrictionsForTopLevelDirectorySearch ( string path )
{
2022-09-10 15:45:41 +00:00
return QueryWhereRestrictionsFromLocationPath ( path , "directory='file:" ) ;
2020-05-27 21:41:14 +00:00
}
///<summary>
/// Set the required WHERE clause restriction to search all files and subfolders of a specified directory.
///</summary>
public string QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch ( string path )
{
2022-09-10 15:45:41 +00:00
return QueryWhereRestrictionsFromLocationPath ( path , "directory='scope:" ) ;
2020-05-27 21:41:14 +00:00
}
private string QueryWhereRestrictionsFromLocationPath ( string path , string searchDepth )
{
2020-05-28 11:35:12 +00:00
if ( path . EndsWith ( Constants . DirectorySeperator ) )
2020-05-27 21:41:14 +00:00
return searchDepth + $"{path}'" ;
2020-05-26 11:55:07 +00:00
2020-05-28 11:35:12 +00:00
var indexOfSeparator = path . LastIndexOf ( Constants . DirectorySeperator ) ;
2020-05-26 11:55:07 +00:00
2022-09-10 15:45:41 +00:00
var itemName = path [ ( indexOfSeparator + 1 ) . . ] ;
2020-05-26 11:55:07 +00:00
2020-05-28 11:35:12 +00:00
if ( itemName . StartsWith ( Constants . AllFilesFolderSearchWildcard ) )
2022-09-10 15:45:41 +00:00
itemName = itemName [ 1. . ] ;
2020-05-27 21:41:14 +00:00
2020-05-26 11:55:07 +00:00
var previousLevelDirectory = path . Substring ( 0 , indexOfSeparator ) ;
2020-05-27 21:41:14 +00:00
if ( string . IsNullOrEmpty ( itemName ) )
2021-01-23 03:32:41 +00:00
return $"{searchDepth}{previousLevelDirectory}'" ;
2020-05-27 21:41:14 +00:00
2022-09-10 15:45:41 +00:00
return $"(System.FileName LIKE '{itemName}%' OR CONTAINS({FileName},'\" { itemName } * \ "',1033)) AND {searchDepth}{previousLevelDirectory}'" ;
2020-05-11 13:19:41 +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-10 15:45:41 +00:00
public string Directory ( string path , string searchString = "" , bool recursive = false )
2020-05-11 20:50:17 +00:00
{
2022-09-10 15:45:41 +00:00
var queryConstraint = searchString is "" ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\" { searchString } * \ "'))" ;
var scopeConstraint = recursive
? RecursiveDirectoryConstraint ( path )
: TopLevelDirectoryConstraint ( path ) ;
2020-05-11 20:50:17 +00:00
2022-09-10 15:45:41 +00:00
string query = $"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.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-10 15:45:41 +00:00
public string FilesAndFolders ( string userSearchString )
2020-05-18 10:14:38 +00:00
{
2022-07-04 00:50:02 +00:00
if ( string . IsNullOrEmpty ( userSearchString ) )
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-09-10 15:45:41 +00:00
return $"{BaseQueryHelper.GenerateSQLFromUserQuery(userSearchString)} 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-10 15:45:41 +00:00
public string FileContent ( string userSearchString )
2020-07-12 12:43:38 +00:00
{
2022-09-10 15:45:41 +00:00
string query =
$"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.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-10 15:45:41 +00:00
public static string RestrictionsForFileContentSearch ( string searchQuery ) = > $"FREETEXT('{searchQuery}')" ;
2020-05-11 13:19:41 +00:00
}
}