Skip to content

Commit

Permalink
Change search indexing methods to use trie instead, drastically impro…
Browse files Browse the repository at this point in the history
…ves search time
  • Loading branch information
Kaioru committed Oct 2, 2023
1 parent f7433c4 commit 655a949
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Diagnostics;
using DotNet.Globbing;
using Edelstein.Protocol.Gameplay.Game.Objects.User;
using Edelstein.Protocol.Utilities.Templates;
using Gma.DataStructures.StringSearch;
using PowerArgs;

namespace Edelstein.Plugin.Rue.Commands;
Expand All @@ -26,35 +26,51 @@ protected AbstractTemplateCommand(ITemplateManager<TTemplate> templates) : base(
}
}

public abstract class AbstractTemplateCommand<TTemplate, TArgs> : AbstractCommand<TArgs>
public abstract class AbstractTemplateCommand<TTemplate, TArgs> : AbstractCommand<TArgs>, IIndexedCommand
where TTemplate : class, ITemplate
where TArgs : TemplateCommandArgs
{
private readonly ITemplateManager<TTemplate> _templates;
private readonly GlobOptions _options = new() { Evaluation = { CaseInsensitive = true } };
private readonly ITrie<TemplateCommandIndex> _trie;
private bool isIndexing;
private bool isIndexed;

protected AbstractTemplateCommand(
ITemplateManager<TTemplate> templates
)
{
_templates = templates;
_trie = new UkkonenTrie<TemplateCommandIndex>();
}

protected abstract Task<IEnumerable<TemplateCommandIndex>> Indices();
protected abstract Task Execute(IFieldUser user, TTemplate template, TArgs args);

public async Task Index()
{
if (isIndexed || isIndexing) return;
isIndexing = true;
foreach (var index in await Indices())
_trie.Add(index.SearchString.ToLower(), index);
isIndexing = false;
isIndexed = true;
}

protected override async Task Execute(IFieldUser user, TArgs args)
{
if (isIndexing || !isIndexed)
{
await user.Message("Templates have not finished indexing yet, please try again later..");
return;
}

var stopwatch = new Stopwatch();

await user.Message($"Searching for '{args.Search}', this might take awhile..");
stopwatch.Start();

var glob = Glob.Parse(args.Search, _options);
var data = await Indices();

var results = data
.Where(d => glob.IsMatch(d.SearchString))
var results = _trie
.Retrieve(args.Search.ToLower())
.DistinctBy(d => d.ID)
.ToList();
var elapsed = stopwatch.Elapsed;
Expand Down Expand Up @@ -91,7 +107,7 @@ protected override async Task Execute(IFieldUser user, TArgs args)
if (currentPage < maxPage) menu.Add(-10, "#rNext page#k");
if (currentPage > minPage) menu.Add(-20, "#rPrevious page#k");

var selection = target.AskMenu($"Found {results.Count} results for '{args.Search}' in {elapsed.TotalSeconds:F} seconds (page {currentPage} of {maxPage})", menu);
var selection = target.AskMenu($"Found {results.Count} results for '{args.Search}' in {elapsed.TotalMilliseconds:F2}ms (page {currentPage} of {maxPage})", menu);

if (selection == -10) { currentPage++; continue; }
if (selection == -20) { currentPage--; continue; }
Expand Down
6 changes: 6 additions & 0 deletions src/plugin/Edelstein.Plugin.Rue/Commands/IIndexedCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Edelstein.Plugin.Rue.Commands;

public interface IIndexedCommand : ICommand
{
Task Index();
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="PowerArgs" Version="4.0.2" />
<PackageReference Include="TrieNet" Version="1.0.3.26316" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Edelstein.Plugin.Rue.Contracts;
using Edelstein.Protocol.Gameplay.Login.Contexts;
using Edelstein.Protocol.Gameplay.Login.Contracts;
using Edelstein.Protocol.Services.Auth.Contracts;
using Edelstein.Protocol.Utilities.Pipelines;
using Microsoft.Extensions.Logging;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Edelstein.Plugin.Rue.Configs;
using Edelstein.Plugin.Rue.Contracts;
using Edelstein.Protocol.Gameplay.Game.Contexts;
using Edelstein.Protocol.Gameplay.Login.Contexts;
using Edelstein.Protocol.Gameplay.Login.Contracts;
using Edelstein.Protocol.Utilities.Pipelines;
Expand Down
41 changes: 27 additions & 14 deletions src/plugin/Edelstein.Plugin.Rue/RueGamePlugin.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Edelstein.Plugin.Rue.Commands;
using System.Diagnostics;
using Edelstein.Plugin.Rue.Commands;
using Edelstein.Plugin.Rue.Commands.Admin;
using Edelstein.Plugin.Rue.Commands.Common;
using Edelstein.Plugin.Rue.Plugs;
Expand All @@ -21,37 +22,49 @@ public Task OnInit(IPluginHost<GameContext> host, GameContext ctx)
return Task.CompletedTask;
}

public Task OnStart(IPluginHost<GameContext> host, GameContext ctx)
public async Task OnStart(IPluginHost<GameContext> host, GameContext ctx)
{
var commandManager = new CommandManager();

commandManager.Insert(new HelpCommand(commandManager));
commandManager.Insert(new AliasCommand(commandManager));
await commandManager.Insert(new HelpCommand(commandManager));
await commandManager.Insert(new AliasCommand(commandManager));

commandManager.Insert(new FieldCommand(
await commandManager.Insert(new FieldCommand(
ctx.Managers.Field,
ctx.Templates.Field,
ctx.Templates.FieldString
));
commandManager.Insert(new ItemCommand(
await commandManager.Insert(new ItemCommand(
ctx.Templates.Item,
ctx.Templates.ItemString
));
commandManager.Insert(new SkillCommand(
await commandManager.Insert(new SkillCommand(
ctx.Templates.Skill,
ctx.Templates.SkillString
));
commandManager.Insert(new QuestCommand(
await commandManager.Insert(new QuestCommand(
ctx.Templates.Quest
));
commandManager.Insert(new EquipCommand());
commandManager.Insert(new StatCommand());
commandManager.Insert(new TemporaryStatCommand());
commandManager.Insert(new MobTemporaryStatCommand());
commandManager.Insert(new DebugCommand());
await commandManager.Insert(new EquipCommand());
await commandManager.Insert(new StatCommand());
await commandManager.Insert(new TemporaryStatCommand());
await commandManager.Insert(new MobTemporaryStatCommand());
await commandManager.Insert(new DebugCommand());

ctx.Pipelines.FieldOnPacketUserChat.Add(PipelinePriority.High, new FieldOnPacketUserChatCommandPlug(commandManager));
return Task.CompletedTask;

_ = Task.Run(async () =>
{
await Task.WhenAll((await commandManager.RetrieveAll())
.OfType<IIndexedCommand>()
.Select(async c =>
{
var stopwatch = new Stopwatch();
stopwatch.Start();
await c.Index();
host.Logger.LogDebug("Finished indexing for command {Command} in {Elapsed:F2}ms", c.Name, stopwatch.Elapsed.TotalMilliseconds);
}));
});
}

public Task OnStop()
Expand Down

0 comments on commit 655a949

Please sign in to comment.