mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #567 from pc223/SearchOrderByRank
Introduce new search order - System.Search.Rank - for Explorer plugin
This commit is contained in:
commit
9414710552
3 changed files with 27 additions and 26 deletions
|
|
@ -39,8 +39,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
|
||||
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
|
||||
[TestCase("C:\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
|
||||
{
|
||||
// Given
|
||||
|
|
@ -59,7 +59,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" +
|
||||
" FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" +
|
||||
" AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" +
|
||||
" ORDER BY System.FileName")]
|
||||
$" ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
|
||||
string folderPath, string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
@ -87,8 +87,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
|
||||
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
|
||||
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
|
||||
[TestCase("", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY System.FileName")]
|
||||
$"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
@ -107,7 +107,6 @@ namespace Flow.Launcher.Test.Plugins
|
|||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase(@"some words", @"FREETEXT('some words')")]
|
||||
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
|
||||
|
|
@ -127,7 +126,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
|
||||
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
|
||||
$"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
{
|
||||
public class QueryConstructor
|
||||
{
|
||||
private static Regex _specialCharacterMatcher = new(@"[\@\@\#\#\&\&*_;,\%\|\!\(\)\{\}\[\]\^\~\?\\""\/\:\=\-]+", RegexOptions.Compiled);
|
||||
private static Regex _multiWhiteSpacesMatcher = new(@"\s+", RegexOptions.Compiled);
|
||||
private static readonly Regex _specialCharacterMatcher = new(@"[\@\@\#\#\&\&*_;,\%\|\!\(\)\{\}\[\]\^\~\?\\""\/\:\=\-]+", RegexOptions.Compiled);
|
||||
private static readonly Regex _multiWhiteSpacesMatcher = new(@"\s+", RegexOptions.Compiled);
|
||||
|
||||
private Settings settings { get; }
|
||||
private Settings Settings { get; }
|
||||
|
||||
private const string SystemIndex = "SystemIndex";
|
||||
|
||||
public QueryConstructor(Settings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
public CSearchQueryHelper CreateBaseQuery()
|
||||
|
|
@ -23,7 +23,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
var baseQuery = CreateQueryHelper();
|
||||
|
||||
// Set the number of results we want. Don't set this property if all results are needed.
|
||||
baseQuery.QueryMaxResults = settings.MaxResult;
|
||||
baseQuery.QueryMaxResults = Settings.MaxResult;
|
||||
|
||||
// Set list of columns we want to display, getting the path presently
|
||||
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
|
||||
|
|
@ -37,7 +37,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
return baseQuery;
|
||||
}
|
||||
|
||||
internal CSearchQueryHelper CreateQueryHelper()
|
||||
internal static CSearchQueryHelper CreateQueryHelper()
|
||||
{
|
||||
// This uses the Microsoft.Search.Interop assembly
|
||||
// Throws COMException if Windows Search service is not running/disabled, this needs to be caught
|
||||
|
|
@ -54,20 +54,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
public static string TopLevelDirectoryConstraint(ReadOnlySpan<char> path) => $"directory='file:{path}'";
|
||||
public static string RecursiveDirectoryConstraint(ReadOnlySpan<char> path) => $"scope='file:{path}'";
|
||||
|
||||
|
||||
///<summary>
|
||||
/// Search will be performed on all folders and files on the first level of a specified directory.
|
||||
///</summary>
|
||||
public string Directory(ReadOnlySpan<char> path, ReadOnlySpan<char> searchString = default, bool recursive = false)
|
||||
{
|
||||
var queryConstraint = searchString.IsWhiteSpace() ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\"{searchString}*\"'))";
|
||||
var queryConstraint = searchString.IsWhiteSpace() ? "" : $"AND (System.FileName LIKE '{searchString}%' OR CONTAINS(System.FileName,'\"{searchString}*\"'))";
|
||||
|
||||
var scopeConstraint = recursive
|
||||
? RecursiveDirectoryConstraint(path)
|
||||
: TopLevelDirectoryConstraint(path);
|
||||
|
||||
var query = $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}";
|
||||
var query = $"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {OrderIdentifier}";
|
||||
|
||||
return query;
|
||||
}
|
||||
|
|
@ -84,7 +83,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
var replacedSearchString = ReplaceSpecialCharacterWithTwoSideWhiteSpace(userSearchString);
|
||||
|
||||
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
|
||||
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(replacedSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
|
||||
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(replacedSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -121,10 +120,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
public const string RestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
|
||||
|
||||
/// <summary>
|
||||
/// Order identifier: file name
|
||||
/// Order identifier: System.Search.Rank DESC
|
||||
/// </summary>
|
||||
public const string FileName = "System.FileName";
|
||||
|
||||
/// <remarks>
|
||||
/// <see href="https://docs.microsoft.com/en-us/windows/win32/properties/props-system-search-rank"/>
|
||||
/// </remarks>
|
||||
public const string OrderIdentifier = "System.Search.Rank DESC";
|
||||
|
||||
///<summary>
|
||||
/// Search will be performed on all indexed file contents for the specified search keywords.
|
||||
|
|
@ -132,7 +133,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
public string FileContent(ReadOnlySpan<char> userSearchString)
|
||||
{
|
||||
string query =
|
||||
$"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
|
||||
$"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
|
||||
|
||||
return query;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,14 +82,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
return HandledEngineNotAvailableExceptionAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
|
||||
{
|
||||
return WindowsIndexFilesAndFoldersSearchAsync(search, token: token);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
|
||||
{
|
||||
return WindowsIndexFileContentSearchAsync(contentSearch, token);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
|
||||
{
|
||||
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
|
||||
|
|
@ -100,19 +103,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
if (!Settings.WarnWindowsSearchServiceOff)
|
||||
return AsyncEnumerable.Empty<SearchResult>();
|
||||
|
||||
var api = Main.Context.API;
|
||||
|
||||
throw new EngineNotAvailableException(
|
||||
"Windows Index",
|
||||
api.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
|
||||
api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
|
||||
Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
|
||||
Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
|
||||
Constants.WindowsIndexErrorImagePath,
|
||||
c =>
|
||||
{
|
||||
Settings.WarnWindowsSearchServiceOff = false;
|
||||
|
||||
// Clears the warning message so user is not mistaken that it has not worked
|
||||
api.ChangeQuery(string.Empty);
|
||||
Main.Context.API.ChangeQuery(string.Empty);
|
||||
|
||||
return ValueTask.FromResult(false);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue