Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Changing default sort order to relevance

  • Loading branch information...
commit fc0fcf44535566eb8015915aca41f45cdf7da359 1 parent 01da197
@pranavkm pranavkm authored
View
27 Facts/Infrastructure/LuceneIndexingServiceFacts.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
+using System.Linq;
using Moq;
using Xunit;
using Xunit.Extensions;
@@ -10,24 +11,26 @@ namespace NuGetGallery.Infrastructure
public class LuceneIndexingServiceFacts
{
[Theory]
- [InlineData(new object[] { "NHibernate", new string[0] })]
- [InlineData(new object[] { "NUnit", new string[0] })]
- [InlineData(new object[] { "SisoDb", new[] { "Siso", "Db" } })]
- [InlineData(new object[] { "EntityFramework", new[] { "Entity", "Framework" } })]
- [InlineData(new object[] { "Sys-netFX", new[] { "Sys", "net", "FX" } })]
- [InlineData(new object[] { "xUnit", new string[0] })]
- [InlineData(new object[] { "jQueryUI", new[] { "jQuery", "UI" } })]
- [InlineData(new object[] { "jQuery-UI", new[] { "jQuery", "UI" } })]
- [InlineData(new object[] { "NuGetPowerTools", new[] { "NuGet", "Power", "Tools" } })]
- [InlineData(new object[] { "microsoft-web-helpers", new[] { "microsoft", "web", "helpers" } })]
- [InlineData(new object[] { "EntityFramework.sample", new[] { "Entity", "Framework", "sample" } })]
+ [InlineData("NHibernate", new string[0])]
+ [InlineData("NUnit", new string[0])]
+ [InlineData("EntityFramework", new[] { "Framework", "Entity" })]
+ [InlineData("Sys-netFX", new[] { "Sys", "netFX" })]
+ [InlineData("xUnit", new string[0])]
+ [InlineData("jQueryUI", new string[0])]
+ [InlineData("jQuery-UI", new[] { "jQuery", "UI" })]
+ [InlineData("NuGetPowerTools", new[] { "NuGet", "Power", "Tools" } )]
+ [InlineData("microsoft-web-helpers", new[] { "microsoft", "web", "helpers" } )]
+ [InlineData("EntityFramework.sample", new[] { "EntityFramework", "sample", "Framework", "Entity" })]
+ [InlineData("SignalR.MicroSliver", new[] { "SignalR", "MicroSliver", "Micro", "Sliver" })]
+ [InlineData("ABCMicroFramework", new[] { "ABC", "Micro", "Framework" })]
+ [InlineData("SignalR.Hosting.AspNet", new[] { "SignalR", "Hosting", "AspNet", "Asp", "Net"})]
public void CamelCaseTokenizer(string term, IEnumerable<string> tokens)
{
// Act
var result = LuceneIndexingService.TokenizeId(term);
// Assert
- Assert.Equal(tokens, result);
+ Assert.Equal(tokens.OrderBy(p => p), result.OrderBy(p => p));
}
[Fact]
View
10 Website/Controllers/PackagesController.cs
@@ -129,17 +129,25 @@ public virtual ActionResult DisplayPackage(string id, string version)
return View(model);
}
- public virtual ActionResult ListPackages(string q, string sortOrder = Constants.PopularitySortOrder, int page = 1)
+ public virtual ActionResult ListPackages(string q, string sortOrder = null, int page = 1)
{
if (page < 1)
{
page = 1;
}
+
IQueryable<Package> packageVersions = packageSvc.GetLatestPackageVersions(allowPrerelease: true);
q = (q ?? "").Trim();
+ if (String.IsNullOrEmpty(sortOrder))
+ {
+ // Determine the default sort order. If no query string is specified, then the sortOrder is DownloadCount
+ // If we are searching for something, sort by relevance.
+ sortOrder = q.IsEmpty() ? Constants.PopularitySortOrder : Constants.RelevanceSortOrder;
+ }
+
if (GetIdentity().IsAuthenticated)
{
// Only show listed packages. For unlisted packages, only show them if the owner is viewing it.
View
39 Website/Infrastructure/Lucene/LuceneIndexingService.cs
@@ -171,7 +171,16 @@ protected internal virtual void UpdateLastWriteTime()
internal static IEnumerable<string> TokenizeId(string term)
{
- var result = CamelCaseTokenize(term).SelectMany(s => s.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries)).ToList();
+
+ // First tokenize the result by id-separators. For e.g. tokenize SignalR.EventStream as SignalR and EventStream
+ var tokens = term.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries);
+
+ // For each token, further attempt to tokenize camelcase values. e.g. .EventStream -> Event, Stream.
+ // Skip the exact term since we index it indep
+ var result = tokens.Concat(tokens.SelectMany(CamelCaseTokenize))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Where(t => !term.Equals(t))
+ .ToList();
if (result.Count == 1)
{
return Enumerable.Empty<string>();
@@ -181,25 +190,31 @@ internal static IEnumerable<string> TokenizeId(string term)
private static IEnumerable<string> CamelCaseTokenize(string term)
{
- if (term.Length < 2)
+ const int MinTokenLength = 3;
+ if (term.Length < MinTokenLength)
{
yield break;
}
- int tokenStart = 0;
- for (int i = 1; i < term.Length; i++)
+ int tokenEnd = term.Length;
+ for (int i = term.Length - 1; i > 0; i--)
{
- if (Char.IsUpper(term[i]) && (i - tokenStart > 2))
+ // If the remainder is fewer than 2 chars or we have a token that is at least 2 chars long, tokenize it.
+ if (i < MinTokenLength || (Char.IsUpper(term[i]) && (tokenEnd - i >= MinTokenLength)))
{
- yield return term.Substring(tokenStart, i - tokenStart);
- tokenStart = i;
+ if (i < MinTokenLength)
+ {
+ // If the remainder is smaller than 2 chars, just return the entire string
+ i = 0;
+ }
+
+ yield return term.Substring(i, tokenEnd - i);
+ tokenEnd = i;
}
}
- if (term.Length - tokenStart < 2)
- {
- yield break;
- }
- yield return term.Substring(tokenStart);
+
+ // Finally return the term in entirety
+ yield return term;
}
}
}
View
19 Website/Infrastructure/Lucene/LuceneSearchService.cs
@@ -4,9 +4,9 @@
using System.IO;
using System.Linq;
using Lucene.Net.Analysis.Standard;
+using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
-using Lucene.Net.Index;
namespace NuGetGallery
{
@@ -86,25 +86,28 @@ private static IEnumerable<int> SearchCore(string searchTerm)
private static Query ParseQuery(string searchTerm)
{
- var fields = new Dictionary<string, float> { { "Id", 1.2f }, { "Title", 1.0f }, { "Tags", 1.0f}, { "Description", 0.8f }, { "Author", 0.6f } };
+ var fields = new Dictionary<string, float> { { "Id", 1.2f }, { "Title", 1.0f }, { "Tags", 0.8f }, { "Description", 0.3f },
+ { "Author", 1.0f } };
var analyzer = new StandardAnalyzer(LuceneCommon.LuceneVersion);
searchTerm = QueryParser.Escape(searchTerm).ToLowerInvariant();
var queryParser = new MultiFieldQueryParser(LuceneCommon.LuceneVersion, fields.Keys.ToArray(), analyzer, fields);
var conjuctionQuery = new BooleanQuery();
- conjuctionQuery.SetBoost(1.5f);
+ conjuctionQuery.SetBoost(1.2f);
var disjunctionQuery = new BooleanQuery();
+ disjunctionQuery.SetBoost(0.3f);
var wildCardQuery = new BooleanQuery();
- wildCardQuery.SetBoost(0.7f);
+ wildCardQuery.SetBoost(0.5f);
var exactIdQuery = new TermQuery(new Term("Id-Exact", searchTerm));
exactIdQuery.SetBoost(2.5f);
-
- foreach(var term in searchTerm.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
+ var wildCardIdQuery = new WildcardQuery(new Term("Id-Exact", "*" + searchTerm + "*"));
+
+ foreach (var term in searchTerm.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
{
conjuctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.MUST);
disjunctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.SHOULD);
-
+
foreach (var field in fields)
{
var wildCardTermQuery = new WildcardQuery(new Term(field.Key, term + "*"));
@@ -113,7 +116,7 @@ private static Query ParseQuery(string searchTerm)
}
}
- return conjuctionQuery.Combine(new Query[] { exactIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery });
+ return conjuctionQuery.Combine(new Query[] { exactIdQuery, wildCardIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery });
}
}
}
View
5 Website/Views/Packages/ListPackages.cshtml
@@ -22,11 +22,14 @@
<fieldset class="form search">
<legend>Sort Order</legend>
<input type="hidden" name="q" value="@Model.SearchTerm" />
+
<div class="form-field">
<label for="sortOrder">Sort By</label>
<select name="sortOrder" id="sortOrder">
+ @if (!Model.SearchTerm.IsEmpty()) {
+ @ViewHelpers.Option(Constants.RelevanceSortOrder, "Relevance", Model.SortOrder)
+ }
@ViewHelpers.Option(Constants.PopularitySortOrder, "Popularity", Model.SortOrder)
- @ViewHelpers.Option(Constants.RelevanceSortOrder, "Relevance", Model.SortOrder)
@ViewHelpers.Option(Constants.AlphabeticSortOrder, "A-Z", Model.SortOrder)
@ViewHelpers.Option(Constants.RecentSortOrder, "Recent", Model.SortOrder)
</select>
Please sign in to comment.
Something went wrong with that request. Please try again.