Skip to content

Commit

Permalink
[PT Run] Split indexer plugin's queries into a fast and slow query (#…
Browse files Browse the repository at this point in the history
…5748)

* Added regex code

* Added regex based method to remove all LIKE queries

* Made regex readonly

* Added plugin interface and code to execute slower plugins after the fast plugins

* Added scoring for indexer and added statement to remove old indexer results

* Refactored from master and added thread sleep for debugging

* Removed lock from indexer plugin and added checks to avoid exceptions

* Remove debug statement

* Removed selected index update and fixed tests not building

* Added tests

* Removed scoring

* Removed lock

* Resolve merge conflicts

* Moved dispatcher code to function and add parallel foreach loop

* Removed DelayedExec metadata and modified QueryForPlugin to run only delayed exec plugins when bool param is true

* Removed metadata from plugin.json
  • Loading branch information
arjunbalgovind committed Aug 11, 2020
1 parent 304981f commit dcd0ca8
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 41 deletions.
19 changes: 16 additions & 3 deletions src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

namespace Microsoft.Plugin.Indexer
{
internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu, IDisposable
internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu, IDisposable, IDelayedExecutionPlugin
{
// This variable contains metadata about the Plugin
private PluginInitContext _context;
Expand Down Expand Up @@ -54,7 +54,7 @@ public void Save()

// This function uses the Windows indexer and returns the list of results obtained
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive but will log the exception")]
public List<Result> Query(Query query)
public List<Result> Query(Query query, bool isFullQuery)
{
var results = new List<Result>();

Expand Down Expand Up @@ -95,7 +95,14 @@ public List<Result> Query(Query query)
});
}

var searchResultsList = _api.Search(searchQuery, maxCount: _settings.MaxSearchCount).ToList();
var searchResultsList = _api.Search(searchQuery, isFullQuery, maxCount: _settings.MaxSearchCount).ToList();

// If the delayed execution query is not required (since the SQL query is fast) return empty results
if (searchResultsList.Count == 0 && isFullQuery)
{
return new List<Result>();
}

foreach (var searchResult in searchResultsList)
{
var path = searchResult.Path;
Expand Down Expand Up @@ -161,6 +168,12 @@ public List<Result> Query(Query query)
return results;
}

// This function uses the Windows indexer and returns the list of results obtained. This version is required to implement the interface
public List<Result> Query(Query query)
{
return Query(query, false);
}

public void Init(PluginInitContext context)
{
// initialize the context of the plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public List<OleDBResult> Query(string connectionString, string sqlQuery)
{
using (wDSResults = command.ExecuteReader())
{
if (wDSResults.HasRows)
if (!wDSResults.IsClosed && wDSResults.HasRows)
{
while (wDSResults.Read())
while (!wDSResults.IsClosed && wDSResults.Read())
{
List<object> fieldData = new List<object>();
for (int i = 0; i < wDSResults.FieldCount; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.Search.Interop;

namespace Microsoft.Plugin.Indexer.SearchHelper
Expand All @@ -13,16 +15,17 @@ public class WindowsSearchAPI
public bool DisplayHiddenFiles { get; set; }

private readonly ISearch windowsIndexerSearch;
private readonly object _lock = new object();

private const uint _fileAttributeHidden = 0x2;
private static readonly Regex _likeRegex = new Regex(@"[^\s(]+\s+LIKE\s+'([^']|'')*'\s+OR\s+", RegexOptions.Compiled);

public WindowsSearchAPI(ISearch windowsIndexerSearch, bool displayHiddenFiles = false)
{
this.windowsIndexerSearch = windowsIndexerSearch;
DisplayHiddenFiles = displayHiddenFiles;
}

public List<SearchResult> ExecuteQuery(ISearchQueryHelper queryHelper, string keyword)
public List<SearchResult> ExecuteQuery(ISearchQueryHelper queryHelper, string keyword, bool isFullQuery = false)
{
if (queryHelper == null)
{
Expand All @@ -33,6 +36,17 @@ public List<SearchResult> ExecuteQuery(ISearchQueryHelper queryHelper, string ke

// Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause
string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword);
var simplifiedQuery = SimplifyQuery(sqlQuery);

if (!isFullQuery)
{
sqlQuery = simplifiedQuery;
}
else if (simplifiedQuery.Equals(sqlQuery, StringComparison.CurrentCultureIgnoreCase))
{
// if a full query is requested but there is no difference between the queries, return empty results
return results;
}

// execute the command, which returns the results as an OleDBResults.
List<OleDBResult> oleDBResults = windowsIndexerSearch.Query(queryHelper.ConnectionString, sqlQuery);
Expand Down Expand Up @@ -121,15 +135,17 @@ public static void InitQueryHelper(out ISearchQueryHelper queryHelper, int maxCo
queryHelper.QuerySorting = "System.DateModified DESC";
}

public IEnumerable<SearchResult> Search(string keyword, string pattern = "*", int maxCount = 30)
public IEnumerable<SearchResult> Search(string keyword, bool isFullQuery = false, string pattern = "*", int maxCount = 30)
{
lock (_lock)
{
ISearchQueryHelper queryHelper;
InitQueryHelper(out queryHelper, maxCount);
ModifyQueryHelper(ref queryHelper, pattern);
return ExecuteQuery(queryHelper, keyword);
}
ISearchQueryHelper queryHelper;
InitQueryHelper(out queryHelper, maxCount);
ModifyQueryHelper(ref queryHelper, pattern);
return ExecuteQuery(queryHelper, keyword, isFullQuery);
}

public static string SimplifyQuery(string sqlQuery)
{
return _likeRegex.Replace(sqlQuery, string.Empty);
}
}
}
83 changes: 60 additions & 23 deletions src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,6 @@ private void InitializeKeyCommands()
if (!string.IsNullOrEmpty(QueryText))
{
ChangeQueryText(string.Empty, true);
// Push Event to UI SystemQuery has changed
OnPropertyChanged(nameof(SystemQueryText));
}
Expand Down Expand Up @@ -338,7 +337,6 @@ private ResultsViewModel SelectedResults
QueryText = string.Empty;
}
}

_selectedResults.Visibility = Visibility.Visible;
}
}
Expand All @@ -362,6 +360,7 @@ public Visibility MainWindowVisibility
{
PowerToysTelemetry.Log.WriteEvent(new LauncherHideEvent());
}

}
}

Expand Down Expand Up @@ -507,23 +506,41 @@ private void QueryResults()
}
currentCancellationToken.ThrowIfCancellationRequested();
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (query.RawQuery == _currentQuery.RawQuery)
{
Results.Results.NotifyChanges();
}
UpdateResultsListViewAfterQuery(query);
if (Results.Results.Count > 0)
{
Results.Visibility = Visibility.Visible;
Results.SelectedIndex = 0;
}
else
// Run the slower query of the DelayedExecution plugins
currentCancellationToken.ThrowIfCancellationRequested();
Parallel.ForEach(plugins, (plugin) =>
{
Results.Visibility = Visibility.Hidden;
}
}));
if (!plugin.Metadata.Disabled)
{
var results = PluginManager.QueryForPlugin(plugin, query, true);
currentCancellationToken.ThrowIfCancellationRequested();
if ((results?.Count ?? 0) != 0)
{
lock (_addResultsLock)
{
if (query.RawQuery == _currentQuery.RawQuery)
{
currentCancellationToken.ThrowIfCancellationRequested();
// Remove the original results from the plugin
Results.Results.RemoveAll(r => r.Result.PluginID == plugin.Metadata.ID);
currentCancellationToken.ThrowIfCancellationRequested();
// Add the new results from the plugin
UpdateResultView(results, query, currentCancellationToken);
currentCancellationToken.ThrowIfCancellationRequested();
Results.Sort();
}
}
currentCancellationToken.ThrowIfCancellationRequested();
UpdateResultsListViewAfterQuery(query, true);
}
}
});
}
catch (OperationCanceledException)
{
Expand All @@ -538,6 +555,7 @@ private void QueryResults()
QueryLength = query.RawQuery.Length
};
PowerToysTelemetry.Log.WriteEvent(queryEvent);
}, currentCancellationToken);
}
}
Expand All @@ -551,6 +569,30 @@ private void QueryResults()
}
}

private void UpdateResultsListViewAfterQuery(Query query, bool isDelayedInvoke = false)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (query.RawQuery == _currentQuery.RawQuery)
{
Results.Results.NotifyChanges();
}
if (Results.Results.Count > 0)
{
Results.Visibility = Visibility.Visible;
if (!isDelayedInvoke)
{
Results.SelectedIndex = 0;
}
}
else
{
Results.Visibility = Visibility.Hidden;
}
}));
}

private bool SelectedIsFromQueryResults()
{
var selected = SelectedResults == Results;
Expand Down Expand Up @@ -638,7 +680,6 @@ private void OnHotkey()
{
StartHotkeyTimer();
}
if (_settings.LastQueryMode == LastQueryMode.Empty)
{
ChangeQueryText(string.Empty);
Expand Down Expand Up @@ -746,9 +787,7 @@ public void ColdStartFix()
{
var _ = PluginManager.QueryForPlugin(plugin, query);
}
}

;
};
}

public void HandleContextMenu(Key AcceleratorKey, ModifierKeys AcceleratorModifiers)
Expand Down Expand Up @@ -795,7 +834,6 @@ public static string GetSearchText(int index, String input, string query)
return query + input.Substring(query.Length);
}
}

return input;
}

Expand Down Expand Up @@ -826,7 +864,6 @@ protected virtual void Dispose(bool disposing)
{
_hotkeyManager?.UnregisterHotkey(_hotkeyHandle);
}

_hotkeyManager?.Dispose();
_updateSource?.Dispose();
_disposed = true;
Expand Down
17 changes: 14 additions & 3 deletions src/modules/launcher/Wox.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,27 @@ public static List<PluginPair> ValidPluginsForQuery(Query query)
}
}

public static List<Result> QueryForPlugin(PluginPair pair, Query query)
public static List<Result> QueryForPlugin(PluginPair pair, Query query, bool delayedExecution = false)
{
try
{
List<Result> results = null;
var metadata = pair.Metadata;
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
{
results = pair.Plugin.Query(query) ?? new List<Result>();
UpdatePluginMetadata(results, metadata, query);
if (delayedExecution && (pair.Plugin is IDelayedExecutionPlugin))
{
results = ((IDelayedExecutionPlugin)pair.Plugin).Query(query, delayedExecution) ?? new List<Result>();
}
else if (!delayedExecution)
{
results = pair.Plugin.Query(query) ?? new List<Result>();
}
if (results != null)
{
UpdatePluginMetadata(results, metadata, query);
}
});
metadata.QueryCount += 1;
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
Expand Down
13 changes: 13 additions & 0 deletions src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

namespace Wox.Plugin
{
public interface IDelayedExecutionPlugin : IFeatures
{
List<Result> Query(Query query, bool delayedExecution);
}
}

0 comments on commit dcd0ca8

Please sign in to comment.