Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lucene search in package manager #14098

Merged
merged 11 commits into from
Jun 27, 2023
26 changes: 25 additions & 1 deletion src/DynamoCore/Configuration/LuceneConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ internal class LuceneConfig
/// </summary>
internal static int FuzzySearchWeight = 2;

/// <summary>
/// Directory where Nodes info are indexed
/// </summary>
internal static string NodesIndexingDirectory = "Nodes";

/// <summary>
/// Directory where packages info are indexed
/// </summary>
internal static string PackagesIndexingDirectory = "Packages";

/// <summary>
/// This represent the fields that will be indexed when initializing Lucene Search
/// </summary>
Expand Down Expand Up @@ -107,7 +117,12 @@ public enum IndexFieldsEnum
/// <summary>
/// Documentation - Documentation of the node
/// </summary>
Documentation
Documentation,

/// <summary>
/// Hosts - Package hosts
/// </summary>
Hosts
}

/// <summary>
Expand All @@ -119,5 +134,14 @@ public enum IndexFieldsEnum
nameof(IndexFieldsEnum.SearchKeywords),
nameof(IndexFieldsEnum.DocName),
nameof(IndexFieldsEnum.Documentation)};


/// <summary>
/// Package Fields to be indexed by Lucene Search
/// </summary>
public static string[] PackageIndexFields = { nameof(IndexFieldsEnum.Name),
nameof(IndexFieldsEnum.Description),
nameof(IndexFieldsEnum.SearchKeywords),
nameof(IndexFieldsEnum.Hosts)};
}
}
2 changes: 1 addition & 1 deletion src/DynamoCore/Models/DynamoModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ private void InitializeLuceneConfig()
var userDataDir = new DirectoryInfo(pathManager.UserDataDirectory);
webBrowserUserDataFolder = userDataDir.Exists ? userDataDir : null;

string indexPath = Path.Combine(webBrowserUserDataFolder.FullName, "Index");
string indexPath = Path.Combine(webBrowserUserDataFolder.FullName, "Index", LuceneConfig.NodesIndexingDirectory);
indexDir = Lucene.Net.Store.FSDirectory.Open(indexPath);

// Create an analyzer to process the text
Expand Down
1 change: 1 addition & 0 deletions src/DynamoCoreWpf/DynamoCoreWpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
<Compile Include="Utilities\MessageBoxUtilities.cs" />
<Compile Include="Utilities\NodeContextMenuBuilder.cs" />
<Compile Include="Utilities\OnceDisposable.cs" />
<Compile Include="Utilities\LuceneSearchUtility.cs" />
<Compile Include="TestInfrastructure\ConnectorMutator.cs" />
<Compile Include="TestInfrastructure\MutationTestAttribute.cs" />
<Compile Include="Utilities\DelegateCommand.cs" />
Expand Down
100 changes: 100 additions & 0 deletions src/DynamoCoreWpf/Utilities/LuceneSearchUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Linq;
using Dynamo.Configuration;
using Dynamo.Models;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers.Classic;
using Lucene.Net.Search;

namespace Dynamo.Utilities
{
internal class LuceneSearchUtility
{
internal DynamoModel dynamoModel;

internal LuceneSearchUtility(DynamoModel model)
{
dynamoModel = model;
}

/// <summary>
/// Creates a search query with adjusted priority, fuzzy logic and wildcards.
/// Complete Search term appearing in Name of the package will be given highest priority.
/// Then, complete search term appearing in other metadata,
/// Then, a part of the search term(if containing multiple words) appearing in Name of the package
/// Then, a part of the search term appearing in other metadata of the package.
/// Then priority will be given based on fuzzy logic- that is if the complete search term may have been misspelled for upto 2(max edits) characters.
/// Then, the same fuzzy logic will be applied to each part of the search term.
/// </summary>
/// <param name="fields">All fields to be searched in.</param>
/// <param name="SearchTerm">Search key to be searched for.</param>
/// <returns></returns>
internal string CreateSearchQuery(string[] fields, string SearchTerm)
{
int fuzzyLogicMaxEdits = LuceneConfig.FuzzySearchMinEdits;
// Use a larger max edit value - more tolerant with typo when search term is longer than threshold
if (SearchTerm.Length > LuceneConfig.FuzzySearchMaxEditsThreshold)
{
fuzzyLogicMaxEdits = LuceneConfig.FuzzySearchMaxEdits;
}

var booleanQuery = new BooleanQuery();
string searchTerm = QueryParser.Escape(SearchTerm);

foreach (string f in fields)
{
FuzzyQuery fuzzyQuery;
if (searchTerm.Length > LuceneConfig.FuzzySearchMinimalTermLength)
{
fuzzyQuery = new FuzzyQuery(new Term(f, searchTerm), fuzzyLogicMaxEdits);
booleanQuery.Add(fuzzyQuery, Occur.SHOULD);
}

var wildcardQuery = new WildcardQuery(new Term(f, searchTerm));
if (f.Equals(nameof(LuceneConfig.IndexFieldsEnum.Name)))
{
wildcardQuery.Boost = LuceneConfig.SearchNameWeight;
}
else
{
wildcardQuery.Boost = LuceneConfig.SearchMetaFieldsWeight;
}
booleanQuery.Add(wildcardQuery, Occur.SHOULD);

wildcardQuery = new WildcardQuery(new Term(f, "*" + searchTerm + "*"));
if (f.Equals(nameof(LuceneConfig.IndexFieldsEnum.Name)))
{
wildcardQuery.Boost = LuceneConfig.WildcardsSearchNameWeight;
}
else
{
wildcardQuery.Boost = LuceneConfig.WildcardsSearchMetaFieldsWeight;
}
booleanQuery.Add(wildcardQuery, Occur.SHOULD);

if (searchTerm.Contains(' ') || searchTerm.Contains('.'))
{
foreach (string s in searchTerm.Split(' ', '.'))
{
if (s.Length > LuceneConfig.FuzzySearchMinimalTermLength)
{
fuzzyQuery = new FuzzyQuery(new Term(f, s), LuceneConfig.FuzzySearchMinEdits);
booleanQuery.Add(fuzzyQuery, Occur.SHOULD);
}
wildcardQuery = new WildcardQuery(new Term(f, "*" + s + "*"));

if (f.Equals(nameof(LuceneConfig.IndexFieldsEnum.Name)))
{
wildcardQuery.Boost = 5;
}
else
{
wildcardQuery.Boost = LuceneConfig.FuzzySearchWeight;
}
booleanQuery.Add(wildcardQuery, Occur.SHOULD);
}
}
}
return booleanQuery.ToString();
}
}
}
Loading