Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into live

  • Loading branch information...
commit 0d6b52311dfc0173357c54c2cd3725e7138966f6 2 parents b002008 + 2789792
Jeff Handley jeffhandley authored
BIN  .nuget/NuGet.exe
View
Binary file not shown
1  Facts/Facts.csproj
View
@@ -131,6 +131,7 @@
<Compile Include="Infrastructure\CookieTempDataProviderFacts.cs" />
<Compile Include="Infrastructure\Jobs\UpdateStatisticsJobFacts.cs" />
<Compile Include="Infrastructure\LuceneIndexingServiceFacts.cs" />
+ <Compile Include="PackageCurators\Windows8PackageCuratorFacts.cs" />
<Compile Include="PackageCurators\WebMatrixPackageCuratorFacts.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RequireRemoteHttpsAttributeFacts.cs" />
27 Facts/Infrastructure/LuceneIndexingServiceFacts.cs
View
@@ -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[] { "NHibernate" })]
+ [InlineData("NUnit", new[] { "NUnit" })]
+ [InlineData("EntityFramework", new[] { "EntityFramework", "Framework", "Entity" })]
+ [InlineData("Sys-netFX", new[] { "Sys-netFX", "Sys", "netFX" })]
+ [InlineData("xUnit", new[] { "xUnit" })]
+ [InlineData("jQueryUI", new [] { "jQueryUI" })]
+ [InlineData("jQuery-UI", new[] { "jQuery-UI", "jQuery", "UI" })]
+ [InlineData("NuGetPowerTools", new[] { "NuGetPowerTools", "NuGet", "Power", "Tools" } )]
+ [InlineData("microsoft-web-helpers", new[] { "microsoft-web-helpers", "microsoft", "web", "helpers" } )]
+ [InlineData("EntityFramework.sample", new[] { "EntityFramework.sample", "EntityFramework", "sample", "Framework", "Entity" })]
+ [InlineData("SignalR.MicroSliver", new[] { "SignalR.MicroSliver", "SignalR", "MicroSliver", "Micro", "Sliver" })]
+ [InlineData("ABCMicroFramework", new[] { "ABCMicroFramework", "ABC", "Micro", "Framework" })]
+ [InlineData("SignalR.Hosting.AspNet", new[] { "SignalR.Hosting.AspNet", "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]
199 Facts/PackageCurators/Windows8PackageCuratorFacts.cs
View
@@ -0,0 +1,199 @@
+using System;
+using Moq;
+using NuGet;
+using Xunit;
+using Xunit.Extensions;
+
+namespace NuGetGallery.PackageCurators
+{
+ public class Windows8PackageCuratorFacts
+ {
+ public class TheCurateMethod
+ {
+ [Fact]
+ public void WillNotIncludeThePackageWhenTheWindows8CuratedFeedDoesNotExist()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ curator.StubCuratedFeedByNameQry.Setup(stub => stub.Execute(It.IsAny<string>(), It.IsAny<bool>())).Returns((CuratedFeed)null);
+ var package = CreateStubGalleryPackage();
+ package.Tags = "winrt";
+
+ curator.Curate(package, null);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()), Times.Never());
+ }
+
+ [Theory]
+ [InlineData("winrt")]
+ [InlineData("win8")]
+ [InlineData("windows8")]
+ [InlineData("winjs")]
+ [InlineData("wInRt")]
+ [InlineData("wIn8")]
+ [InlineData("wInDows8")]
+ [InlineData("wInJs")]
+ public void WillIncludeThePackageWhenItHasAcceptedTag(string tag)
+ {
+ var curator = new TestableWindows8PackageCurator();
+ var stubGalleryPackage = CreateStubGalleryPackage();
+ stubGalleryPackage.Tags = "aTag " + tag + " aThirdTag";
+
+ curator.Curate(stubGalleryPackage, null);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()), Times.Once());
+ }
+
+ [Fact]
+ public void WillNotIncludeThePackageWhenNotTagged()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ var stubGalleryPackage = CreateStubGalleryPackage();
+ stubGalleryPackage.Tags = "aTag notforwinrt aThirdTag";
+
+ curator.Curate(stubGalleryPackage, null);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()), Times.Never());
+ }
+
+ [Fact]
+ public void WillNotIncludeThePackageWhenTagsIsNull()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ var stubGalleryPackage = CreateStubGalleryPackage();
+ stubGalleryPackage.Tags = null;
+
+ curator.Curate(stubGalleryPackage, null);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()), Times.Never());
+ }
+
+ [Fact]
+ public void WillIncludeThePackageUsingTheCuratedFeedKey()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ curator.StubCuratedFeed.Key = 42;
+ var package = CreateStubGalleryPackage();
+ package.Tags = "winrt";
+
+ curator.Curate(package, CreateStubNuGetPackage().Object);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ 42,
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()));
+ }
+
+ [Fact]
+ public void WillIncludeThePackageUsingThePackageRegistrationKey()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ var stubGalleryPackage = CreateStubGalleryPackage();
+ stubGalleryPackage.PackageRegistration.Key = 42;
+ stubGalleryPackage.Tags = "winrt";
+
+ curator.Curate(stubGalleryPackage, CreateStubNuGetPackage().Object);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ 42,
+ It.IsAny<bool>(),
+ It.IsAny<bool>(),
+ It.IsAny<string>()));
+ }
+
+ [Fact]
+ public void WillSetTheAutomaticBitWhenIncludingThePackage()
+ {
+ var curator = new TestableWindows8PackageCurator();
+ var stubGalleryPackage = CreateStubGalleryPackage();
+ stubGalleryPackage.Tags = "winrt";
+
+ curator.Curate(stubGalleryPackage, CreateStubNuGetPackage().Object);
+
+ curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
+ It.IsAny<int>(),
+ It.IsAny<int>(),
+ It.IsAny<bool>(),
+ true,
+ It.IsAny<string>()));
+ }
+
+ static Package CreateStubGalleryPackage()
+ {
+ return new Package
+ {
+ IsLatestStable = true ,
+ PackageRegistration = new PackageRegistration
+ {
+ Key = 0,
+ },
+ };
+ }
+
+ static Mock<IPackage> CreateStubNuGetPackage()
+ {
+ var stubNuGetPackage = new Mock<IPackage>();
+ stubNuGetPackage.Setup(stub => stub.GetFiles()).Returns(new IPackageFile[] {});
+ return stubNuGetPackage;
+ }
+
+ static Mock<IPackageFile> CreateStubNuGetPackageFile(string path)
+ {
+ var stubPackageFile = new Mock<IPackageFile>();
+ stubPackageFile.Setup(stub => stub.Path).Returns(path);
+ return stubPackageFile;
+ }
+ }
+
+ public class TestableWindows8PackageCurator : Windows8PackageCurator
+ {
+ public TestableWindows8PackageCurator()
+ {
+ StubCreatedCuratedPackageCmd = new Mock<ICreateCuratedPackageCommand>();
+ StubCuratedFeed = new CuratedFeed { Key = 0 };
+ StubCuratedFeedByNameQry = new Mock<ICuratedFeedByNameQuery>();
+
+ StubCuratedFeedByNameQry
+ .Setup(stub => stub.Execute(It.IsAny<string>(), It.IsAny<bool>()))
+ .Returns(StubCuratedFeed);
+ }
+
+ public Mock<ICreateCuratedPackageCommand> StubCreatedCuratedPackageCmd { get; set; }
+ public CuratedFeed StubCuratedFeed { get; private set; }
+ public Mock<ICuratedFeedByNameQuery> StubCuratedFeedByNameQry { get; private set; }
+
+ protected override T GetService<T>()
+ {
+ if (typeof(T) == typeof(ICreateCuratedPackageCommand))
+ return (T)StubCreatedCuratedPackageCmd.Object;
+
+ if (typeof(T) == typeof(ICuratedFeedByNameQuery))
+ return (T)StubCuratedFeedByNameQry.Object;
+
+ throw new Exception("Tried to get an unexpected service.");
+ }
+ }
+ }
+}
2  Website/App_Start/ContainerBindings.cs
View
@@ -173,6 +173,8 @@ public override void Load()
// todo: bind all package curators by convention
Bind<IAutomaticPackageCurator>()
.To<WebMatrixPackageCurator>();
+ Bind<IAutomaticPackageCurator>()
+ .To<Windows8PackageCurator>();
// todo: bind all commands by convention
Bind<IAutomaticallyCuratePackageCommand>()
10 Website/Controllers/PackagesController.cs
View
@@ -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.
205 Website/Infrastructure/Elmah.SqlServer.sql
View
@@ -0,0 +1,205 @@
+SET ANSI_NULLS ON
+SET QUOTED_IDENTIFIER ON
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_Error]') AND type in (N'U'))
+BEGIN
+CREATE TABLE [dbo].[ELMAH_Error](
+ [ErrorId] [uniqueidentifier] NOT NULL,
+ [Application] [nvarchar](60) NOT NULL,
+ [Host] [nvarchar](50) NOT NULL,
+ [Type] [nvarchar](100) NOT NULL,
+ [Source] [nvarchar](60) NOT NULL,
+ [Message] [nvarchar](500) NOT NULL,
+ [User] [nvarchar](50) NOT NULL,
+ [StatusCode] [int] NOT NULL,
+ [TimeUtc] [datetime] NOT NULL,
+ [Sequence] [int] IDENTITY(1,1) NOT NULL,
+ [AllXml] [nvarchar](max) NOT NULL,
+ CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY CLUSTERED
+(
+ [ErrorId] ASC
+)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
+)
+END
+
+IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_Error]') AND name = N'IX_ELMAH_Error_App_Time_Seq')
+CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error]
+(
+ [Application] ASC,
+ [TimeUtc] DESC,
+ [Sequence] DESC
+)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF)
+GO
+IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_ELMAH_Error_ErrorId]') AND type = 'D')
+BEGIN
+ALTER TABLE [dbo].[ELMAH_Error] ADD CONSTRAINT [DF_ELMAH_Error_ErrorId] DEFAULT (newid()) FOR [ErrorId]
+END
+
+GO
+SET ANSI_NULLS ON
+SET QUOTED_IDENTIFIER ON
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorsXml]') AND type in (N'P', N'PC'))
+BEGIN
+EXEC dbo.sp_executesql @statement = N'
+CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
+(
+ @Application NVARCHAR(60),
+ @PageIndex INT = 0,
+ @PageSize INT = 15,
+ @TotalCount INT OUTPUT
+)
+AS
+
+ SET NOCOUNT ON
+
+ DECLARE @FirstTimeUTC DATETIME
+ DECLARE @FirstSequence INT
+ DECLARE @StartRow INT
+ DECLARE @StartRowIndex INT
+
+ SELECT
+ @TotalCount = COUNT(1)
+ FROM
+ [ELMAH_Error]
+ WHERE
+ [Application] = @Application
+
+ -- Get the ID of the first error for the requested page
+
+ SET @StartRowIndex = @PageIndex * @PageSize + 1
+
+ IF @StartRowIndex <= @TotalCount
+ BEGIN
+
+ SET ROWCOUNT @StartRowIndex
+
+ SELECT
+ @FirstTimeUTC = [TimeUtc],
+ @FirstSequence = [Sequence]
+ FROM
+ [ELMAH_Error]
+ WHERE
+ [Application] = @Application
+ ORDER BY
+ [TimeUtc] DESC,
+ [Sequence] DESC
+
+ END
+ ELSE
+ BEGIN
+
+ SET @PageSize = 0
+
+ END
+
+ -- Now set the row count to the requested page size and get
+ -- all records below it for the pertaining application.
+
+ SET ROWCOUNT @PageSize
+
+ SELECT
+ errorId = [ErrorId],
+ application = [Application],
+ host = [Host],
+ type = [Type],
+ source = [Source],
+ message = [Message],
+ [user] = [User],
+ statusCode = [StatusCode],
+ time = CONVERT(VARCHAR(50), [TimeUtc], 126) + ''Z''
+ FROM
+ [ELMAH_Error] error
+ WHERE
+ [Application] = @Application
+ AND
+ [TimeUtc] <= @FirstTimeUTC
+ AND
+ [Sequence] <= @FirstSequence
+ ORDER BY
+ [TimeUtc] DESC,
+ [Sequence] DESC
+ FOR
+ XML AUTO
+
+'
+END
+GO
+SET ANSI_NULLS ON
+SET QUOTED_IDENTIFIER ON
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorXml]') AND type in (N'P', N'PC'))
+BEGIN
+EXEC dbo.sp_executesql @statement = N'
+CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
+(
+ @Application NVARCHAR(60),
+ @ErrorId UNIQUEIDENTIFIER
+)
+AS
+
+ SET NOCOUNT ON
+
+ SELECT
+ [AllXml]
+ FROM
+ [ELMAH_Error]
+ WHERE
+ [ErrorId] = @ErrorId
+ AND
+ [Application] = @Application
+
+'
+END
+GO
+SET ANSI_NULLS ON
+SET QUOTED_IDENTIFIER ON
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_LogError]') AND type in (N'P', N'PC'))
+BEGIN
+EXEC dbo.sp_executesql @statement = N'
+CREATE PROCEDURE [dbo].[ELMAH_LogError]
+(
+ @ErrorId UNIQUEIDENTIFIER,
+ @Application NVARCHAR(60),
+ @Host NVARCHAR(30),
+ @Type NVARCHAR(100),
+ @Source NVARCHAR(60),
+ @Message NVARCHAR(500),
+ @User NVARCHAR(50),
+ @AllXml NVARCHAR(MAX),
+ @StatusCode INT,
+ @TimeUtc DATETIME
+)
+AS
+
+ SET NOCOUNT ON
+
+ INSERT
+ INTO
+ [ELMAH_Error]
+ (
+ [ErrorId],
+ [Application],
+ [Host],
+ [Type],
+ [Source],
+ [Message],
+ [User],
+ [AllXml],
+ [StatusCode],
+ [TimeUtc]
+ )
+ VALUES
+ (
+ @ErrorId,
+ @Application,
+ @Host,
+ @Type,
+ @Source,
+ @Message,
+ @User,
+ @AllXml,
+ @StatusCode,
+ @TimeUtc
+ )
+
+'
+END
+GO
56 Website/Infrastructure/Lucene/LuceneIndexingService.cs
View
@@ -89,19 +89,25 @@ private static void AddPackages(IndexWriter indexWriter, List<PackageIndexEntity
var document = new Document();
document.Add(new Field("Key", package.Key.ToString(CultureInfo.InvariantCulture), Field.Store.YES, Field.Index.NO));
- document.Add(new Field("Id-Exact", package.Id, Field.Store.NO, Field.Index.ANALYZED));
+ document.Add(new Field("Id-Exact", package.Id.ToLowerInvariant(), Field.Store.NO, Field.Index.NOT_ANALYZED));
document.Add(new Field("Description", package.Description, Field.Store.NO, Field.Index.ANALYZED));
- foreach (var idToken in TokenizeId(package.Id))
+ var tokenizedId = TokenizeId(package.Id);
+ foreach (var idToken in tokenizedId)
{
document.Add(new Field("Id", idToken, Field.Store.NO, Field.Index.ANALYZED));
}
- if (!String.IsNullOrEmpty(package.Title))
+ // If an element does not have a Title, then add all the tokenized Id components as Title.
+ // Lucene's StandardTokenizer does not tokenize items of the format a.b.c which does not play well with things like "xunit.net".
+ // We will feed it values that are already tokenized.
+ var titleTokens = String.IsNullOrEmpty(package.Title) ? tokenizedId : package.Title.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var idToken in titleTokens)
{
- document.Add(new Field("Title", package.Title, Field.Store.NO, Field.Index.ANALYZED));
+ document.Add(new Field("Title", idToken, Field.Store.NO, Field.Index.ANALYZED));
}
+
if (!String.IsNullOrEmpty(package.Tags))
{
document.Add(new Field("Tags", package.Tags, Field.Store.NO, Field.Index.ANALYZED));
@@ -171,35 +177,45 @@ protected internal virtual void UpdateLastWriteTime()
internal static IEnumerable<string> TokenizeId(string term)
{
- var result = CamelCaseTokenize(term).SelectMany(s => s.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries)).ToList();
- if (result.Count == 1)
- {
- return Enumerable.Empty<string>();
- }
+
+ // 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.
+ var result = tokens.Concat(new[] { term })
+ .Concat(tokens.SelectMany(CamelCaseTokenize))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
return result;
}
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;
}
}
}
32 Website/Infrastructure/Lucene/LuceneSearchService.cs
View
@@ -4,9 +4,10 @@
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;
+using Lucene.Net.Search.Function;
namespace NuGetGallery
{
@@ -86,25 +87,29 @@ 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.1f },
+ { "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(2.0f);
var disjunctionQuery = new BooleanQuery();
+ disjunctionQuery.SetBoost(0.1f);
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);
+ var wildCardIdQuery = new WildcardQuery(new Term("Id-Exact", "*" + searchTerm + "*"));
- foreach(var term in searchTerm.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
+ foreach(var term in GetSearchTerms(searchTerm))
{
- conjuctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.MUST);
- disjunctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.SHOULD);
-
+ var termQuery = queryParser.Parse(term);
+ conjuctionQuery.Add(termQuery, BooleanClause.Occur.MUST);
+ disjunctionQuery.Add(termQuery, BooleanClause.Occur.SHOULD);
+
foreach (var field in fields)
{
var wildCardTermQuery = new WildcardQuery(new Term(field.Key, term + "*"));
@@ -113,7 +118,16 @@ private static Query ParseQuery(string searchTerm)
}
}
- return conjuctionQuery.Combine(new Query[] { exactIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery });
+ var downloadCountBooster = new FieldScoreQuery("DownloadCount", FieldScoreQuery.Type.INT);
+ return new CustomScoreQuery(conjuctionQuery.Combine(new Query[] { exactIdQuery, wildCardIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery }),
+ downloadCountBooster);
+ }
+
+ private static IEnumerable<string> GetSearchTerms(string searchTerm)
+ {
+ return searchTerm.Split(new[] { ' ', '.', '-' }, StringSplitOptions.RemoveEmptyEntries)
+ .Concat(new[] { searchTerm })
+ .Distinct(StringComparer.OrdinalIgnoreCase);
}
}
}
24 Website/Migrations/201206250141447_ExecuteELMAHSql.Designer.cs
View
@@ -0,0 +1,24 @@
+// <auto-generated />
+namespace NuGetGallery.Migrations
+{
+ using System.Data.Entity.Migrations;
+ using System.Data.Entity.Migrations.Infrastructure;
+
+ public sealed partial class ExecuteELMAHSql : IMigrationMetadata
+ {
+ string IMigrationMetadata.Id
+ {
+ get { return "201206250141447_ExecuteELMAHSql"; }
+ }
+
+ string IMigrationMetadata.Source
+ {
+ get { return null; }
+ }
+
+ string IMigrationMetadata.Target
+ {
+ get { return "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"; }
+ }
+ }
+}
36 Website/Migrations/201206250141447_ExecuteELMAHSql.cs
View
@@ -0,0 +1,36 @@
+using System;
+using System.Data.Entity.Migrations;
+using System.IO;
+
+namespace NuGetGallery.Migrations
+{
+ public partial class ExecuteELMAHSql : DbMigration
+ {
+ private static readonly string[] Go = new[] { "GO" };
+
+ public override void Up()
+ {
+ using (var stream = typeof(ExecuteELMAHSql).Assembly.GetManifestResourceStream("NuGetGallery.Infrastructure.Elmah.SqlServer.sql"))
+ {
+ using (var streamReader = new StreamReader(stream))
+ {
+ var statements = streamReader.ReadToEnd().Split(Go, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var statement in statements)
+ {
+ if (String.IsNullOrWhiteSpace(statement))
+ {
+ continue;
+ }
+
+ Sql(statement);
+ }
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
41 Website/PackageCurators/TagBasedPackageCurator.cs
View
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using NuGet;
+
+namespace NuGetGallery
+{
+ public abstract class TagBasedPackageCurator : AutomaticPackageCurator
+ {
+ /// <summary>
+ /// Gets a list of tags required for a package to be selected by this curator. A package MUST have ONE of the specified tags to be curated.
+ /// </summary>
+ protected abstract IEnumerable<string> RequiredTags { get; }
+
+ /// <summary>
+ /// Gets the name of the curated feed to add the package to.
+ /// </summary>
+ protected abstract string CuratedFeedName { get; }
+
+ public override void Curate(Package galleryPackage, IPackage nugetPackage)
+ {
+ // Make sure the target feed exists
+ CuratedFeed feed = GetService<ICuratedFeedByNameQuery>().Execute(CuratedFeedName, includePackages: false);
+ if (feed != null && galleryPackage.Tags != null)
+ {
+ // Break the tags up so we can be sure we don't catch any partial matches (i.e. "foobar" when we're looking for "foo")
+ string[] tags = galleryPackage.Tags.Split();
+
+ // Check if this package should be curated
+ if (tags.Any(tag => RequiredTags.Contains(tag, StringComparer.OrdinalIgnoreCase)))
+ {
+ // It should! Add it to the curated feed
+ GetService<ICreateCuratedPackageCommand>().Execute(
+ feed.Key, galleryPackage.PackageRegistration.Key, automaticallyCurated: true);
+ }
+
+ }
+ }
+ }
+}
26 Website/PackageCurators/Windows8PackageCurator.cs
View
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace NuGetGallery
+{
+ public class Windows8PackageCurator : TagBasedPackageCurator
+ {
+ protected override IEnumerable<string> RequiredTags
+ {
+ get
+ {
+ yield return "winrt";
+ yield return "win8";
+ yield return "windows8";
+ yield return "winjs";
+ }
+ }
+
+ protected override string CuratedFeedName
+ {
+ get { return "windows8-packages"; }
+ }
+ }
+}
2  Website/Scripts/stats.js
View
@@ -45,7 +45,7 @@ function update(data, key) {
function animateEl(el, v) {
v = v || '';
- var parent = e.parent();
+ var parent = el.parent();
el.animate({ top: 0.3 * parseInt(parent.height()) }, 350, 'linear', function () {
$(this).html(v).css({ top: -0.8 * parseInt(parent.height()) }).animate({ top: 0 }, 350, 'linear')
});
10 Website/Services/AggregateStatsService.cs
View
@@ -24,12 +24,14 @@ public AggregateStats GetAggregateStats()
database.Connection.Open();
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection | CommandBehavior.SingleRow))
{
- reader.Read();
+ bool hasData = reader.Read();
+ if (!hasData)
+ return new AggregateStats();
return new AggregateStats
{
- UniquePackages = reader.GetInt32(0),
- TotalPackages = reader.GetInt32(1),
- Downloads = reader.GetInt32(2),
+ UniquePackages = reader.IsDBNull(0) ? 0 : reader.GetInt32(0),
+ TotalPackages = reader.IsDBNull(1) ? 0 : reader.GetInt32(1),
+ Downloads = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
};
}
}
5 Website/Views/Packages/ListPackages.cshtml
View
@@ -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>
13 Website/Website.csproj
View
@@ -26,6 +26,10 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
+ <IISExpressSSLPort />
+ <IISExpressAnonymousAuthentication />
+ <IISExpressWindowsAuthentication />
+ <IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -346,10 +350,16 @@
<Compile Include="Migrations\201206131919241_AddTargetFxToDependencies.Designer.cs">
<DependentUpon>201206131919241_AddTargetFxToDependencies.cs</DependentUpon>
</Compile>
+ <Compile Include="Migrations\201206250141447_ExecuteELMAHSql.cs" />
+ <Compile Include="Migrations\201206250141447_ExecuteELMAHSql.Designer.cs">
+ <DependentUpon>201206250141447_ExecuteELMAHSql.cs</DependentUpon>
+ </Compile>
<Compile Include="Migrations\MigrationsConfiguration.cs" />
<Compile Include="PackageCurators\IAutomaticPackageCurator.cs" />
<Compile Include="PackageCurators\AutomaticPackageCurator.cs" />
+ <Compile Include="PackageCurators\TagBasedPackageCurator.cs" />
<Compile Include="PackageCurators\WebMatrixPackageCurator.cs" />
+ <Compile Include="PackageCurators\Windows8PackageCurator.cs" />
<Compile Include="PackagesController.generated.cs">
<DependentUpon>T4MVC.tt</DependentUpon>
</Compile>
@@ -787,6 +797,7 @@
<Content Include="DynamicData\Site.css" />
<Content Include="Errors\Error.html" />
<Content Include="favicon.ico" />
+ <EmbeddedResource Include="Infrastructure\Elmah.SqlServer.sql" />
<Content Include="Scripts\stats.js" />
<Content Include="Scripts\jquery-1.6.2-vsdoc.js" />
<Content Include="Scripts\jquery-1.6.2.js" />
@@ -918,4 +929,4 @@
</VisualStudio>
</ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
-</Project>
+</Project>
Please sign in to comment.
Something went wrong with that request. Please try again.