private static void ProcessCompatibleVersion(string packageId, PerfEventTracker perfTracker, IDictionary <string, FacetedDocument> candidateNewFacets, FacetedDocument doc, FrameworkName projectFx)
        {
            using (perfTracker.TrackEvent("ProcessCompatibleVersion", "{0} v{1} (fx:{2})", packageId, doc.Version, projectFx))
            {
                // Add compatible facet
                doc.AddFacet(Facets.Compatible(projectFx));

                // If listed, process it against latest versions
                if (doc.Data.Package.Listed)
                {
                    // Check it against the current latest prerelease and swap latests if necessary
                    string latestPreFacet    = Facets.LatestPrereleaseVersion(projectFx);
                    string latestStableFacet = Facets.LatestStableVersion(projectFx);
                    if (!candidateNewFacets.ContainsKey(latestPreFacet))
                    {
                        candidateNewFacets[latestPreFacet] = doc;
                    }

                    // If this package is a stable version, do the same for latest stable
                    if (String.IsNullOrEmpty(doc.Version.SpecialVersion) && !candidateNewFacets.ContainsKey(latestStableFacet))
                    {
                        candidateNewFacets[latestStableFacet] = doc;
                    }
                }
            }
        }
        //  this function will incrementally build an index from the gallery using a high water mark stored in the commit metadata
        //  this function is useful for building a fresh index as in that case it is more efficient than diff-ing approach

        public static void RebuildIndex(string sqlConnectionString, Lucene.Net.Store.Directory directory, FrameworksList frameworks, TextWriter log = null, PerfEventTracker perfTracker = null)
        {
            perfTracker = perfTracker ?? new PerfEventTracker();
            log         = log ?? DefaultTraceWriter;

            Stopwatch sw = new Stopwatch();

            sw.Start();

            using (perfTracker.TrackEvent("RebuildIndex", String.Empty))
            {
                // Empty the index, we're rebuilding
                CreateNewEmptyIndex(directory);

                var projectFxs = frameworks.Load();

                log.WriteLine("get curated feeds by PackageRegistration");
                IDictionary <int, IEnumerable <string> > feeds = GalleryExport.GetFeedsByPackageRegistration(sqlConnectionString, log, verbose: false);

                int highestPackageKey = 0;
                while (true)
                {
                    log.WriteLine("get the checksums from the gallery");
                    IDictionary <int, int> checksums = GalleryExport.FetchGalleryChecksums(sqlConnectionString, highestPackageKey);

                    log.WriteLine("get packages from gallery where the Package.Key > {0}", highestPackageKey);
                    List <Package> packages = GalleryExport.GetPublishedPackagesSince(sqlConnectionString, highestPackageKey, log, verbose: false);

                    if (packages.Count == 0)
                    {
                        break;
                    }

                    log.WriteLine("associate the feeds and checksum data with each packages");
                    List <IndexDocumentData> indexDocumentData = MakeIndexDocumentData(packages, feeds, checksums);
                    highestPackageKey = indexDocumentData.Max(d => d.Package.Key);

                    AddPackagesToIndex(indexDocumentData, directory, log, projectFxs, perfTracker);

                    // Summarize performance
                    // (Save some time by not bothering if the log is "null")
                    if (!ReferenceEquals(TextWriter.Null, log) && !ReferenceEquals(PerfEventTracker.Null, perfTracker))
                    {
                        SummarizePerf(log, perfTracker);
                    }
                }
            }

            SummarizePerf(log, perfTracker);

            sw.Stop();
            log.WriteLine("all done, took {0}", sw.Elapsed);
        }
        private static IEnumerable <FacetedDocument> DetermineDirtyDocuments(IEnumerable <FrameworkName> projectFxs, PerfEventTracker perfTracker, IndexReader reader, string id, IEnumerable <IndexDocumentData> data)
        {
            using (perfTracker.TrackEvent("Processdata", id))
            {
                // Get all documents matching the ID of this data.
                var documents = CollectExistingDocuments(perfTracker, reader, id);

                // Add the new documents
                using (perfTracker.TrackEvent("CreateNewDocuments", id))
                {
                    foreach (var package in data)
                    {
                        documents.Add(new FacetedDocument(package));
                    }
                }

                // Process the facets
                UpdateFacets(id, documents, projectFxs, perfTracker);

                return(documents.Where(d => d.Dirty));
            }
        }
        private static void WriteDirtyDocuments(List <FacetedDocument> dirtyDocs, IndexWriter indexWriter, PerfEventTracker perfTracker)
        {
            // Delete dirty documents and flush
            foreach (var dirtyDoc in dirtyDocs.Where(d => d.Dirty))
            {
                indexWriter.DeleteDocuments(dirtyDoc.GetQuery());
            }

            using (perfTracker.TrackEvent("FlushingDeletes", ""))
            {
                indexWriter.Flush(triggerMerge: false, flushDocStores: true, flushDeletes: true);
            }

            // (Re-)add dirty documents
            foreach (var dirtyDoc in dirtyDocs)
            {
                using (perfTracker.TrackEvent("AddDocument", "{0} v{1}", dirtyDoc.Id, dirtyDoc.Version))
                {
                    indexWriter.AddDocument(CreateLuceneDocument(dirtyDoc));
                }
            }
        }
        private static List <FacetedDocument> CollectExistingDocuments(PerfEventTracker perfTracker, IndexReader reader, string id)
        {
            var docs      = reader.TermDocs(new Term("Id", id.ToLowerInvariant()));
            var documents = new List <FacetedDocument>();

            using (perfTracker.TrackEvent("GetExistingDocuments", id))
            {
                while (docs.Next())
                {
                    documents.Add(new FacetedDocument(reader.Document(docs.Doc)));
                }
            }
            return(documents);
        }
        private static void AddToIndex(Lucene.Net.Store.Directory directory, List <IndexDocumentData> rangeToIndex, TextWriter log, IEnumerable <FrameworkName> projectFxs, PerfEventTracker perfTracker)
        {
            log.WriteLine("begin AddToIndex");

            int highestPackageKey = -1;

            var groups = rangeToIndex.GroupBy(d => d.Package.PackageRegistration.Id).ToList();

            // Collect documents to change
            var dirtyDocs = new List <FacetedDocument>();

            using (var reader = IndexReader.Open(directory, readOnly: true))
                using (perfTracker.TrackEvent("CalculateChanges", ""))
                {
                    foreach (var group in groups)
                    {
                        var newDirtyDocs = DetermineDirtyDocuments(projectFxs, perfTracker, reader, group.Key, group);

                        // (Re-)Add any dirty documents to the index
                        dirtyDocs.AddRange(newDirtyDocs);
                    }
                }

            using (IndexWriter indexWriter = CreateIndexWriter(directory, create: false))
            {
                WriteDirtyDocuments(dirtyDocs, indexWriter, perfTracker);

                highestPackageKey = rangeToIndex.Max(i => i.Package.Key);

                log.WriteLine("about to commit {0} packages", rangeToIndex.Count);

                IDictionary <string, string> commitUserData = indexWriter.GetReader().CommitUserData;

                string lastEditsIndexTime = commitUserData["last-edits-index-time"];

                if (lastEditsIndexTime == null)
                {
                    //  this should never happen but if it did Lucene would throw
                    lastEditsIndexTime = DateTime.MinValue.ToString();
                }

                indexWriter.Commit(PackageIndexing.CreateCommitMetadata(lastEditsIndexTime, highestPackageKey, rangeToIndex.Count, "add"));

                log.WriteLine("commit done");
            }

            log.WriteLine("end AddToIndex");
        }
        private static void UpdateFacets(string packageId, IList <FacetedDocument> documents, IEnumerable <FrameworkName> projectFxs, PerfEventTracker perfTracker)
        {
            using (perfTracker.TrackEvent("UpdateFacets", "{0} ({1} items)", packageId, documents.Count))
            {
                // Collect all the current latest versions into dictionaries
                IDictionary <string, List <FacetedDocument> > existingFacets = new Dictionary <string, List <FacetedDocument> >(StringComparer.OrdinalIgnoreCase);

                using (perfTracker.TrackEvent("FindExistingFacets", packageId))
                {
                    foreach (var document in documents.Where(d => !d.IsNew))
                    {
                        foreach (var projectFx in projectFxs)
                        {
                            AddToExistingFacetsList(existingFacets, document, projectFx, Facets.LatestStableVersion(projectFx));
                            AddToExistingFacetsList(existingFacets, document, projectFx, Facets.LatestPrereleaseVersion(projectFx));
                        }
                    }
                }

                IDictionary <string, FacetedDocument> candidateNewFacets = new Dictionary <string, FacetedDocument>();

                // Process the new documents
                var newDocs = documents.Where(d => d.IsNew).OrderByDescending(d => d.Version).ToList();
                documents = null; // Done with the master list of all documents

                using (perfTracker.TrackEvent("DetermineNewLatestVersions", packageId))
                {
                    foreach (var doc in newDocs)
                    {
                        if (!String.IsNullOrEmpty(doc.Version.SpecialVersion))
                        {
                            doc.AddFacet(Facets.PrereleaseVersion);
                        }
                        if (doc.Data.Package.Listed)
                        {
                            doc.AddFacet(Facets.Listed);
                        }
                        var packageFxs = doc.Data.Package.SupportedFrameworks
                                         .Select(fx =>
                        {
                            using (perfTracker.TrackEvent("ParseFrameworkName", fx.TargetFramework))
                            {
                                return(VersionUtility.ParseFrameworkName(fx.TargetFramework));
                            }
                        })
                                         .ToList();

                        // Process each target framework
                        foreach (var projectFx in projectFxs)
                        {
                            if (projectFx == FrameworksList.AnyFramework || VersionUtility.IsCompatible(projectFx, packageFxs))
                            {
                                ProcessCompatibleVersion(packageId, perfTracker, candidateNewFacets, doc, projectFx);
                            }
                        }
                    }
                }

                // Adjust facets as needed
                using (perfTracker.TrackEvent("AdjustProjectFxes", packageId))
                {
                    foreach (var projectFx in projectFxs)
                    {
                        using (perfTracker.TrackEvent("AdjustProjectFx", "{0} ({1})", packageId, projectFx.FullName))
                        {
                            UpdateLatestVersionFacet(existingFacets, candidateNewFacets, Facets.LatestStableVersion(projectFx));
                            UpdateLatestVersionFacet(existingFacets, candidateNewFacets, Facets.LatestPrereleaseVersion(projectFx));
                        }
                    }
                }
            }
        }