mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Refactor Explorer Code to fit the new architecture
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.
This commit is contained in:
parent
193ea552dd
commit
000c45bfa8
16 changed files with 155 additions and 128 deletions
|
|
@ -73,7 +73,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath);
|
||||
var queryString = queryConstructor.Directory(folderPath);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
|
|
@ -93,7 +93,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(userSearchString);
|
||||
var queryString = queryConstructor.Directory(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
|
|
@ -125,7 +125,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
|
||||
{
|
||||
//When
|
||||
var resultString = QueryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch;
|
||||
var resultString = QueryConstructor.RestrictionsForAllFilesAndFoldersSearch;
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
@ -142,7 +142,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
var baseQuery = queryConstructor.CreateBaseQuery();
|
||||
var baseQuery = queryConstructor.BaseQueryHelper;
|
||||
|
||||
// system running this test could have different locale than the hard-coded 1033 LCID en-US.
|
||||
var queryKeywordLocale = baseQuery.QueryKeywordLocale;
|
||||
|
|
@ -151,7 +151,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString);
|
||||
var resultString = queryConstructor.FilesAndFolders(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
@ -169,7 +169,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryWhereRestrictionsForFileContentSearch(querySearchString);
|
||||
var resultString = QueryConstructor.RestrictionsForFileContentSearch(querySearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
@ -187,7 +187,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryForFileContentSearch(userSearchString);
|
||||
var resultString = queryConstructor.FileContent(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
|
|||
|
|
@ -51,4 +51,8 @@
|
|||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Search\IIndexProvider.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -90,4 +90,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
|
|||
return results.OrderBy(r=>r.Type).ThenBy(r=>r.FullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,4 +193,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
||||
{
|
||||
public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
|
||||
public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider
|
||||
{
|
||||
private Settings Settings { get; }
|
||||
|
||||
|
|
@ -48,4 +49,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
{
|
||||
public interface IPathEnumerable
|
||||
{
|
||||
public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, CancellationToken token);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.IProvider
|
||||
{
|
||||
public interface IContentIndexProvider
|
||||
{
|
||||
public IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.IProvider
|
||||
{
|
||||
public interface IIndexProvider
|
||||
{
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.IProvider
|
||||
{
|
||||
public interface IPathIndexProvider
|
||||
{
|
||||
public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, CancellationToken token);
|
||||
}
|
||||
}
|
||||
|
|
@ -210,4 +210,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
Folder,
|
||||
File
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,30 +55,39 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
results.UnionWith(quickAccessLinks);
|
||||
}
|
||||
|
||||
IAsyncEnumerable<SearchResult> searchResults;
|
||||
|
||||
if (ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword))
|
||||
{
|
||||
// A backdoor to prevent everything to do content search
|
||||
if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch)
|
||||
{
|
||||
return EverythingContentSearchResult(query);
|
||||
}
|
||||
searchResults = Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
|
||||
return new List<Result>();
|
||||
}
|
||||
|
||||
IAsyncEnumerable<SearchResult> searchResults = null;
|
||||
|
||||
if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) ||
|
||||
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword))
|
||||
bool isPathSearch = query.Search.IsLocationPathString();
|
||||
|
||||
switch (isPathSearch)
|
||||
{
|
||||
results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false));
|
||||
case true
|
||||
when (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword)
|
||||
|| ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)):
|
||||
results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false));
|
||||
return results.ToList();
|
||||
break;
|
||||
case false when ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword):
|
||||
{
|
||||
// A backdoor to prevent everything to do content search
|
||||
if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch)
|
||||
{
|
||||
return EverythingContentSearchResult(query);
|
||||
}
|
||||
searchResults = Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
|
||||
break;
|
||||
}
|
||||
case false:
|
||||
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (searchResults == null)
|
||||
{
|
||||
return results.ToList();
|
||||
|
|
@ -154,7 +163,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists())
|
||||
return results.ToList();
|
||||
|
||||
var useIndexSearch = Settings.IndexSearchEngine is Settings.IndexSearchEngineOption.WindowsIndex
|
||||
var useIndexSearch = Settings.IndexSearchEngine is Settings.IndexSearchEngineOption.WindowsIndex
|
||||
&& UseWindowsIndexForDirectorySearch(locationPath);
|
||||
|
||||
results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch));
|
||||
|
|
@ -172,7 +181,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
query.Search[..recursiveIndicatorIndex],
|
||||
query.Search[(recursiveIndicatorIndex + 1)..],
|
||||
true,
|
||||
token).ToListAsync(cancellationToken: token)
|
||||
token)
|
||||
.ToListAsync(cancellationToken: token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
|
@ -207,7 +217,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
}
|
||||
|
||||
public static bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword;
|
||||
|
||||
|
||||
|
||||
private bool UseWindowsIndexForDirectorySearch(string locationPath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
{
|
||||
public struct SearchResult
|
||||
public record struct SearchResult
|
||||
{
|
||||
public string FullPath { get; init; }
|
||||
public ResultType Type { get; init; }
|
||||
public int Score { get; init; }
|
||||
|
||||
public bool WindowsIndexed { get; init; }
|
||||
|
||||
public bool ShowIndexState { get; init; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,25 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
{
|
||||
public class QueryConstructor
|
||||
{
|
||||
private readonly Settings settings;
|
||||
private Settings Settings { get; }
|
||||
|
||||
private const string SystemIndex = "SystemIndex";
|
||||
|
||||
public CSearchQueryHelper BaseQueryHelper { get; }
|
||||
|
||||
public QueryConstructor(Settings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
Settings = settings;
|
||||
BaseQueryHelper = CreateBaseQuery();
|
||||
}
|
||||
|
||||
public CSearchQueryHelper 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;
|
||||
baseQuery.QueryMaxResults = Settings.MaxResult;
|
||||
|
||||
// Set list of columns we want to display, getting the path presently
|
||||
baseQuery.QuerySelectColumns = "System.FileName, System.ItemUrl, System.ItemType";
|
||||
|
|
@ -32,7 +36,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
return baseQuery;
|
||||
}
|
||||
|
||||
internal CSearchQueryHelper CreateQueryHelper()
|
||||
internal static CSearchQueryHelper CreateQueryHelper()
|
||||
{
|
||||
// This uses the Microsoft.Search.Interop assembly
|
||||
var manager = new CSearchManager();
|
||||
|
|
@ -42,18 +46,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
// 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)
|
||||
{
|
||||
var searchDepth = $"directory='file:";
|
||||
|
||||
return QueryWhereRestrictionsFromLocationPath(path, searchDepth);
|
||||
return QueryWhereRestrictionsFromLocationPath(path, "directory='file:");
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
@ -61,9 +66,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
///</summary>
|
||||
public string QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(string path)
|
||||
{
|
||||
var searchDepth = $"scope='file:";
|
||||
|
||||
return QueryWhereRestrictionsFromLocationPath(path, searchDepth);
|
||||
return QueryWhereRestrictionsFromLocationPath(path, "directory='scope:");
|
||||
}
|
||||
|
||||
private string QueryWhereRestrictionsFromLocationPath(string path, string searchDepth)
|
||||
|
|
@ -73,70 +76,72 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
var indexOfSeparator = path.LastIndexOf(Constants.DirectorySeperator);
|
||||
|
||||
var itemName = path.Substring(indexOfSeparator + 1);
|
||||
var itemName = path[(indexOfSeparator + 1)..];
|
||||
|
||||
if (itemName.StartsWith(Constants.AllFilesFolderSearchWildcard))
|
||||
itemName = itemName.Substring(1);
|
||||
itemName = itemName[1..];
|
||||
|
||||
var previousLevelDirectory = path.Substring(0, indexOfSeparator);
|
||||
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
return $"{searchDepth}{previousLevelDirectory}'";
|
||||
|
||||
return $"(System.FileName LIKE '{itemName}%' OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND {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 QueryForTopLevelDirectorySearch(string path)
|
||||
public string Directory(string path, string searchString = "", bool recursive = false)
|
||||
{
|
||||
string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE ";
|
||||
var queryConstraint = searchString is "" ? "" : $"AND ({FileName} LIKE '{searchString}%' OR CONTAINS({FileName},'\"{searchString}*\"'))";
|
||||
|
||||
if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator))
|
||||
return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path) + QueryOrderByFileNameRestriction;
|
||||
var scopeConstraint = recursive
|
||||
? RecursiveDirectoryConstraint(path)
|
||||
: TopLevelDirectoryConstraint(path);
|
||||
|
||||
return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path) + QueryOrderByFileNameRestriction;
|
||||
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 QueryForAllFilesAndFolders(string userSearchString)
|
||||
public string FilesAndFolders(string userSearchString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userSearchString))
|
||||
userSearchString = "*";
|
||||
|
||||
|
||||
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
|
||||
return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch
|
||||
+ QueryOrderByFileNameRestriction;
|
||||
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 QueryWhereRestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
|
||||
public const string RestrictionsForAllFilesAndFoldersSearch = "scope='file:'";
|
||||
|
||||
public const string QueryOrderByFileNameRestriction = " ORDER BY System.FileName";
|
||||
/// <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 QueryForFileContentSearch(string userSearchString)
|
||||
public string FileContent(string userSearchString)
|
||||
{
|
||||
string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE ";
|
||||
string query =
|
||||
$"SELECT TOP {Settings.MaxResult} {BaseQueryHelper.QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {FileName}";
|
||||
|
||||
return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch
|
||||
+ QueryOrderByFileNameRestriction;
|
||||
return query;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Set the required WHERE clause restriction to search within file content.
|
||||
///</summary>
|
||||
public string QueryWhereRestrictionsForFileContentSearch(string searchQuery)
|
||||
{
|
||||
return $"FREETEXT('{searchQuery}')";
|
||||
}
|
||||
public static string RestrictionsForFileContentSearch(string searchQuery) => $"FREETEXT('{searchQuery}')";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
{
|
||||
|
||||
// Reserved keywords in oleDB
|
||||
private const string ReservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$";
|
||||
private static Regex _reservedPatternMatcher = new(@"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$", RegexOptions.Compiled);
|
||||
|
||||
private static async IAsyncEnumerable<SearchResult> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, [EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
|
|
@ -28,31 +28,42 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
await using var command = new OleDbCommand(indexQueryString, conn);
|
||||
// Results return as an OleDbDataReader.
|
||||
await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
|
||||
OleDbDataReader dataReaderAttempt;
|
||||
try
|
||||
{
|
||||
dataReaderAttempt = await command.ExecuteReaderAsync(token) as OleDbDataReader;
|
||||
}
|
||||
catch (OleDbException e)
|
||||
{
|
||||
Log.Exception($"|WindowsIndex.ExecuteWindowsIndexSearchAsync|Failed to execute windows index search query: {indexQueryString}", e);
|
||||
yield break;
|
||||
}
|
||||
await using var dataReader = dataReaderAttempt;
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (dataReaderResults is not { HasRows: true })
|
||||
if (dataReader is not { HasRows: true })
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
while (await dataReaderResults.ReadAsync(token))
|
||||
|
||||
while (await dataReader.ReadAsync(token))
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (dataReaderResults.GetValue(0) == DBNull.Value || dataReaderResults.GetValue(1) == DBNull.Value)
|
||||
if (dataReader.GetValue(0) == DBNull.Value || dataReader.GetValue(1) == DBNull.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
|
||||
var encodedFragmentPath = dataReaderResults
|
||||
var encodedFragmentPath = dataReader
|
||||
.GetString(1)
|
||||
.Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var path = new Uri(encodedFragmentPath).LocalPath;
|
||||
|
||||
yield return new SearchResult()
|
||||
yield return new SearchResult
|
||||
{
|
||||
FullPath = path,
|
||||
Type = dataReaderResults.GetString(2) == "Directory" ? ResultType.Folder : ResultType.File,
|
||||
Type = dataReader.GetString(2) == "Directory" ? ResultType.Folder : ResultType.File,
|
||||
WindowsIndexed = true
|
||||
};
|
||||
}
|
||||
|
|
@ -60,22 +71,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
|
||||
}
|
||||
|
||||
internal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(string searchString,
|
||||
Func<CSearchQueryHelper> createQueryHelper,
|
||||
Func<string, string> constructQuery,
|
||||
IEnumerable<AccessLink> exclusionList,
|
||||
|
||||
|
||||
internal static IAsyncEnumerable<SearchResult> WindowsIndexSearchAsync(string connectionString,
|
||||
string search,
|
||||
CancellationToken token)
|
||||
{
|
||||
var regexMatch = Regex.Match(searchString, ReservedStringPattern);
|
||||
|
||||
if (regexMatch.Success)
|
||||
return AsyncEnumerable.Empty<SearchResult>();
|
||||
|
||||
var constructedQuery = constructQuery(searchString);
|
||||
|
||||
return ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, token);
|
||||
return _reservedPatternMatcher.IsMatch(search)
|
||||
? AsyncEnumerable.Empty<SearchResult>()
|
||||
: ExecuteWindowsIndexSearchAsync(search, connectionString, token);
|
||||
}
|
||||
|
||||
// TODO: Move to General Search Manager
|
||||
private static void RemoveResultsInExclusionList(List<SearchResult> results, IReadOnlyList<AccessLink> exclusionList, CancellationToken token)
|
||||
{
|
||||
var indexExclusionListCount = exclusionList.Count;
|
||||
|
|
@ -102,6 +109,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Use a custom exception to handle this
|
||||
private static List<Result> ResultForWindexSearchOff(string rawQuery)
|
||||
{
|
||||
var api = SearchManager.Context.API;
|
||||
|
|
@ -160,4 +168,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,24 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
||||
using Microsoft.Search.Interop;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||
{
|
||||
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
|
||||
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathIndexProvider
|
||||
{
|
||||
private Settings Settings { get; }
|
||||
private QueryConstructor QueryConstructor { get; }
|
||||
|
||||
private CSearchQueryHelper QueryHelper { get; }
|
||||
public WindowsIndexSearchManager(Settings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
QueryConstructor = new QueryConstructor(Settings);
|
||||
QueryHelper = QueryConstructor.CreateQueryHelper();
|
||||
}
|
||||
|
||||
|
||||
private IAsyncEnumerable<SearchResult> WindowsIndexFileContentSearchAsync(string querySearchString,
|
||||
CancellationToken token)
|
||||
{
|
||||
|
|
@ -22,39 +27,35 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
return AsyncEnumerable.Empty<SearchResult>();
|
||||
|
||||
return WindowsIndex.WindowsIndexSearchAsync(
|
||||
querySearchString,
|
||||
QueryConstructor.CreateQueryHelper,
|
||||
QueryConstructor.QueryForFileContentSearch,
|
||||
Settings.IndexSearchExcludedSubdirectoryPaths,
|
||||
QueryHelper.ConnectionString,
|
||||
QueryConstructor.FileContent(querySearchString),
|
||||
token);
|
||||
}
|
||||
|
||||
private IAsyncEnumerable<SearchResult> WindowsIndexFilesAndFoldersSearchAsync(string querySearchString,
|
||||
CancellationToken token)
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return WindowsIndex.WindowsIndexSearchAsync(
|
||||
querySearchString,
|
||||
QueryConstructor.CreateQueryHelper,
|
||||
QueryConstructor.QueryForAllFilesAndFolders,
|
||||
Settings.IndexSearchExcludedSubdirectoryPaths,
|
||||
QueryHelper.ConnectionString,
|
||||
QueryConstructor.FilesAndFolders(querySearchString),
|
||||
token);
|
||||
}
|
||||
|
||||
private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(string path,string search,
|
||||
private IAsyncEnumerable<SearchResult> WindowsIndexTopLevelFolderSearchAsync(string search,
|
||||
string path,
|
||||
bool recursive,
|
||||
CancellationToken token)
|
||||
{
|
||||
var queryConstructor = new QueryConstructor(Settings);
|
||||
|
||||
return WindowsIndex.WindowsIndexSearchAsync(
|
||||
path,
|
||||
queryConstructor.CreateQueryHelper,
|
||||
queryConstructor.QueryForTopLevelDirectorySearch,
|
||||
Settings.IndexSearchExcludedSubdirectoryPaths,
|
||||
QueryConstructor.CreateQueryHelper().ConnectionString,
|
||||
queryConstructor.Directory(path, search, recursive),
|
||||
token);
|
||||
}
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
|
||||
{
|
||||
return WindowsIndexFilesAndFoldersSearchAsync(search, token);
|
||||
return WindowsIndexFilesAndFoldersSearchAsync(search, token: token);
|
||||
}
|
||||
public IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
|
||||
{
|
||||
|
|
@ -62,7 +63,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
}
|
||||
public IAsyncEnumerable<SearchResult> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
|
||||
{
|
||||
return recursive ? WindowsIndexFilesAndFoldersSearchAsync(search, token) : WindowsIndexTopLevelFolderSearchAsync(path, search, token);
|
||||
return WindowsIndexTopLevelFolderSearchAsync(search, path, recursive, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Collections.ObjectModel;
|
|||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.IProvider;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer
|
||||
{
|
||||
|
|
@ -65,7 +66,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
|
||||
#region SearchEngine
|
||||
|
||||
public PathEnumerationEngineOption PathEnumerationEngine { get; set; }
|
||||
public PathEnumerationEngineOption PathEnumerationEngine { get; set; } = PathEnumerationEngineOption.WindowsIndex;
|
||||
|
||||
private EverythingSearchManager EverythingManagerInstance => _everythingManagerInstance ??= new EverythingSearchManager(this);
|
||||
private WindowsIndexSearchManager WindowsIndexSearchManager => _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this);
|
||||
|
|
@ -81,7 +82,7 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
};
|
||||
|
||||
[JsonIgnore]
|
||||
public IPathEnumerable PathEnumerator => PathEnumerationEngine switch
|
||||
public IPathIndexProvider PathEnumerator => PathEnumerationEngine switch
|
||||
{
|
||||
PathEnumerationEngineOption.Everything => EverythingManagerInstance,
|
||||
PathEnumerationEngineOption.WindowsIndex => WindowsIndexSearchManager,
|
||||
|
|
@ -192,4 +193,4 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue