public async Task <AutocompleteResponse> AutocompleteAsync(AutocompleteRequest request)
        {
            var operation = _operationBuilder.Autocomplete(request);

            AutocompleteResponse output;

            switch (operation.Type)
            {
            case IndexOperationType.Search:
                var result = await Measure.DurationWithValueAsync(() => _searchIndex.Documents.SearchAsync <SearchDocument.Full>(
                                                                      operation.SearchText,
                                                                      operation.SearchParameters));

                output = _responseBuilder.AutocompleteFromSearch(
                    request,
                    operation.SearchText,
                    operation.SearchParameters,
                    result.Value,
                    result.Duration);

                _telemetryService.TrackAutocompleteQuery(result.Duration);
                break;

            case IndexOperationType.Empty:
                output = _responseBuilder.EmptyAutocomplete(request);
                break;

            default:
                throw UnsupportedOperation(operation);
            }

            return(output);
        }
        public SearchParameters Autocomplete(AutocompleteRequest request, bool isDefaultSearch)
        {
            var searchParameters = NewSearchParameters();

            ApplySearchIndexFilter(searchParameters, request, isDefaultSearch, request.PackageType);

            switch (request.Type)
            {
            case AutocompleteRequestType.PackageIds:
                searchParameters.Select = PackageIdsAutocompleteSelect;
                ApplyPaging(searchParameters, request);
                break;

            // Package version autocomplete should only match a single document
            // regardless of the request's parameters.
            case AutocompleteRequestType.PackageVersions:
                searchParameters.Select = PackageVersionsAutocompleteSelect;
                searchParameters.Skip   = 0;
                searchParameters.Top    = 1;
                break;

            default:
                throw new InvalidOperationException($"Unknown autocomplete request type '{request.Type}'");
            }

            return(searchParameters);
        }
Beispiel #3
0
 public AutocompleteResponse EmptyAutocomplete(AutocompleteRequest request)
 {
     return(new AutocompleteResponse
     {
         Context = GetAutocompleteContext(),
         TotalHits = 0,
         Data = new List <string>(),
         Debug = DebugInformation.CreateFromEmptyOrNull(request),
     });
 }
        public SearchText Autocomplete(AutocompleteRequest request)
        {
            if (string.IsNullOrWhiteSpace(request.Query))
            {
                return(GetMatchAllDocuments(request.IncludeTestData));
            }

            // Query package ids. If autocompleting package ids, allow prefix matches.
            var builder = new AzureSearchTextBuilder();

            if (request.Type == AutocompleteRequestType.PackageIds)
            {
                var trimmedQuery = request.Query.Trim();

                builder.AppendTerm(
                    fieldName: IndexFields.PackageId,
                    term: trimmedQuery,
                    prefixSearch: true);

                var pieces = trimmedQuery.Split(PackageIdSeparators);
                foreach (var piece in pieces)
                {
                    if (string.IsNullOrWhiteSpace(piece))
                    {
                        continue;
                    }

                    builder.AppendTerm(
                        fieldName: IndexFields.TokenizedPackageId,
                        term: piece,
                        op: Operator.Required,
                        prefixSearch: true);
                }

                if (IsId(trimmedQuery))
                {
                    builder.AppendExactMatchPackageIdBoost(trimmedQuery, _options.Value.ExactMatchBoost);
                }
            }
            else
            {
                builder.AppendTerm(
                    fieldName: IndexFields.PackageId,
                    term: request.Query,
                    prefixSearch: false);
            }

            if (!request.IncludeTestData)
            {
                ExcludeTestData(builder);
            }

            return(new SearchText(builder.ToString(), isDefaultSearch: false));
        }
            public void PackageTypeFiltering(string packageType)
            {
                var request = new AutocompleteRequest
                {
                    PackageType = packageType,
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal($"searchFilters eq 'Default' and filterablePackageTypes/any(p: p eq '{packageType.ToLowerInvariant()}')", output.Filter);
            }
            public void InvalidPackageType()
            {
                var request = new AutocompleteRequest
                {
                    PackageType = "something's-weird",
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal("searchFilters eq 'Default'", output.Filter);
            }
            public void TooLargeTake()
            {
                var request = new AutocompleteRequest
                {
                    Type = AutocompleteRequestType.PackageIds,
                    Take = 1001,
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal(20, output.Top);
            }
            public void NegativeSkip()
            {
                var request = new AutocompleteRequest
                {
                    Skip = -10,
                    Type = AutocompleteRequestType.PackageIds,
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal(0, output.Skip);
            }
        public IndexOperation Autocomplete(AutocompleteRequest request)
        {
            if (HasInvalidParameters(request, request.PackageType))
            {
                return(IndexOperation.Empty());
            }

            var text       = _textBuilder.Autocomplete(request);
            var parameters = _parametersBuilder.Autocomplete(request, text.IsDefaultSearch);

            return(IndexOperation.Search(text.Value, parameters));
        }
Beispiel #10
0
            public void PackageVersionsAutocomplete(string input, string expected)
            {
                var request = new AutocompleteRequest
                {
                    Query = input,
                    Type  = AutocompleteRequestType.PackageVersions
                };

                var actual = _target.Autocomplete(request);

                Assert.Equal(expected, actual);
            }
        public IndexOperation Autocomplete(AutocompleteRequest request)
        {
            if (HasInvalidParameters(request, request.PackageType))
            {
                return(IndexOperation.Empty());
            }

            var text       = _textBuilder.Autocomplete(request);
            var parameters = _parametersBuilder.Autocomplete(request, IsEmptySearchQuery(text));

            return(IndexOperation.Search(text, parameters));
        }
            public void SearchFilters(bool includePrerelease, bool includeSemVer2, string filter)
            {
                var request = new AutocompleteRequest
                {
                    IncludePrerelease = includePrerelease,
                    IncludeSemVer2    = includeSemVer2,
                    Query             = "js"
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal(filter, output.Filter);
            }
            public void PackageVersionsPaging()
            {
                var request = new AutocompleteRequest
                {
                    Skip = 10,
                    Take = 30,
                    Type = AutocompleteRequestType.PackageVersions,
                };

                var output = _target.Autocomplete(request, It.IsAny <bool>());

                Assert.Equal(0, output.Skip);
                Assert.Equal(1, output.Top);
            }
Beispiel #14
0
        public AutocompleteResponse AutocompleteFromSearch(
            AutocompleteRequest request,
            string text,
            SearchParameters parameters,
            DocumentSearchResult <SearchDocument.Full> result,
            TimeSpan duration)
        {
            var results = result.Results;

            result.Results = null;

            List <string> data;

            switch (request.Type)
            {
            case AutocompleteRequestType.PackageIds:
                data = results.Select(x => x.Document.PackageId).ToList();
                break;

            case AutocompleteRequestType.PackageVersions:
                if (result.Count > 1 || results.Count > 1)
                {
                    throw new ArgumentException(
                              "Package version autocomplete queries should have a single document result",
                              nameof(result));
                }

                data = results.SelectMany(x => x.Document.Versions).ToList();
                break;

            default:
                throw new InvalidOperationException($"Unknown autocomplete request type '{request.Type}'");
            }

            return(new AutocompleteResponse
            {
                Context = GetAutocompleteContext(),
                TotalHits = result.Count.Value,
                Data = data,
                Debug = DebugInformation.CreateFromSearchOrNull(
                    request,
                    _options.Value.SearchIndexName,
                    parameters,
                    text,
                    result,
                    duration,
                    auxiliaryFilesMetadata: null),
            });
        }
Beispiel #15
0
            public void PackageVersionsCanExcludeTestData(string query, bool includeTestData, string expected)
            {
                _config.TestOwners = new List <string> {
                    "TestUserA", "TestUserB"
                };
                var request = new AutocompleteRequest
                {
                    Query           = query,
                    Type            = AutocompleteRequestType.PackageVersions,
                    IncludeTestData = includeTestData,
                };

                var actual = _target.Autocomplete(request);

                Assert.Equal(expected, actual.Value);
            }
            public void PackageVersionsDefaults()
            {
                var request = new AutocompleteRequest();

                request.Type = AutocompleteRequestType.PackageVersions;

                var output = _target.Autocomplete(request, isDefaultSearch: true);

                Assert.Equal(QueryType.Full, output.QueryType);
                Assert.True(output.IncludeTotalResultCount);
                Assert.Equal(DefaultOrderBy, output.OrderBy.ToArray());
                Assert.Equal(0, output.Skip);
                Assert.Equal(1, output.Top);
                Assert.Equal("searchFilters eq 'Default' and (isExcludedByDefault eq false or isExcludedByDefault eq null)", output.Filter);
                Assert.Single(output.Select);
                Assert.Equal(IndexFields.Search.Versions, output.Select[0]);
            }
            public Facts()
            {
                TextBuilder       = new Mock <ISearchTextBuilder>();
                ParametersBuilder = new Mock <ISearchParametersBuilder>();

                AutocompleteRequest = new AutocompleteRequest {
                    Skip = 0, Take = 20
                };
                V2SearchRequest = new V2SearchRequest {
                    Skip = 0, Take = 20
                };
                V3SearchRequest = new V3SearchRequest {
                    Skip = 0, Take = 20
                };
                Text        = "";
                Parameters  = new SearchParameters();
                ParsedQuery = new ParsedQuery(new Dictionary <QueryField, HashSet <string> >());

                TextBuilder
                .Setup(x => x.Autocomplete(It.IsAny <AutocompleteRequest>()))
                .Returns(() => Text);
                TextBuilder
                .Setup(x => x.ParseV2Search(It.IsAny <V2SearchRequest>()))
                .Returns(() => ParsedQuery);
                TextBuilder
                .Setup(x => x.ParseV3Search(It.IsAny <V3SearchRequest>()))
                .Returns(() => ParsedQuery);
                TextBuilder
                .Setup(x => x.Build(It.IsAny <ParsedQuery>()))
                .Returns(() => Text);
                ParametersBuilder
                .Setup(x => x.Autocomplete(It.IsAny <AutocompleteRequest>(), It.IsAny <bool>()))
                .Returns(() => Parameters);
                ParametersBuilder
                .Setup(x => x.V2Search(It.IsAny <V2SearchRequest>(), It.IsAny <bool>()))
                .Returns(() => Parameters);
                ParametersBuilder
                .Setup(x => x.V3Search(It.IsAny <V3SearchRequest>(), It.IsAny <bool>()))
                .Returns(() => Parameters);

                Target = new IndexOperationBuilder(
                    TextBuilder.Object,
                    ParametersBuilder.Object);
            }
Beispiel #18
0
            public BaseFacts()
            {
                _auxiliaryData = new Mock <IAuxiliaryData>();
                _config        = new SearchServiceConfiguration();
                _options       = new Mock <IOptionsSnapshot <SearchServiceConfiguration> >();
                _options.Setup(x => x.Value).Returns(() => _config);
                _auxiliaryMetadata = new AuxiliaryFilesMetadata(
                    new DateTimeOffset(2019, 1, 3, 11, 0, 0, TimeSpan.Zero),
                    new AuxiliaryFileMetadata(
                        new DateTimeOffset(2019, 1, 1, 11, 0, 0, TimeSpan.Zero),
                        TimeSpan.FromSeconds(15),
                        1234,
                        "\"etag-a\""),
                    new AuxiliaryFileMetadata(
                        new DateTimeOffset(2019, 1, 2, 11, 0, 0, TimeSpan.Zero),
                        TimeSpan.FromSeconds(30),
                        5678,
                        "\"etag-b\""));

                _config.SearchIndexName             = "search-index";
                _config.HijackIndexName             = "hijack-index";
                _config.SemVer1RegistrationsBaseUrl = "https://example/reg/";
                _config.SemVer2RegistrationsBaseUrl = "https://example/reg-gz-semver2/";
                _config.FlatContainerBaseUrl        = Data.FlatContainerBaseUrl;
                _config.FlatContainerContainerName  = Data.FlatContainerContainerName;

                _auxiliaryData
                .Setup(x => x.GetTotalDownloadCount(It.IsAny <string>()))
                .Returns(Data.TotalDownloadCount);
                _auxiliaryData
                .Setup(x => x.GetDownloadCount(It.IsAny <string>(), It.IsAny <string>()))
                .Returns(23);
                _auxiliaryData
                .Setup(x => x.IsVerified(It.IsAny <string>()))
                .Returns(true);
                _auxiliaryData
                .Setup(x => x.Metadata)
                .Returns(() => _auxiliaryMetadata);

                _v2Request = new V2SearchRequest
                {
                    IncludePrerelease = true,
                    IncludeSemVer2    = true,
                };
                _v3Request = new V3SearchRequest
                {
                    IncludePrerelease = true,
                    IncludeSemVer2    = true
                };
                _autocompleteRequest = new AutocompleteRequest
                {
                    IncludePrerelease = true,
                    IncludeSemVer2    = true,
                };
                _searchParameters  = new SearchParameters();
                _text              = "azure storage sdk";
                _duration          = TimeSpan.FromMilliseconds(250);
                _emptySearchResult = new DocumentSearchResult <SearchDocument.Full>
                {
                    Count   = 0,
                    Results = new List <SearchResult <SearchDocument.Full> >(),
                };
                _searchResult = new DocumentSearchResult <SearchDocument.Full>
                {
                    Count   = 1,
                    Results = new List <SearchResult <SearchDocument.Full> >
                    {
                        new SearchResult <SearchDocument.Full>
                        {
                            Document = Data.SearchDocument,
                        },
                    },
                };
                _manySearchResults = new DocumentSearchResult <SearchDocument.Full>
                {
                    Count   = 2,
                    Results = new List <SearchResult <SearchDocument.Full> >
                    {
                        new SearchResult <SearchDocument.Full>
                        {
                            Document = Data.SearchDocument,
                        },
                        new SearchResult <SearchDocument.Full>
                        {
                            Document = Data.SearchDocument,
                        },
                    },
                };
                _hijackResult = new DocumentSearchResult <HijackDocument.Full>
                {
                    Count   = 1,
                    Results = new List <SearchResult <HijackDocument.Full> >
                    {
                        new SearchResult <HijackDocument.Full>
                        {
                            Document = Data.HijackDocument,
                        },
                    },
                };

                _jsonSerializerSettings = new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore,
                    Converters        =
                    {
                        new StringEnumConverter(),
                    },
                    Formatting = Formatting.Indented,
                };
            }
            public BaseFacts()
            {
                _operationBuilder = new Mock <IIndexOperationBuilder>();
                _searchIndex      = new Mock <ISearchIndexClientWrapper>();
                _searchOperations = new Mock <IDocumentsOperationsWrapper>();
                _hijackIndex      = new Mock <ISearchIndexClientWrapper>();
                _hijackOperations = new Mock <IDocumentsOperationsWrapper>();
                _responseBuilder  = new Mock <ISearchResponseBuilder>();
                _telemetryService = new Mock <IAzureSearchTelemetryService>();

                _v2Request           = new V2SearchRequest();
                _v3Request           = new V3SearchRequest();
                _autocompleteRequest = new AutocompleteRequest();
                _key                  = "key";
                _text                 = "search";
                _parameters           = new SearchParameters();
                _operation            = IndexOperation.Search(_text, _parameters);
                _searchResult         = new DocumentSearchResult <SearchDocument.Full>();
                _searchDocument       = new SearchDocument.Full();
                _hijackResult         = new DocumentSearchResult <HijackDocument.Full>();
                _hijackDocument       = new HijackDocument.Full();
                _v2Response           = new V2SearchResponse();
                _v3Response           = new V3SearchResponse();
                _autocompleteResponse = new AutocompleteResponse();

                _operationBuilder
                .Setup(x => x.V2SearchWithHijackIndex(It.IsAny <V2SearchRequest>()))
                .Returns(() => _operation);
                _operationBuilder
                .Setup(x => x.V2SearchWithSearchIndex(It.IsAny <V2SearchRequest>()))
                .Returns(() => _operation);
                _operationBuilder
                .Setup(x => x.V3Search(It.IsAny <V3SearchRequest>()))
                .Returns(() => _operation);
                _operationBuilder
                .Setup(x => x.Autocomplete(It.IsAny <AutocompleteRequest>()))
                .Returns(() => _operation);

                // Set up the search to take at least one millisecond. This allows us to test the measured duration.
                _searchOperations
                .Setup(x => x.SearchAsync <SearchDocument.Full>(It.IsAny <string>(), It.IsAny <SearchParameters>()))
                .ReturnsAsync(() => _searchResult)
                .Callback(() => Thread.Sleep(TimeSpan.FromMilliseconds(1)));
                _searchOperations
                .Setup(x => x.GetOrNullAsync <SearchDocument.Full>(It.IsAny <string>()))
                .ReturnsAsync(() => _searchDocument)
                .Callback(() => Thread.Sleep(TimeSpan.FromMilliseconds(1)));
                _hijackOperations
                .Setup(x => x.SearchAsync <HijackDocument.Full>(It.IsAny <string>(), It.IsAny <SearchParameters>()))
                .ReturnsAsync(() => _hijackResult)
                .Callback(() => Thread.Sleep(TimeSpan.FromMilliseconds(1)));
                _hijackOperations
                .Setup(x => x.GetOrNullAsync <HijackDocument.Full>(It.IsAny <string>()))
                .ReturnsAsync(() => _hijackDocument)
                .Callback(() => Thread.Sleep(TimeSpan.FromMilliseconds(1)));

                _responseBuilder
                .Setup(x => x.V2FromHijack(
                           It.IsAny <V2SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchParameters>(),
                           It.IsAny <DocumentSearchResult <HijackDocument.Full> >(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v2Response);
                _responseBuilder
                .Setup(x => x.V2FromHijackDocument(
                           It.IsAny <V2SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <HijackDocument.Full>(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v2Response);
                _responseBuilder
                .Setup(x => x.V2FromSearch(
                           It.IsAny <V2SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchParameters>(),
                           It.IsAny <DocumentSearchResult <SearchDocument.Full> >(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v2Response);
                _responseBuilder
                .Setup(x => x.V2FromSearchDocument(
                           It.IsAny <V2SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchDocument.Full>(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v2Response);
                _responseBuilder
                .Setup(x => x.V3FromSearch(
                           It.IsAny <V3SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchParameters>(),
                           It.IsAny <DocumentSearchResult <SearchDocument.Full> >(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v3Response);
                _responseBuilder
                .Setup(x => x.V3FromSearchDocument(
                           It.IsAny <V3SearchRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchDocument.Full>(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _v3Response);
                _responseBuilder
                .Setup(x => x.AutocompleteFromSearch(
                           It.IsAny <AutocompleteRequest>(),
                           It.IsAny <string>(),
                           It.IsAny <SearchParameters>(),
                           It.IsAny <DocumentSearchResult <SearchDocument.Full> >(),
                           It.IsAny <TimeSpan>()))
                .Returns(() => _autocompleteResponse);
                _responseBuilder
                .Setup(x => x.EmptyV2(It.IsAny <V2SearchRequest>()))
                .Returns(() => _v2Response);
                _responseBuilder
                .Setup(x => x.EmptyV3(It.IsAny <V3SearchRequest>()))
                .Returns(() => _v3Response);
                _responseBuilder
                .Setup(x => x.EmptyAutocomplete(It.IsAny <AutocompleteRequest>()))
                .Returns(() => _autocompleteResponse);

                _searchIndex.Setup(x => x.Documents).Returns(() => _searchOperations.Object);
                _hijackIndex.Setup(x => x.Documents).Returns(() => _hijackOperations.Object);

                _target = new AzureSearchService(
                    _operationBuilder.Object,
                    _searchIndex.Object,
                    _hijackIndex.Object,
                    _responseBuilder.Object,
                    _telemetryService.Object);
            }