Skip to content
This repository
Browse code

Merge branch 'master' into live

  • Loading branch information...
commit 0d6b52311dfc0173357c54c2cd3725e7138966f6 2 parents b002008 + 2789792
Jeff Handley authored July 02, 2012
BIN  .nuget/NuGet.exe
Binary file not shown
1  Facts/Facts.csproj
@@ -131,6 +131,7 @@
131 131
     <Compile Include="Infrastructure\CookieTempDataProviderFacts.cs" />
132 132
     <Compile Include="Infrastructure\Jobs\UpdateStatisticsJobFacts.cs" />
133 133
     <Compile Include="Infrastructure\LuceneIndexingServiceFacts.cs" />
  134
+    <Compile Include="PackageCurators\Windows8PackageCuratorFacts.cs" />
134 135
     <Compile Include="PackageCurators\WebMatrixPackageCuratorFacts.cs" />
135 136
     <Compile Include="Properties\AssemblyInfo.cs" />
136 137
     <Compile Include="RequireRemoteHttpsAttributeFacts.cs" />
27  Facts/Infrastructure/LuceneIndexingServiceFacts.cs
... ...
@@ -1,6 +1,7 @@
1 1
 using System;
2 2
 using System.Collections.Generic;
3 3
 using System.Data.Entity;
  4
+using System.Linq;
4 5
 using Moq;
5 6
 using Xunit;
6 7
 using Xunit.Extensions;
@@ -10,24 +11,26 @@ namespace NuGetGallery.Infrastructure
10 11
     public class LuceneIndexingServiceFacts
11 12
     {
12 13
         [Theory]
13  
-        [InlineData(new object[] { "NHibernate", new string[0] })]
14  
-        [InlineData(new object[] { "NUnit", new string[0] })]
15  
-        [InlineData(new object[] { "SisoDb", new[] { "Siso", "Db" } })]
16  
-        [InlineData(new object[] { "EntityFramework", new[] { "Entity", "Framework" } })]
17  
-        [InlineData(new object[] { "Sys-netFX", new[] { "Sys", "net", "FX" } })]
18  
-        [InlineData(new object[] { "xUnit", new string[0] })]
19  
-        [InlineData(new object[] { "jQueryUI", new[] { "jQuery", "UI" } })]
20  
-        [InlineData(new object[] { "jQuery-UI", new[] { "jQuery", "UI" } })]
21  
-        [InlineData(new object[] { "NuGetPowerTools", new[] { "NuGet", "Power", "Tools" } })]
22  
-        [InlineData(new object[] { "microsoft-web-helpers", new[] { "microsoft", "web", "helpers" } })]
23  
-        [InlineData(new object[] { "EntityFramework.sample", new[] { "Entity", "Framework", "sample" } })]
  14
+        [InlineData("NHibernate", new[] { "NHibernate" })]
  15
+        [InlineData("NUnit", new[] { "NUnit" })]
  16
+        [InlineData("EntityFramework", new[] { "EntityFramework", "Framework", "Entity" })]
  17
+        [InlineData("Sys-netFX", new[] { "Sys-netFX", "Sys", "netFX" })]
  18
+        [InlineData("xUnit", new[] { "xUnit" })]
  19
+        [InlineData("jQueryUI", new [] { "jQueryUI" })]
  20
+        [InlineData("jQuery-UI", new[] { "jQuery-UI", "jQuery", "UI" })]
  21
+        [InlineData("NuGetPowerTools", new[] { "NuGetPowerTools", "NuGet", "Power", "Tools" } )]
  22
+        [InlineData("microsoft-web-helpers", new[] { "microsoft-web-helpers", "microsoft", "web", "helpers" } )]
  23
+        [InlineData("EntityFramework.sample", new[] { "EntityFramework.sample", "EntityFramework", "sample", "Framework", "Entity" })]
  24
+        [InlineData("SignalR.MicroSliver", new[] { "SignalR.MicroSliver", "SignalR", "MicroSliver", "Micro", "Sliver" })]
  25
+        [InlineData("ABCMicroFramework", new[] { "ABCMicroFramework", "ABC", "Micro", "Framework" })]
  26
+        [InlineData("SignalR.Hosting.AspNet", new[] { "SignalR.Hosting.AspNet", "SignalR", "Hosting", "AspNet", "Asp", "Net"})] 
24 27
         public void CamelCaseTokenizer(string term, IEnumerable<string> tokens)
25 28
         {
26 29
             // Act
27 30
             var result = LuceneIndexingService.TokenizeId(term);
28 31
 
29 32
             // Assert
30  
-            Assert.Equal(tokens, result);
  33
+            Assert.Equal(tokens.OrderBy(p => p), result.OrderBy(p => p));
31 34
         }
32 35
 
33 36
         [Fact]
199  Facts/PackageCurators/Windows8PackageCuratorFacts.cs
... ...
@@ -0,0 +1,199 @@
  1
+using System;
  2
+using Moq;
  3
+using NuGet;
  4
+using Xunit;
  5
+using Xunit.Extensions;
  6
+
  7
+namespace NuGetGallery.PackageCurators
  8
+{
  9
+    public class Windows8PackageCuratorFacts
  10
+    {
  11
+        public class TheCurateMethod
  12
+        {
  13
+            [Fact]
  14
+            public void WillNotIncludeThePackageWhenTheWindows8CuratedFeedDoesNotExist()
  15
+            {
  16
+                var curator = new TestableWindows8PackageCurator();
  17
+                curator.StubCuratedFeedByNameQry.Setup(stub => stub.Execute(It.IsAny<string>(), It.IsAny<bool>())).Returns((CuratedFeed)null);
  18
+                var package = CreateStubGalleryPackage();
  19
+                package.Tags = "winrt";
  20
+
  21
+                curator.Curate(package, null);
  22
+
  23
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  24
+                    It.IsAny<int>(),
  25
+                    It.IsAny<int>(),
  26
+                    It.IsAny<bool>(),
  27
+                    It.IsAny<bool>(),
  28
+                    It.IsAny<string>()), Times.Never());   
  29
+            }
  30
+
  31
+            [Theory]
  32
+            [InlineData("winrt")]
  33
+            [InlineData("win8")]
  34
+            [InlineData("windows8")]
  35
+            [InlineData("winjs")]
  36
+            [InlineData("wInRt")]
  37
+            [InlineData("wIn8")]
  38
+            [InlineData("wInDows8")]
  39
+            [InlineData("wInJs")]
  40
+            public void WillIncludeThePackageWhenItHasAcceptedTag(string tag)
  41
+            {
  42
+                var curator = new TestableWindows8PackageCurator();
  43
+                var stubGalleryPackage = CreateStubGalleryPackage();
  44
+                stubGalleryPackage.Tags = "aTag " + tag + " aThirdTag";
  45
+
  46
+                curator.Curate(stubGalleryPackage, null);
  47
+
  48
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  49
+                    It.IsAny<int>(),
  50
+                    It.IsAny<int>(),
  51
+                    It.IsAny<bool>(),
  52
+                    It.IsAny<bool>(),
  53
+                    It.IsAny<string>()), Times.Once());
  54
+            }
  55
+
  56
+            [Fact]
  57
+            public void WillNotIncludeThePackageWhenNotTagged()
  58
+            {
  59
+                var curator = new TestableWindows8PackageCurator();
  60
+                var stubGalleryPackage = CreateStubGalleryPackage();
  61
+                stubGalleryPackage.Tags = "aTag notforwinrt aThirdTag";
  62
+
  63
+                curator.Curate(stubGalleryPackage, null);
  64
+
  65
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  66
+                    It.IsAny<int>(),
  67
+                    It.IsAny<int>(),
  68
+                    It.IsAny<bool>(),
  69
+                    It.IsAny<bool>(),
  70
+                    It.IsAny<string>()), Times.Never());
  71
+            }
  72
+
  73
+            [Fact]
  74
+            public void WillNotIncludeThePackageWhenTagsIsNull()
  75
+            {
  76
+                var curator = new TestableWindows8PackageCurator();
  77
+                var stubGalleryPackage = CreateStubGalleryPackage();
  78
+                stubGalleryPackage.Tags = null;
  79
+
  80
+                curator.Curate(stubGalleryPackage, null);
  81
+
  82
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  83
+                    It.IsAny<int>(),
  84
+                    It.IsAny<int>(),
  85
+                    It.IsAny<bool>(),
  86
+                    It.IsAny<bool>(),
  87
+                    It.IsAny<string>()), Times.Never());
  88
+            }
  89
+
  90
+            [Fact]
  91
+            public void WillIncludeThePackageUsingTheCuratedFeedKey()
  92
+            {
  93
+                var curator = new TestableWindows8PackageCurator();
  94
+                curator.StubCuratedFeed.Key = 42;
  95
+                var package = CreateStubGalleryPackage();
  96
+                package.Tags = "winrt";
  97
+
  98
+                curator.Curate(package, CreateStubNuGetPackage().Object);
  99
+
  100
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  101
+                    42,
  102
+                    It.IsAny<int>(),
  103
+                    It.IsAny<bool>(),
  104
+                    It.IsAny<bool>(),
  105
+                    It.IsAny<string>()));
  106
+            }
  107
+
  108
+            [Fact]
  109
+            public void WillIncludeThePackageUsingThePackageRegistrationKey()
  110
+            {
  111
+                var curator = new TestableWindows8PackageCurator();
  112
+                var stubGalleryPackage = CreateStubGalleryPackage();
  113
+                stubGalleryPackage.PackageRegistration.Key = 42;
  114
+                stubGalleryPackage.Tags = "winrt";
  115
+
  116
+                curator.Curate(stubGalleryPackage, CreateStubNuGetPackage().Object);
  117
+
  118
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  119
+                    It.IsAny<int>(),
  120
+                    42,
  121
+                    It.IsAny<bool>(),
  122
+                    It.IsAny<bool>(),
  123
+                    It.IsAny<string>()));
  124
+            }
  125
+
  126
+            [Fact]
  127
+            public void WillSetTheAutomaticBitWhenIncludingThePackage()
  128
+            {
  129
+                var curator = new TestableWindows8PackageCurator();
  130
+                var stubGalleryPackage = CreateStubGalleryPackage();
  131
+                stubGalleryPackage.Tags = "winrt";
  132
+
  133
+                curator.Curate(stubGalleryPackage, CreateStubNuGetPackage().Object);
  134
+
  135
+                curator.StubCreatedCuratedPackageCmd.Verify(stub => stub.Execute(
  136
+                    It.IsAny<int>(),
  137
+                    It.IsAny<int>(),
  138
+                    It.IsAny<bool>(),
  139
+                    true,
  140
+                    It.IsAny<string>()));
  141
+            }
  142
+
  143
+            static Package CreateStubGalleryPackage()
  144
+            {
  145
+                return new Package
  146
+                {
  147
+                    IsLatestStable = true      ,
  148
+                    PackageRegistration = new PackageRegistration
  149
+                    {
  150
+                        Key = 0,                            
  151
+                    },
  152
+                };
  153
+            }
  154
+
  155
+            static Mock<IPackage> CreateStubNuGetPackage()
  156
+            {
  157
+                var stubNuGetPackage = new Mock<IPackage>();
  158
+                stubNuGetPackage.Setup(stub => stub.GetFiles()).Returns(new IPackageFile[] {});
  159
+                return stubNuGetPackage;
  160
+            }
  161
+
  162
+            static Mock<IPackageFile> CreateStubNuGetPackageFile(string path)
  163
+            {
  164
+                var stubPackageFile = new Mock<IPackageFile>();
  165
+                stubPackageFile.Setup(stub => stub.Path).Returns(path);
  166
+                return stubPackageFile;
  167
+            }
  168
+        }
  169
+
  170
+        public class TestableWindows8PackageCurator : Windows8PackageCurator
  171
+        {
  172
+            public TestableWindows8PackageCurator()
  173
+            {
  174
+                StubCreatedCuratedPackageCmd = new Mock<ICreateCuratedPackageCommand>();
  175
+                StubCuratedFeed = new CuratedFeed { Key = 0 };
  176
+                StubCuratedFeedByNameQry = new Mock<ICuratedFeedByNameQuery>();
  177
+
  178
+                StubCuratedFeedByNameQry
  179
+                    .Setup(stub => stub.Execute(It.IsAny<string>(), It.IsAny<bool>()))
  180
+                    .Returns(StubCuratedFeed);
  181
+            }
  182
+
  183
+            public Mock<ICreateCuratedPackageCommand> StubCreatedCuratedPackageCmd { get; set; }
  184
+            public CuratedFeed StubCuratedFeed { get; private set; }
  185
+            public Mock<ICuratedFeedByNameQuery> StubCuratedFeedByNameQry { get; private set; }
  186
+
  187
+            protected override T GetService<T>()
  188
+            {
  189
+                if (typeof(T) == typeof(ICreateCuratedPackageCommand))
  190
+                    return (T)StubCreatedCuratedPackageCmd.Object;
  191
+                
  192
+                if (typeof(T) == typeof(ICuratedFeedByNameQuery))
  193
+                    return (T)StubCuratedFeedByNameQry.Object;
  194
+                
  195
+                throw new Exception("Tried to get an unexpected service.");
  196
+            }
  197
+        }
  198
+    }
  199
+}
2  Website/App_Start/ContainerBindings.cs
@@ -173,6 +173,8 @@ public override void Load()
173 173
             // todo: bind all package curators by convention
174 174
             Bind<IAutomaticPackageCurator>()
175 175
                 .To<WebMatrixPackageCurator>();
  176
+            Bind<IAutomaticPackageCurator>()
  177
+                .To<Windows8PackageCurator>();
176 178
 
177 179
             // todo: bind all commands by convention
178 180
             Bind<IAutomaticallyCuratePackageCommand>()
10  Website/Controllers/PackagesController.cs
@@ -129,17 +129,25 @@ public virtual ActionResult DisplayPackage(string id, string version)
129 129
             return View(model);
130 130
         }
131 131
 
132  
-        public virtual ActionResult ListPackages(string q, string sortOrder = Constants.PopularitySortOrder, int page = 1)
  132
+        public virtual ActionResult ListPackages(string q, string sortOrder = null, int page = 1)
133 133
         {
134 134
             if (page < 1)
135 135
             {
136 136
                 page = 1;
137 137
             }
138 138
 
  139
+
139 140
             IQueryable<Package> packageVersions = packageSvc.GetLatestPackageVersions(allowPrerelease: true);
140 141
 
141 142
             q = (q ?? "").Trim();
142 143
 
  144
+            if (String.IsNullOrEmpty(sortOrder))
  145
+            {
  146
+                // Determine the default sort order. If no query string is specified, then the sortOrder is DownloadCount
  147
+                // If we are searching for something, sort by relevance.
  148
+                sortOrder = q.IsEmpty() ? Constants.PopularitySortOrder : Constants.RelevanceSortOrder;
  149
+            }
  150
+
143 151
             if (GetIdentity().IsAuthenticated)
144 152
             {
145 153
                 // Only show listed packages. For unlisted packages, only show them if the owner is viewing it.
205  Website/Infrastructure/Elmah.SqlServer.sql
... ...
@@ -0,0 +1,205 @@
  1
+SET ANSI_NULLS ON
  2
+SET QUOTED_IDENTIFIER ON
  3
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_Error]') AND type in (N'U'))
  4
+BEGIN
  5
+CREATE TABLE [dbo].[ELMAH_Error](
  6
+    [ErrorId] [uniqueidentifier] NOT NULL,
  7
+    [Application] [nvarchar](60) NOT NULL,
  8
+    [Host] [nvarchar](50) NOT NULL,
  9
+    [Type] [nvarchar](100) NOT NULL,
  10
+    [Source] [nvarchar](60) NOT NULL,
  11
+    [Message] [nvarchar](500) NOT NULL,
  12
+    [User] [nvarchar](50) NOT NULL,
  13
+    [StatusCode] [int] NOT NULL,
  14
+    [TimeUtc] [datetime] NOT NULL,
  15
+    [Sequence] [int] IDENTITY(1,1) NOT NULL,
  16
+    [AllXml] [nvarchar](max) NOT NULL,
  17
+ CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY CLUSTERED 
  18
+(
  19
+    [ErrorId] ASC
  20
+)WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF)
  21
+)
  22
+END
  23
+
  24
+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')
  25
+CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error] 
  26
+(
  27
+    [Application] ASC,
  28
+    [TimeUtc] DESC,
  29
+    [Sequence] DESC
  30
+)WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF)
  31
+GO
  32
+IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_ELMAH_Error_ErrorId]') AND type = 'D')
  33
+BEGIN
  34
+ALTER TABLE [dbo].[ELMAH_Error] ADD  CONSTRAINT [DF_ELMAH_Error_ErrorId]  DEFAULT (newid()) FOR [ErrorId]
  35
+END
  36
+
  37
+GO
  38
+SET ANSI_NULLS ON
  39
+SET QUOTED_IDENTIFIER ON
  40
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorsXml]') AND type in (N'P', N'PC'))
  41
+BEGIN
  42
+EXEC dbo.sp_executesql @statement = N'
  43
+CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
  44
+(
  45
+    @Application NVARCHAR(60),
  46
+    @PageIndex INT = 0,
  47
+    @PageSize INT = 15,
  48
+    @TotalCount INT OUTPUT
  49
+)
  50
+AS 
  51
+
  52
+    SET NOCOUNT ON
  53
+
  54
+    DECLARE @FirstTimeUTC DATETIME
  55
+    DECLARE @FirstSequence INT
  56
+    DECLARE @StartRow INT
  57
+    DECLARE @StartRowIndex INT
  58
+
  59
+    SELECT 
  60
+        @TotalCount = COUNT(1) 
  61
+    FROM 
  62
+        [ELMAH_Error]
  63
+    WHERE 
  64
+        [Application] = @Application
  65
+
  66
+    -- Get the ID of the first error for the requested page
  67
+
  68
+    SET @StartRowIndex = @PageIndex * @PageSize + 1
  69
+
  70
+    IF @StartRowIndex <= @TotalCount
  71
+    BEGIN
  72
+
  73
+        SET ROWCOUNT @StartRowIndex
  74
+
  75
+        SELECT  
  76
+            @FirstTimeUTC = [TimeUtc],
  77
+            @FirstSequence = [Sequence]
  78
+        FROM 
  79
+            [ELMAH_Error]
  80
+        WHERE   
  81
+            [Application] = @Application
  82
+        ORDER BY 
  83
+            [TimeUtc] DESC, 
  84
+            [Sequence] DESC
  85
+
  86
+    END
  87
+    ELSE
  88
+    BEGIN
  89
+
  90
+        SET @PageSize = 0
  91
+
  92
+    END
  93
+
  94
+    -- Now set the row count to the requested page size and get
  95
+    -- all records below it for the pertaining application.
  96
+
  97
+    SET ROWCOUNT @PageSize
  98
+
  99
+    SELECT 
  100
+        errorId     = [ErrorId], 
  101
+        application = [Application],
  102
+        host        = [Host], 
  103
+        type        = [Type],
  104
+        source      = [Source],
  105
+        message     = [Message],
  106
+        [user]      = [User],
  107
+        statusCode  = [StatusCode], 
  108
+        time        = CONVERT(VARCHAR(50), [TimeUtc], 126) + ''Z''
  109
+    FROM 
  110
+        [ELMAH_Error] error
  111
+    WHERE
  112
+        [Application] = @Application
  113
+    AND
  114
+        [TimeUtc] <= @FirstTimeUTC
  115
+    AND 
  116
+        [Sequence] <= @FirstSequence
  117
+    ORDER BY
  118
+        [TimeUtc] DESC, 
  119
+        [Sequence] DESC
  120
+    FOR
  121
+        XML AUTO
  122
+
  123
+' 
  124
+END
  125
+GO
  126
+SET ANSI_NULLS ON
  127
+SET QUOTED_IDENTIFIER ON
  128
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorXml]') AND type in (N'P', N'PC'))
  129
+BEGIN
  130
+EXEC dbo.sp_executesql @statement = N'
  131
+CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
  132
+(
  133
+    @Application NVARCHAR(60),
  134
+    @ErrorId UNIQUEIDENTIFIER
  135
+)
  136
+AS
  137
+
  138
+    SET NOCOUNT ON
  139
+
  140
+    SELECT 
  141
+        [AllXml]
  142
+    FROM 
  143
+        [ELMAH_Error]
  144
+    WHERE
  145
+        [ErrorId] = @ErrorId
  146
+    AND
  147
+        [Application] = @Application
  148
+
  149
+' 
  150
+END
  151
+GO
  152
+SET ANSI_NULLS ON
  153
+SET QUOTED_IDENTIFIER ON
  154
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_LogError]') AND type in (N'P', N'PC'))
  155
+BEGIN
  156
+EXEC dbo.sp_executesql @statement = N'
  157
+CREATE PROCEDURE [dbo].[ELMAH_LogError]
  158
+(
  159
+    @ErrorId UNIQUEIDENTIFIER,
  160
+    @Application NVARCHAR(60),
  161
+    @Host NVARCHAR(30),
  162
+    @Type NVARCHAR(100),
  163
+    @Source NVARCHAR(60),
  164
+    @Message NVARCHAR(500),
  165
+    @User NVARCHAR(50),
  166
+    @AllXml NVARCHAR(MAX),
  167
+    @StatusCode INT,
  168
+    @TimeUtc DATETIME
  169
+)
  170
+AS
  171
+
  172
+    SET NOCOUNT ON
  173
+
  174
+    INSERT
  175
+    INTO
  176
+        [ELMAH_Error]
  177
+        (
  178
+            [ErrorId],
  179
+            [Application],
  180
+            [Host],
  181
+            [Type],
  182
+            [Source],
  183
+            [Message],
  184
+            [User],
  185
+            [AllXml],
  186
+            [StatusCode],
  187
+            [TimeUtc]
  188
+        )
  189
+    VALUES
  190
+        (
  191
+            @ErrorId,
  192
+            @Application,
  193
+            @Host,
  194
+            @Type,
  195
+            @Source,
  196
+            @Message,
  197
+            @User,
  198
+            @AllXml,
  199
+            @StatusCode,
  200
+            @TimeUtc
  201
+        )
  202
+
  203
+' 
  204
+END
  205
+GO
56  Website/Infrastructure/Lucene/LuceneIndexingService.cs
@@ -89,19 +89,25 @@ private static void AddPackages(IndexWriter indexWriter, List<PackageIndexEntity
89 89
                 var document = new Document();
90 90
 
91 91
                 document.Add(new Field("Key", package.Key.ToString(CultureInfo.InvariantCulture), Field.Store.YES, Field.Index.NO));
92  
-                document.Add(new Field("Id-Exact", package.Id, Field.Store.NO, Field.Index.ANALYZED));
  92
+                document.Add(new Field("Id-Exact", package.Id.ToLowerInvariant(), Field.Store.NO, Field.Index.NOT_ANALYZED));
93 93
 
94 94
                 document.Add(new Field("Description", package.Description, Field.Store.NO, Field.Index.ANALYZED));
95 95
 
96  
-                foreach (var idToken in TokenizeId(package.Id))
  96
+                var tokenizedId = TokenizeId(package.Id);
  97
+                foreach (var idToken in tokenizedId)
97 98
                 {
98 99
                     document.Add(new Field("Id", idToken, Field.Store.NO, Field.Index.ANALYZED));
99 100
                 }
100 101
 
101  
-                if (!String.IsNullOrEmpty(package.Title))
  102
+                // If an element does not have a Title, then add all the tokenized Id components as Title.
  103
+                // Lucene's StandardTokenizer does not tokenize items of the format a.b.c which does not play well with things like "xunit.net". 
  104
+                // We will feed it values that are already tokenized.
  105
+                var titleTokens = String.IsNullOrEmpty(package.Title) ? tokenizedId : package.Title.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries);
  106
+                foreach (var idToken in titleTokens)
102 107
                 {
103  
-                    document.Add(new Field("Title", package.Title, Field.Store.NO, Field.Index.ANALYZED));
  108
+                    document.Add(new Field("Title", idToken, Field.Store.NO, Field.Index.ANALYZED));
104 109
                 }
  110
+
105 111
                 if (!String.IsNullOrEmpty(package.Tags))
106 112
                 {
107 113
                     document.Add(new Field("Tags", package.Tags, Field.Store.NO, Field.Index.ANALYZED));
@@ -171,35 +177,45 @@ protected internal virtual void UpdateLastWriteTime()
171 177
 
172 178
         internal static IEnumerable<string> TokenizeId(string term)
173 179
         {
174  
-            var result = CamelCaseTokenize(term).SelectMany(s => s.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries)).ToList();
175  
-            if (result.Count == 1)
176  
-            {
177  
-                return Enumerable.Empty<string>();
178  
-            }
  180
+
  181
+            // First tokenize the result by id-separators. For e.g. tokenize SignalR.EventStream as SignalR and EventStream
  182
+            var tokens = term.Split(idSeparators, StringSplitOptions.RemoveEmptyEntries);
  183
+
  184
+            // For each token, further attempt to tokenize camelcase values. e.g. .EventStream -> Event, Stream. 
  185
+            var result = tokens.Concat(new[] { term })
  186
+                               .Concat(tokens.SelectMany(CamelCaseTokenize))
  187
+                               .Distinct(StringComparer.OrdinalIgnoreCase)
  188
+                               .ToList();
179 189
             return result;
180 190
         }
181 191
 
182 192
         private static IEnumerable<string> CamelCaseTokenize(string term)
183 193
         {
184  
-            if (term.Length < 2)
  194
+            const int MinTokenLength = 3;
  195
+            if (term.Length < MinTokenLength)
185 196
             {
186 197
                 yield break;
187 198
             }
188 199
 
189  
-            int tokenStart = 0;
190  
-            for (int i = 1; i < term.Length; i++)
  200
+            int tokenEnd = term.Length;
  201
+            for (int i = term.Length - 1; i > 0; i--)
191 202
             {
192  
-                if (Char.IsUpper(term[i]) && (i - tokenStart > 2))
  203
+                // If the remainder is fewer than 2 chars or we have a token that is at least 2 chars long, tokenize it.
  204
+                if (i < MinTokenLength || (Char.IsUpper(term[i]) && (tokenEnd - i >= MinTokenLength)))
193 205
                 {
194  
-                    yield return term.Substring(tokenStart, i - tokenStart);
195  
-                    tokenStart = i;
  206
+                    if (i < MinTokenLength)
  207
+                    {
  208
+                        // If the remainder is smaller than 2 chars, just return the entire string
  209
+                        i = 0;
  210
+                    }
  211
+                        
  212
+                    yield return term.Substring(i, tokenEnd - i);
  213
+                    tokenEnd = i;
196 214
                 }
197 215
             }
198  
-            if (term.Length - tokenStart < 2)
199  
-            {
200  
-                yield break;
201  
-            }
202  
-            yield return term.Substring(tokenStart);
  216
+
  217
+            // Finally return the term in entirety
  218
+            yield return term;
203 219
         }
204 220
     }
205 221
 }
32  Website/Infrastructure/Lucene/LuceneSearchService.cs
@@ -4,9 +4,10 @@
4 4
 using System.IO;
5 5
 using System.Linq;
6 6
 using Lucene.Net.Analysis.Standard;
  7
+using Lucene.Net.Index;
7 8
 using Lucene.Net.QueryParsers;
8 9
 using Lucene.Net.Search;
9  
-using Lucene.Net.Index;
  10
+using Lucene.Net.Search.Function;
10 11
 
11 12
 namespace NuGetGallery
12 13
 {
@@ -86,25 +87,29 @@ private static IEnumerable<int> SearchCore(string searchTerm)
86 87
 
87 88
         private static Query ParseQuery(string searchTerm)
88 89
         {
89  
-            var fields = new Dictionary<string, float> { { "Id", 1.2f }, { "Title", 1.0f }, { "Tags", 1.0f}, { "Description", 0.8f }, { "Author", 0.6f } };
  90
+            var fields = new Dictionary<string, float> { { "Id", 1.2f }, { "Title", 1.0f }, { "Tags", 0.8f }, { "Description", 0.1f }, 
  91
+                                                         { "Author", 1.0f } };
90 92
             var analyzer = new StandardAnalyzer(LuceneCommon.LuceneVersion);
91 93
             searchTerm = QueryParser.Escape(searchTerm).ToLowerInvariant();
92 94
 
93 95
             var queryParser = new MultiFieldQueryParser(LuceneCommon.LuceneVersion, fields.Keys.ToArray(), analyzer, fields);
94 96
 
95 97
             var conjuctionQuery = new BooleanQuery();
96  
-            conjuctionQuery.SetBoost(1.5f);
  98
+            conjuctionQuery.SetBoost(2.0f);
97 99
             var disjunctionQuery = new BooleanQuery();
  100
+            disjunctionQuery.SetBoost(0.1f);
98 101
             var wildCardQuery = new BooleanQuery();
99  
-            wildCardQuery.SetBoost(0.7f);
  102
+            wildCardQuery.SetBoost(0.5f);
100 103
             var exactIdQuery = new TermQuery(new Term("Id-Exact", searchTerm));
101 104
             exactIdQuery.SetBoost(2.5f);
  105
+            var wildCardIdQuery = new WildcardQuery(new Term("Id-Exact", "*" + searchTerm + "*"));
102 106
             
103  
-            foreach(var term in searchTerm.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
  107
+            foreach(var term in GetSearchTerms(searchTerm))
104 108
             {
105  
-                conjuctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.MUST);
106  
-                disjunctionQuery.Add(queryParser.Parse(term), BooleanClause.Occur.SHOULD);
107  
-                
  109
+                var termQuery = queryParser.Parse(term);
  110
+                conjuctionQuery.Add(termQuery, BooleanClause.Occur.MUST);
  111
+                disjunctionQuery.Add(termQuery, BooleanClause.Occur.SHOULD);
  112
+
108 113
                 foreach (var field in fields)
109 114
                 {
110 115
                     var wildCardTermQuery = new WildcardQuery(new Term(field.Key, term + "*"));
@@ -113,7 +118,16 @@ private static Query ParseQuery(string searchTerm)
113 118
                 }
114 119
             }
115 120
 
116  
-            return conjuctionQuery.Combine(new Query[] { exactIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery });
  121
+            var downloadCountBooster = new FieldScoreQuery("DownloadCount", FieldScoreQuery.Type.INT);
  122
+            return new CustomScoreQuery(conjuctionQuery.Combine(new Query[] { exactIdQuery, wildCardIdQuery, conjuctionQuery, disjunctionQuery, wildCardQuery }),
  123
+                                       downloadCountBooster);
  124
+        }
  125
+
  126
+        private static IEnumerable<string> GetSearchTerms(string searchTerm)
  127
+        {
  128
+            return searchTerm.Split(new[] { ' ', '.', '-' }, StringSplitOptions.RemoveEmptyEntries)
  129
+                             .Concat(new[] { searchTerm })
  130
+                             .Distinct(StringComparer.OrdinalIgnoreCase);
117 131
         }
118 132
     }
119 133
 }
24  Website/Migrations/201206250141447_ExecuteELMAHSql.Designer.cs
... ...
@@ -0,0 +1,24 @@
  1
+// <auto-generated />
  2
+namespace NuGetGallery.Migrations
  3
+{
  4
+    using System.Data.Entity.Migrations;
  5
+    using System.Data.Entity.Migrations.Infrastructure;
  6
+    
  7
+    public sealed partial class ExecuteELMAHSql : IMigrationMetadata
  8
+    {
  9
+        string IMigrationMetadata.Id
  10
+        {
  11
+            get { return "201206250141447_ExecuteELMAHSql"; }
  12
+        }
  13
+        
  14
+        string IMigrationMetadata.Source
  15
+        {
  16
+            get { return null; }
  17
+        }
  18
+        
  19
+        string IMigrationMetadata.Target
  20
+        {
  21
+            get { return "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"; }
  22
+        }
  23
+    }
  24
+}
36  Website/Migrations/201206250141447_ExecuteELMAHSql.cs
... ...
@@ -0,0 +1,36 @@
  1
+using System;
  2
+using System.Data.Entity.Migrations;
  3
+using System.IO;
  4
+
  5
+namespace NuGetGallery.Migrations
  6
+{
  7
+    public partial class ExecuteELMAHSql : DbMigration
  8
+    {
  9
+        private static readonly string[] Go = new[] { "GO" };
  10
+
  11
+        public override void Up()
  12
+        {
  13
+            using (var stream = typeof(ExecuteELMAHSql).Assembly.GetManifestResourceStream("NuGetGallery.Infrastructure.Elmah.SqlServer.sql"))
  14
+            {
  15
+                using (var streamReader = new StreamReader(stream))
  16
+                {
  17
+                    var statements = streamReader.ReadToEnd().Split(Go, StringSplitOptions.RemoveEmptyEntries);
  18
+
  19
+                    foreach (var statement in statements)
  20
+                    {
  21
+                        if (String.IsNullOrWhiteSpace(statement))
  22
+                        {
  23
+                            continue;
  24
+                        }
  25
+
  26
+                        Sql(statement);
  27
+                    }
  28
+                }
  29
+            }
  30
+        }
  31
+
  32
+        public override void Down()
  33
+        {
  34
+        }
  35
+    }
  36
+}
41  Website/PackageCurators/TagBasedPackageCurator.cs
... ...
@@ -0,0 +1,41 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Web;
  5
+using NuGet;
  6
+
  7
+namespace NuGetGallery
  8
+{
  9
+    public abstract class TagBasedPackageCurator : AutomaticPackageCurator
  10
+    {
  11
+        /// <summary>
  12
+        /// 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.
  13
+        /// </summary>
  14
+        protected abstract IEnumerable<string> RequiredTags { get; }
  15
+
  16
+        /// <summary>
  17
+        /// Gets the name of the curated feed to add the package to.
  18
+        /// </summary>
  19
+        protected abstract string CuratedFeedName { get; }
  20
+
  21
+        public override void Curate(Package galleryPackage, IPackage nugetPackage)
  22
+        {
  23
+            // Make sure the target feed exists
  24
+            CuratedFeed feed = GetService<ICuratedFeedByNameQuery>().Execute(CuratedFeedName, includePackages: false);
  25
+            if (feed != null && galleryPackage.Tags != null)
  26
+            {
  27
+                // 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")
  28
+                string[] tags = galleryPackage.Tags.Split();
  29
+
  30
+                // Check if this package should be curated
  31
+                if (tags.Any(tag => RequiredTags.Contains(tag, StringComparer.OrdinalIgnoreCase)))
  32
+                {
  33
+                    // It should! Add it to the curated feed
  34
+                    GetService<ICreateCuratedPackageCommand>().Execute(
  35
+                        feed.Key, galleryPackage.PackageRegistration.Key, automaticallyCurated: true);
  36
+                }
  37
+
  38
+            }
  39
+        }
  40
+    }
  41
+}
26  Website/PackageCurators/Windows8PackageCurator.cs
... ...
@@ -0,0 +1,26 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Web;
  5
+
  6
+namespace NuGetGallery
  7
+{
  8
+    public class Windows8PackageCurator : TagBasedPackageCurator
  9
+    {
  10
+        protected override IEnumerable<string> RequiredTags
  11
+        {
  12
+            get
  13
+            {
  14
+                yield return "winrt";
  15
+                yield return "win8";
  16
+                yield return "windows8";
  17
+                yield return "winjs";
  18
+            }
  19
+        }
  20
+
  21
+        protected override string CuratedFeedName
  22
+        {
  23
+            get { return "windows8-packages"; }
  24
+        }
  25
+    }
  26
+}
2  Website/Scripts/stats.js
@@ -45,7 +45,7 @@ function update(data, key) {
45 45
 
46 46
     function animateEl(el, v) {
47 47
         v = v || '';
48  
-        var parent = e.parent();
  48
+        var parent = el.parent();
49 49
         el.animate({ top: 0.3 * parseInt(parent.height()) }, 350, 'linear', function () {
50 50
             $(this).html(v).css({ top: -0.8 * parseInt(parent.height()) }).animate({ top: 0 }, 350, 'linear')
51 51
         });
10  Website/Services/AggregateStatsService.cs
@@ -24,12 +24,14 @@ public AggregateStats GetAggregateStats()
24 24
                     database.Connection.Open();
25 25
                     using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection | CommandBehavior.SingleRow))
26 26
                     {
27  
-                        reader.Read();
  27
+                        bool hasData = reader.Read();
  28
+                        if (!hasData)
  29
+                            return new AggregateStats();
28 30
                         return new AggregateStats
29 31
                         {
30  
-                            UniquePackages = reader.GetInt32(0),
31  
-                            TotalPackages = reader.GetInt32(1),
32  
-                            Downloads = reader.GetInt32(2),
  32
+                            UniquePackages = reader.IsDBNull(0) ? 0 : reader.GetInt32(0),
  33
+                            TotalPackages = reader.IsDBNull(1) ? 0 : reader.GetInt32(1),
  34
+                            Downloads = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
33 35
                         };
34 36
                     }
35 37
                 }
5  Website/Views/Packages/ListPackages.cshtml
@@ -22,11 +22,14 @@
22 22
     <fieldset class="form search">
23 23
         <legend>Sort Order</legend>
24 24
         <input type="hidden" name="q" value="@Model.SearchTerm" />
  25
+
25 26
         <div class="form-field">
26 27
             <label for="sortOrder">Sort By</label>
27 28
             <select name="sortOrder" id="sortOrder">
  29
+                @if (!Model.SearchTerm.IsEmpty()) {
  30
+                    @ViewHelpers.Option(Constants.RelevanceSortOrder, "Relevance", Model.SortOrder)
  31
+                }
28 32
                 @ViewHelpers.Option(Constants.PopularitySortOrder, "Popularity", Model.SortOrder)
29  
-                @ViewHelpers.Option(Constants.RelevanceSortOrder, "Relevance", Model.SortOrder)
30 33
                 @ViewHelpers.Option(Constants.AlphabeticSortOrder, "A-Z", Model.SortOrder)
31 34
                 @ViewHelpers.Option(Constants.RecentSortOrder, "Recent", Model.SortOrder)
32 35
             </select>
13  Website/Website.csproj
@@ -26,6 +26,10 @@
26 26
     <UpgradeBackupLocation>
27 27
     </UpgradeBackupLocation>
28 28
     <OldToolsVersion>4.0</OldToolsVersion>
  29
+    <IISExpressSSLPort />
  30
+    <IISExpressAnonymousAuthentication />
  31
+    <IISExpressWindowsAuthentication />
  32
+    <IISExpressUseClassicPipelineMode />
29 33
   </PropertyGroup>
30 34
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
31 35
     <DebugSymbols>true</DebugSymbols>
@@ -346,10 +350,16 @@
346 350
     <Compile Include="Migrations\201206131919241_AddTargetFxToDependencies.Designer.cs">
347 351
       <DependentUpon>201206131919241_AddTargetFxToDependencies.cs</DependentUpon>
348 352
     </Compile>
  353
+    <Compile Include="Migrations\201206250141447_ExecuteELMAHSql.cs" />
  354
+    <Compile Include="Migrations\201206250141447_ExecuteELMAHSql.Designer.cs">
  355
+      <DependentUpon>201206250141447_ExecuteELMAHSql.cs</DependentUpon>
  356
+    </Compile>
349 357
     <Compile Include="Migrations\MigrationsConfiguration.cs" />
350 358
     <Compile Include="PackageCurators\IAutomaticPackageCurator.cs" />
351 359
     <Compile Include="PackageCurators\AutomaticPackageCurator.cs" />
  360
+    <Compile Include="PackageCurators\TagBasedPackageCurator.cs" />
352 361
     <Compile Include="PackageCurators\WebMatrixPackageCurator.cs" />
  362
+    <Compile Include="PackageCurators\Windows8PackageCurator.cs" />
353 363
     <Compile Include="PackagesController.generated.cs">
354 364
       <DependentUpon>T4MVC.tt</DependentUpon>
355 365
     </Compile>
@@ -787,6 +797,7 @@
787 797
     <Content Include="DynamicData\Site.css" />
788 798
     <Content Include="Errors\Error.html" />
789 799
     <Content Include="favicon.ico" />
  800
+    <EmbeddedResource Include="Infrastructure\Elmah.SqlServer.sql" />
790 801
     <Content Include="Scripts\stats.js" />
791 802
     <Content Include="Scripts\jquery-1.6.2-vsdoc.js" />
792 803
     <Content Include="Scripts\jquery-1.6.2.js" />
@@ -918,4 +929,4 @@
918 929
     </VisualStudio>
919 930
   </ProjectExtensions>
920 931
   <Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
921  
-</Project>
  932
+</Project>

0 notes on commit 0d6b523

Please sign in to comment.
Something went wrong with that request. Please try again.