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); }
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)); }
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); }
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), }); }
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); }
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); }