public static void Access(IOwinContext context, string tenantId, SecureSearcherManager searcherManager, string connectionString, int accessDuration)
        {
            string relativeUri = context.Request.Path.ToString();

            string[] parts = relativeUri.Split('/');

            if (!(parts.Length >= 4))
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            if (!parts[1].Equals("resource"))
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            string containerName = parts[2];
            string blobName = string.Join("/", parts, 3, parts.Length - 3);

            if (IsAuthorized(searcherManager, tenantId, containerName, blobName))
            {
                Redirect(context, containerName, blobName, connectionString, accessDuration);
            }
            else
            {
                context.Response.Write("unauthorized");
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
        }
        public static void Access(IOwinContext context, string tenantId, SecureSearcherManager searcherManager, string connectionString, int accessDuration)
        {
            string relativeUri = context.Request.Path.ToString();

            string[] parts = relativeUri.Split('/');

            if (!(parts.Length >= 4))
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            if (!parts[1].Equals("resource"))
            {
                context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            string containerName = parts[2];
            string blobName      = string.Join("/", parts, 3, parts.Length - 3);

            if (IsAuthorized(searcherManager, tenantId, containerName, blobName))
            {
                Redirect(context, containerName, blobName, connectionString, accessDuration);
            }
            else
            {
                context.Response.Write("unauthorized");
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
        }
        static JToken FindById(SecureSearcherManager searcherManager, string id, string tenantId, string scheme)
        {
            IndexSearcher searcher = searcherManager.Get();

            try
            {
                string analyzedId = id.ToLowerInvariant();

                BooleanQuery query = new BooleanQuery();
                query.Add(new BooleanClause(new TermQuery(new Term("FullId", analyzedId)), Occur.MUST));

                Filter filter = searcherManager.GetFilter(tenantId, new string [] { "http://schema.nuget.org/schema#ApiAppPackage" });

                TopDocs topDocs = searcher.Search(query, 1000);

                if (topDocs.TotalHits > 0)
                {
                    Uri     registrationBaseAddress     = searcherManager.RegistrationBaseAddress[scheme];
                    JObject registrationObj             = new JObject();
                    string  registrationRelativeAddress = string.Format("{0}/index.json", id.ToLowerInvariant());
                    registrationObj["registration"] = new Uri(registrationBaseAddress, registrationRelativeAddress).AbsoluteUri;

                    JArray data = new JArray();
                    for (int i = 0; i < topDocs.ScoreDocs.Length; i++)
                    {
                        Document document = searcher.Doc(topDocs.ScoreDocs[i].Doc);

                        JObject versionObj = new JObject();
                        ServiceHelpers.AddField(versionObj, document, "version", "Version");
                        ServiceHelpers.AddField(versionObj, document, "packageContent", "PackageContent");
                        ServiceHelpers.AddField(versionObj, document, "catalogEntry", "CatalogEntry");

                        data.Add(versionObj);
                    }

                    registrationObj["data"] = data;

                    return(registrationObj);
                }
                else
                {
                    return(null);
                }
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        public static async Task Query(IOwinContext context, SecureSearcherManager searcherManager, string tenantId, string currentOwner)
        {
            int skip;

            if (!int.TryParse(context.Request.Query["skip"], out skip))
            {
                skip = 0;
            }

            int take;

            if (!int.TryParse(context.Request.Query["take"], out take))
            {
                take = 50;
            }

            bool countOnly;

            if (!bool.TryParse(context.Request.Query["countOnly"], out countOnly))
            {
                countOnly = false;
            }

            bool includePrerelease;

            if (!bool.TryParse(context.Request.Query["prerelease"], out includePrerelease))
            {
                includePrerelease = false;
            }

            bool includeExplanation = false;

            if (!bool.TryParse(context.Request.Query["explanation"], out includeExplanation))
            {
                includeExplanation = false;
            }

            string q = context.Request.Query["q"] ?? string.Empty;

            string scheme = context.Request.Uri.Scheme;

            JToken result = Search(searcherManager, tenantId, currentOwner, scheme, q, countOnly, includePrerelease, skip, take, includeExplanation);

            await ServiceHelpers.WriteResponse(context, HttpStatusCode.OK, result);
        }
        public static JToken Search(SecureSearcherManager searcherManager, string tenantId, string currentOwner, string scheme, string q, bool countOnly, bool includePrerelease, int skip, int take, bool includeExplanation)
        {
            IndexSearcher searcher = searcherManager.Get();
            try
            {
                Filter filter = searcherManager.GetFilter(tenantId, new[] { "http://schema.nuget.org/schema#ApiAppPackage" }, includePrerelease, false);

                Query query = MakeQuery(q);

                TopDocs topDocs = searcher.Search(query, filter, skip + take);

                return MakeResult(searcher, currentOwner, scheme, topDocs, skip, take, searcherManager, includeExplanation, query);
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        static bool IsAuthorized(SecureSearcherManager searcherManager, string tenantId, string containerName, string blobName)
        {
            searcherManager.MaybeReopen();

            IndexSearcher searcher = searcherManager.Get();

            try
            {
                Filter  filter       = searcherManager.GetFilter(tenantId, new string[] { "http://schema.nuget.org/schema#ApiAppPackage", "http://schema.nuget.org/schema#CatalogInfrastructure" });
                string  relativePath = string.Format("/{0}/{1}", containerName, blobName);
                Query   query        = new TermQuery(new Term("StoragePath", relativePath));
                TopDocs topDocs      = searcher.Search(query, 1);
                return(topDocs.TotalHits > 0);
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        static bool IsAuthorized(SecureSearcherManager searcherManager, string tenantId, string containerName, string blobName)
        {
            searcherManager.MaybeReopen();

            IndexSearcher searcher = searcherManager.Get();

            try
            {
                Filter filter = searcherManager.GetFilter(tenantId, new string[] { "http://schema.nuget.org/schema#ApiAppPackage", "http://schema.nuget.org/schema#CatalogInfrastructure" });
                string relativePath = string.Format("/{0}/{1}", containerName, blobName);
                Query query = new TermQuery(new Term("StoragePath", relativePath));
                TopDocs topDocs = searcher.Search(query, 1);
                return (topDocs.TotalHits > 0);
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        public static JToken Search(SecureSearcherManager searcherManager, string tenantId, string currentOwner, string scheme, string q, bool countOnly, bool includePrerelease, int skip, int take, bool includeExplanation)
        {
            IndexSearcher searcher = searcherManager.Get();

            try
            {
                Filter filter = searcherManager.GetFilter(tenantId, new string [] { "http://schema.nuget.org/schema#ApiAppPackage" }, includePrerelease, false);

                Query query = MakeQuery(q);

                TopDocs topDocs = searcher.Search(query, filter, skip + take);

                return(MakeResult(searcher, currentOwner, scheme, topDocs, skip, take, searcherManager, includeExplanation, query));
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        public static async Task Find(IOwinContext context, SecureSearcherManager searcherManager, string tenantId)
        {
            string id      = context.Request.Query["id"];
            string version = context.Request.Query["version"];

            HttpStatusCode statusCode;
            JToken         result;

            if (!string.IsNullOrEmpty(id))
            {
                if (string.IsNullOrEmpty(version))
                {
                    result = FindById(searcherManager, id, tenantId, context.Request.Uri.Scheme);
                }
                else
                {
                    result = FindByIdAndVersion(searcherManager, id, version, tenantId, context.Request.Uri.Scheme);
                }

                if (result == null)
                {
                    result = new JObject {
                        { "error", "not found" }
                    };
                    statusCode = HttpStatusCode.NotFound;
                }
                else
                {
                    statusCode = HttpStatusCode.OK;
                }
            }
            else
            {
                result = new JObject {
                    { "error", "id is a required parameter" }
                };
                statusCode = HttpStatusCode.BadRequest;
            }

            await ServiceHelpers.WriteResponse(context, statusCode, result);
        }
        public static async Task Query(IOwinContext context, SecureSearcherManager searcherManager, string tenantId, string currentOwner)
        {
            int skip;
            if (!int.TryParse(context.Request.Query["skip"], out skip))
            {
                skip = 0;
            }

            int take;
            if (!int.TryParse(context.Request.Query["take"], out take))
            {
                take = 50;
            }

            bool countOnly;
            if (!bool.TryParse(context.Request.Query["countOnly"], out countOnly))
            {
                countOnly = false;
            }

            bool includePrerelease;
            if (!bool.TryParse(context.Request.Query["prerelease"], out includePrerelease))
            {
                includePrerelease = false;
            }

            bool includeExplanation;
            if (!bool.TryParse(context.Request.Query["explanation"], out includeExplanation))
            {
                includeExplanation = false;
            }

            string q = context.Request.Query["q"] ?? string.Empty;

            string scheme = context.Request.Uri.Scheme;

            JToken result = Search(searcherManager, tenantId, currentOwner, scheme, q, countOnly, includePrerelease, skip, take, includeExplanation);

            await ServiceHelpers.WriteResponse(context, HttpStatusCode.OK, result);
        }
        static JToken FindByIdAndVersion(SecureSearcherManager searcherManager, string id, string version, string tenantId, string scheme)
        {
            IndexSearcher searcher = searcherManager.Get();

            try
            {
                string analyzedId      = id.ToLowerInvariant();
                string analyzedVersion = NuGetVersion.Parse(version).ToNormalizedString();

                BooleanQuery query = new BooleanQuery();
                query.Add(new BooleanClause(new TermQuery(new Term("FullId", analyzedId)), Occur.MUST));
                query.Add(new BooleanClause(new TermQuery(new Term("Version", analyzedVersion)), Occur.MUST));

                Filter filter = searcherManager.GetFilter(tenantId, new string [] { "http://schema.nuget.org/schema#ApiAppPackage" });

                TopDocs topDocs = searcher.Search(query, filter, 1);

                if (topDocs.TotalHits > 0)
                {
                    Uri     registrationBaseAddress = searcherManager.RegistrationBaseAddress[scheme];
                    JObject obj = new JObject();
                    obj["registration"] = new Uri(registrationBaseAddress, string.Format("{0}/index.json", id.ToLowerInvariant())).AbsoluteUri;

                    Document document = searcher.Doc(topDocs.ScoreDocs[0].Doc);
                    ServiceHelpers.AddField(obj, document, "packageContent", "PackageContent");
                    ServiceHelpers.AddField(obj, document, "catalogEntry", "CatalogEntry");

                    return(obj);
                }
                else
                {
                    return(null);
                }
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        static JToken FindByIdAndVersion(SecureSearcherManager searcherManager, string id, string version, string tenantId, string scheme)
        {
            IndexSearcher searcher = searcherManager.Get();
            try
            {
                string analyzedId = id.ToLowerInvariant();
                string analyzedVersion = NuGetVersion.Parse(version).ToNormalizedString();

                BooleanQuery query = new BooleanQuery();
                query.Add(new BooleanClause(new TermQuery(new Term("FullId", analyzedId)), Occur.MUST));
                query.Add(new BooleanClause(new TermQuery(new Term("Version", analyzedVersion)), Occur.MUST));

                Filter filter = searcherManager.GetFilter(tenantId, new string [] { "http://schema.nuget.org/schema#ApiAppPackage" });

                TopDocs topDocs = searcher.Search(query, filter, 1);

                if (topDocs.TotalHits > 0)
                {
                    Uri registrationBaseAddress = searcherManager.RegistrationBaseAddress[scheme];
                    JObject obj = new JObject();
                    obj["registration"] = new Uri(registrationBaseAddress, string.Format("{0}/index.json", id.ToLowerInvariant())).AbsoluteUri;

                    Document document = searcher.Doc(topDocs.ScoreDocs[0].Doc);
                    ServiceHelpers.AddField(obj, document, "packageContent", "PackageContent");
                    ServiceHelpers.AddField(obj, document, "catalogEntry", "CatalogEntry");

                    return obj;
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        public static async Task Find(IOwinContext context, SecureSearcherManager searcherManager, string tenantId)
        {
            string id = context.Request.Query["id"];
            string version = context.Request.Query["version"];

            HttpStatusCode statusCode;
            JToken result;
            if (!string.IsNullOrEmpty(id))
            {
                if (string.IsNullOrEmpty(version))
                {
                    result = FindById(searcherManager, id, tenantId, context.Request.Uri.Scheme);
                }
                else
                {
                    result = FindByIdAndVersion(searcherManager, id, version, tenantId, context.Request.Uri.Scheme);
                }

                if (result == null)
                {
                    result = new JObject { { "error", "not found" } };
                    statusCode = HttpStatusCode.NotFound;
                }
                else
                {
                    statusCode = HttpStatusCode.OK;
                }
            }
            else
            {
                result = new JObject { { "error", "id is a required parameter" } };
                statusCode = HttpStatusCode.BadRequest;
            }

            await ServiceHelpers.WriteResponse(context, statusCode, result);
        }
        public static JToken MakeResultData(IndexSearcher searcher, string currentOwner, string scheme, TopDocs topDocs, int skip, int take, SecureSearcherManager searcherManager, bool includeExplanation, Query query)
        {
            Uri registrationBaseAddress = searcherManager.RegistrationBaseAddress[scheme];

            JArray array = new JArray();

            for (int i = skip; i < Math.Min(skip + take, topDocs.ScoreDocs.Length); i++)
            {
                ScoreDoc scoreDoc = topDocs.ScoreDocs[i];

                Document document = searcher.Doc(scoreDoc.Doc);

                string url     = document.Get("Url");
                string id      = document.Get("Id");
                string version = document.Get("Version");
                string owner   = document.Get("Owner");

                string ns = document.Get("Namespace");
                if (ns != null)
                {
                    id = string.Format("{0}.{1}", ns, id);
                }

                JObject obj = new JObject();
                obj["@id"]          = new Uri(registrationBaseAddress, url).AbsoluteUri;
                obj["@type"]        = document.Get("@type");
                obj["registration"] = new Uri(registrationBaseAddress, string.Format("{0}/index.json", id.ToLowerInvariant())).AbsoluteUri;
                obj["id"]           = id;

                obj["isOwner"] = (owner == currentOwner);

                ServiceHelpers.AddField(obj, document, "packageContent", "PackageContent");
                ServiceHelpers.AddField(obj, document, "catalogEntry", "CatalogEntry");

                ServiceHelpers.AddFieldBool(obj, document, "listed", "Listed");

                ServiceHelpers.AddField(obj, document, "tenantId", "TenantId");
                ServiceHelpers.AddField(obj, document, "namespace", "Namespace");
                ServiceHelpers.AddField(obj, document, "visibility", "Visibility");
                ServiceHelpers.AddField(obj, document, "description", "Description");
                ServiceHelpers.AddField(obj, document, "summary", "Summary");
                ServiceHelpers.AddField(obj, document, "title", "Title");
                ServiceHelpers.AddField(obj, document, "iconUrl", "IconUrl");
                ServiceHelpers.AddFieldAsObject(obj, document, "owner", "OwnerDetails");
                ServiceHelpers.AddFieldAsArray(obj, document, "tags", "Tags");
                ServiceHelpers.AddFieldAsArray(obj, document, "authors", "Authors");

                obj["version"]  = version;
                obj["versions"] = searcherManager.GetVersions(scheme, scoreDoc.Doc);

                if (includeExplanation)
                {
                    Explanation explanation = searcher.Explain(query, scoreDoc.Doc);
                    obj["explanation"] = explanation.ToString();
                }

                array.Add(obj);
            }

            return(array);
        }
        static JToken MakeResult(IndexSearcher searcher, string currentOwner, string scheme, TopDocs topDocs, int skip, int take, SecureSearcherManager searcherManager, bool includeExplanation, Query query)
        {
            JToken data = MakeResultData(searcher, currentOwner, scheme, topDocs, skip, take, searcherManager, includeExplanation, query);

            JObject result = new JObject();

            result.Add("@context", new JObject {
                { "@vocab", "http://schema.nuget.org/schema#" }
            });
            result.Add("totalHits", topDocs.TotalHits);
            result.Add("lastReopen", searcherManager.LastReopen.ToString("o"));
            result.Add("index", searcherManager.IndexName);
            result.Add("data", data);
            result.Add("currentOwner", currentOwner);

            return(result);
        }
        public static JToken MakeResultData(IndexSearcher searcher, string currentOwner, string scheme, TopDocs topDocs, int skip, int take, SecureSearcherManager searcherManager, bool includeExplanation, Query query)
        {
            Uri registrationBaseAddress = searcherManager.RegistrationBaseAddress[scheme];

            JArray array = new JArray();

            for (int i = skip; i < Math.Min(skip + take, topDocs.ScoreDocs.Length); i++)
            {
                ScoreDoc scoreDoc = topDocs.ScoreDocs[i];

                Document document = searcher.Doc(scoreDoc.Doc);

                string url = document.Get("Url");
                string id = document.Get("Id");
                string version = document.Get("Version");
                string owner = document.Get("Owner");

                string ns = document.Get("Namespace");
                if (ns != null)
                {
                    id = string.Format("{0}.{1}", ns, id);
                }

                JObject obj = new JObject();
                obj["@id"] = new Uri(registrationBaseAddress, url).AbsoluteUri;
                obj["@type"] = document.Get("@type");
                obj["registration"] = new Uri(registrationBaseAddress, string.Format("{0}/index.json", id.ToLowerInvariant())).AbsoluteUri;
                obj["id"] = id;

                obj["isOwner"] = (owner == currentOwner);

                ServiceHelpers.AddField(obj, document, "packageContent", "PackageContent");
                ServiceHelpers.AddField(obj, document, "catalogEntry", "CatalogEntry");

                ServiceHelpers.AddFieldBool(obj, document, "listed", "Listed");

                ServiceHelpers.AddField(obj, document, "tenantId", "TenantId");
                ServiceHelpers.AddField(obj, document, "namespace", "Namespace");
                ServiceHelpers.AddField(obj, document, "visibility", "Visibility");
                ServiceHelpers.AddField(obj, document, "description", "Description");
                ServiceHelpers.AddField(obj, document, "summary", "Summary");
                ServiceHelpers.AddField(obj, document, "title", "Title");
                ServiceHelpers.AddField(obj, document, "iconUrl", "IconUrl");
                ServiceHelpers.AddField(obj, document, "homepage", "Homepage");
                ServiceHelpers.AddField(obj, document, "licenseUrl", "LicenseUrl");
                ServiceHelpers.AddField(obj, document, "projectUrl", "ProjectUrl");
                ServiceHelpers.AddFieldAsObject(obj, document, "license", "LicenseDetails");
                ServiceHelpers.AddFieldAsObject(obj, document, "owner", "OwnerDetails");
                ServiceHelpers.AddFieldAsArray(obj, document, "tags", "Tags");
                ServiceHelpers.AddFieldAsArray(obj, document, "authors", "Authors");
                ServiceHelpers.AddFieldAsArray(obj, document, "categories", "Categories");

                obj["version"] = version;
                obj["versions"] = searcherManager.GetVersions(scheme, scoreDoc.Doc);

                if (includeExplanation)
                {
                    Explanation explanation = searcher.Explain(query, scoreDoc.Doc);
                    obj["explanation"] = explanation.ToString();
                }

                array.Add(obj);
            }

            return array;
        }
예제 #17
0
        public void Configuration(IAppBuilder app)
        {
            app.UseErrorPage();

            //  search test console

            app.Use(async (context, next) =>
            {
                if (String.Equals(context.Request.Path.Value, "/console", StringComparison.OrdinalIgnoreCase))
                {
                    // Redirect to trailing slash to maintain relative links
                    context.Response.Redirect(context.Request.PathBase + context.Request.Path + "/");
                    context.Response.StatusCode = 301;
                    return;
                }
                else if (String.Equals(context.Request.Path.Value, "/console/", StringComparison.OrdinalIgnoreCase))
                {
                    context.Request.Path = new PathString("/console/Index.html");
                }
                await next();
            });

            app.UseStaticFiles(new StaticFileOptions(new SharedOptions
            {
                RequestPath = new PathString("/console"),
                FileSystem = new EmbeddedResourceFileSystem(typeof(Startup).Assembly, "NuGet.Services.Metadata.Console")
            }));

            //  AAD integration - adding this middleware gives us the claims

            string audience = _configurationService.Get("ida.Audience");
            string tenant = _configurationService.Get("ida.Tenant");
            string aadInstance = _configurationService.Get("ida.AADInstance");

            string metadataAddress = string.Format(aadInstance, tenant) + "/federationmetadata/2007-06/federationmetadata.xml";

            app.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidAudience = audience,
                    ValidateIssuer = true,
                    IssuerValidator = (string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) => issuer
                },
                Tenant = tenant,
                MetadataAddress = metadataAddress
            });

            //  start the service running - the Lucene index needs to be reopened regularly on a background thread

            string searchIndexRefresh = _configurationService.Get("Search.IndexRefresh") ?? "15";
            int seconds;
            if (!int.TryParse(searchIndexRefresh, out seconds))
            {
                seconds = 60;
            }

            _searcherManager = null;

            _gate = 0;
            _timer = new Timer(ReopenCallback, 0, 10, seconds * 1000);

            app.Run(Invoke);
        }
예제 #18
0
 public void TryMaybeReopen()
 {
     try
     {
         _searcherManager.MaybeReopen();
     }
     catch (StorageException)
     {
         _searcherManager = null;
     }
 }
예제 #19
0
 public void TrySetSearcherManager()
 {
     try
     {
         _searcherManager = CreateSearcherManager();
         _searcherManager.Open();
     }
     catch (FileNotFoundException)
     {
         _searcherManager = null;
     }
 }
        static JToken FindById(SecureSearcherManager searcherManager, string id, string tenantId, string scheme)
        {
            IndexSearcher searcher = searcherManager.Get();
            try
            {
                string analyzedId = id.ToLowerInvariant();

                BooleanQuery query = new BooleanQuery();
                query.Add(new BooleanClause(new TermQuery(new Term("FullId", analyzedId)), Occur.MUST));

                Filter filter = searcherManager.GetFilter(tenantId, new string [] { "http://schema.nuget.org/schema#ApiAppPackage" });

                TopDocs topDocs = searcher.Search(query, 1000);

                if (topDocs.TotalHits > 0)
                {
                    Uri registrationBaseAddress = searcherManager.RegistrationBaseAddress[scheme];
                    JObject registrationObj = new JObject();
                    string registrationRelativeAddress = string.Format("{0}/index.json", id.ToLowerInvariant());
                    registrationObj["registration"] = new Uri(registrationBaseAddress, registrationRelativeAddress).AbsoluteUri;

                    JArray data = new JArray();
                    for (int i = 0; i < topDocs.ScoreDocs.Length; i++)
                    {
                        Document document = searcher.Doc(topDocs.ScoreDocs[i].Doc);

                        JObject versionObj = new JObject();
                        ServiceHelpers.AddField(versionObj, document, "version", "Version");
                        ServiceHelpers.AddField(versionObj, document, "packageContent", "PackageContent");
                        ServiceHelpers.AddField(versionObj, document, "catalogEntry", "CatalogEntry");

                        data.Add(versionObj);
                    }

                    registrationObj["data"] = data;

                    return registrationObj;
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                searcherManager.Release(searcher);
            }
        }
        static JToken MakeResult(IndexSearcher searcher, string currentOwner, string scheme, TopDocs topDocs, int skip, int take, SecureSearcherManager searcherManager, bool includeExplanation, Query query)
        {
            JToken data = MakeResultData(searcher, currentOwner, scheme, topDocs, skip, take, searcherManager, includeExplanation, query);

            JObject result = new JObject();

            result.Add("@context", new JObject { { "@vocab", "http://schema.nuget.org/schema#" } });
            result.Add("totalHits", topDocs.TotalHits);
            result.Add("lastReopen", searcherManager.LastReopen.ToString("o"));
            result.Add("index", searcherManager.IndexName);
            result.Add("data", data);
            result.Add("currentOwner", currentOwner);

            return result;
        }