Skip to content

Commit 9414710

Browse files
authored
Merge pull request #567 from pc223/SearchOrderByRank
Introduce new search order - System.Search.Rank - for Explorer plugin
2 parents c8d16a8 + a9748ac commit 9414710

File tree

3 files changed

+27
-26
lines changed

3 files changed

+27
-26
lines changed

Flow.Launcher.Test/Plugins/ExplorerTest.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestric
3939
}
4040

4141
[SupportedOSPlatform("windows7.0")]
42-
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
43-
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
42+
[TestCase("C:\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
43+
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
4444
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
4545
{
4646
// Given
@@ -59,7 +59,7 @@ public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_Then
5959
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" +
6060
" FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" +
6161
" AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" +
62-
" ORDER BY System.FileName")]
62+
$" ORDER BY {QueryConstructor.OrderIdentifier}")]
6363
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
6464
string folderPath, string userSearchString, string expectedString)
6565
{
@@ -87,8 +87,8 @@ public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereR
8787
[SupportedOSPlatform("windows7.0")]
8888
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
8989
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
90-
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
91-
[TestCase("", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY System.FileName")]
90+
$"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
91+
[TestCase("", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
9292
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
9393
string userSearchString, string expectedString)
9494
{
@@ -107,7 +107,6 @@ public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShould
107107
ClassicAssert.AreEqual(expectedString, resultString);
108108
}
109109

110-
111110
[SupportedOSPlatform("windows7.0")]
112111
[TestCase(@"some words", @"FREETEXT('some words')")]
113112
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
@@ -127,7 +126,7 @@ public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSe
127126

128127
[SupportedOSPlatform("windows7.0")]
129128
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
130-
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
129+
$"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
131130
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
132131
string userSearchString, string expectedString)
133132
{

Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
66
{
77
public class QueryConstructor
88
{
9-
private static Regex _specialCharacterMatcher = new(@"[\@\@\#\#\&\&*_;,\%\|\!\(\)\{\}\[\]\^\~\?\\""\/\:\=\-]+", RegexOptions.Compiled);
10-
private static Regex _multiWhiteSpacesMatcher = new(@"\s+", RegexOptions.Compiled);
9+
private static readonly Regex _specialCharacterMatcher = new(@"[\@\@\#\#\&\&*_;,\%\|\!\(\)\{\}\[\]\^\~\?\\""\/\:\=\-]+", RegexOptions.Compiled);
10+
private static readonly Regex _multiWhiteSpacesMatcher = new(@"\s+", RegexOptions.Compiled);
1111

12-
private Settings settings { get; }
12+
private Settings Settings { get; }
1313

1414
private const string SystemIndex = "SystemIndex";
1515

1616
public QueryConstructor(Settings settings)
1717
{
18-
this.settings = settings;
18+
Settings = settings;
1919
}
2020

2121
public CSearchQueryHelper CreateBaseQuery()
2222
{
2323
var baseQuery = CreateQueryHelper();
2424

2525
// Set the number of results we want. Don't set this property if all results are needed.
26-
baseQuery.QueryMaxResults = settings.MaxResult;
26+
baseQuery.QueryMaxResults = Settings.MaxResult;
2727

2828
// Set list of columns we want to display, getting the path presently
2929
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
@@ -37,7 +37,7 @@ public CSearchQueryHelper CreateBaseQuery()
3737
return baseQuery;
3838
}
3939

40-
internal CSearchQueryHelper CreateQueryHelper()
40+
internal static CSearchQueryHelper CreateQueryHelper()
4141
{
4242
// This uses the Microsoft.Search.Interop assembly
4343
// Throws COMException if Windows Search service is not running/disabled, this needs to be caught
@@ -54,20 +54,19 @@ internal CSearchQueryHelper CreateQueryHelper()
5454

5555
public static string TopLevelDirectoryConstraint(ReadOnlySpan<char> path) => $"directory='file:{path}'";
5656
public static string RecursiveDirectoryConstraint(ReadOnlySpan<char> path) => $"scope='file:{path}'";
57-
5857

5958
///<summary>
6059
/// Search will be performed on all folders and files on the first level of a specified directory.
6160
///</summary>
6261
public string Directory(ReadOnlySpan<char> path, ReadOnlySpan<char> searchString = default, bool recursive = false)
6362
{
64-
var queryConstraint = searchString.IsWhiteSpace() ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\"{searchString}*\"'))";
63+
var queryConstraint = searchString.IsWhiteSpace() ? "" : $"AND (System.FileName LIKE '{searchString}%' OR CONTAINS(System.FileName,'\"{searchString}*\"'))";
6564

6665
var scopeConstraint = recursive
6766
? RecursiveDirectoryConstraint(path)
6867
: TopLevelDirectoryConstraint(path);
6968

70-
var query = $"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {FileName}";
69+
var query = $"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {OrderIdentifier}";
7170

7271
return query;
7372
}
@@ -84,7 +83,7 @@ public string FilesAndFolders(ReadOnlySpan<char> userSearchString)
8483
var replacedSearchString = ReplaceSpecialCharacterWithTwoSideWhiteSpace(userSearchString);
8584

8685
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
87-
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(replacedSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
86+
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(replacedSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
8887
}
8988

9089
/// <summary>
@@ -121,18 +120,20 @@ private static string ReplaceSpecialCharacterWithTwoSideWhiteSpace(ReadOnlySpan<
121120
public const string RestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
122121

123122
/// <summary>
124-
/// Order identifier: file name
123+
/// Order identifier: System.Search.Rank DESC
125124
/// </summary>
126-
public const string FileName = "System.FileName";
127-
125+
/// <remarks>
126+
/// <see href="https://docs.microsoft.com/en-us/windows/win32/properties/props-system-search-rank"/>
127+
/// </remarks>
128+
public const string OrderIdentifier = "System.Search.Rank DESC";
128129

129130
///<summary>
130131
/// Search will be performed on all indexed file contents for the specified search keywords.
131132
///</summary>
132133
public string FileContent(ReadOnlySpan<char> userSearchString)
133134
{
134135
string query =
135-
$"SELECT TOP {settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
136+
$"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
136137

137138
return query;
138139
}

Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,17 @@ private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(
8282
return HandledEngineNotAvailableExceptionAsync();
8383
}
8484
}
85+
8586
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
8687
{
8788
return WindowsIndexFilesAndFoldersSearchAsync(search, token: token);
8889
}
90+
8991
public IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
9092
{
9193
return WindowsIndexFileContentSearchAsync(contentSearch, token);
9294
}
95+
9396
public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
9497
{
9598
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
@@ -100,19 +103,17 @@ private IAsyncEnumerable<SearchResult> HandledEngineNotAvailableExceptionAsync()
100103
if (!Settings.WarnWindowsSearchServiceOff)
101104
return AsyncEnumerable.Empty<SearchResult>();
102105

103-
var api = Main.Context.API;
104-
105106
throw new EngineNotAvailableException(
106107
"Windows Index",
107-
api.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
108-
api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
108+
Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceFix"),
109+
Main.Context.API.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"),
109110
Constants.WindowsIndexErrorImagePath,
110111
c =>
111112
{
112113
Settings.WarnWindowsSearchServiceOff = false;
113114

114115
// Clears the warning message so user is not mistaken that it has not worked
115-
api.ChangeQuery(string.Empty);
116+
Main.Context.API.ChangeQuery(string.Empty);
116117

117118
return ValueTask.FromResult(false);
118119
});

0 commit comments

Comments
 (0)