public async Task GetDownloadDetails_Returns_Correct_Data() { var key = "*"; var searchResult = new DocumentSearchResult <DownloadDetailsModel>( PopulateDownloadsData(), 1, 0, null, SearchContinuationToken.CreateTestToken("foo")); var clientMock = new Mock <IDownloadsIndexService>(); var azureServiceMock = new Mock <IAzureService>(); var searchTermFormattingServiceMock = new Mock <ISearchTermFormattingService>(); azureServiceMock .Setup(m => m.SearchIndexAsync <DownloadDetailsModel>(clientMock.Object, key, It.IsAny <SearchParameters>())) .ReturnsAsync(searchResult); var mapper = new AzureDownloadDataMapper(); var service = new AzureDownloadsService(azureServiceMock.Object, mapper, clientMock.Object, searchTermFormattingServiceMock.Object); var result = (await service.GetDownloadDetails(key)).ToList(); result.Count.Should().Be(6); result[0].Id.Should().Be("4"); result[1].Id.Should().Be("8"); result[2].Id.Should().Be("12"); result[3].Id.Should().Be("2"); result[4].Id.Should().Be("6"); result[5].Id.Should().Be("10"); }
public void CanDeserializeTokenWithOnlyLink() { SearchContinuationToken token = JsonConvert.DeserializeObject <SearchContinuationToken>(TokenWithOnlyLinkJson); Assert.Equal(_tokenWithOnlyLink, token, _tokenComparer); }
private bool ValidateAndTraceContinueSearch( SearchContinuationToken continuationToken, out string invocationId) { // Validate if (continuationToken == null) { throw new ArgumentNullException("continuationToken"); } // Tracing bool shouldTrace = TracingAdapter.IsEnabled; invocationId = null; if (shouldTrace) { invocationId = TracingAdapter.NextInvocationId.ToString(); Dictionary <string, object> tracingParameters = new Dictionary <string, object>(); tracingParameters.Add("continuationToken", continuationToken); TracingAdapter.Enter(invocationId, this, "ContinueSearchAsync", tracingParameters); } return(shouldTrace); }
public async Task GetLarsLearningDeliveries_Returns_Valid_Aims() { var learnAimRef = "12345678"; var azureLearningAim = new LearningAimModel { LearnAimRef = learnAimRef }; var apiLearningAim = new Models.LearningAimModel { LearnAimRef = learnAimRef }; var searchModel = new LearningAimsSearchModel { SearchTerm = learnAimRef }; var searchResult = new DocumentSearchResult <LearningAimModel>( new List <SearchResult <LearningAimModel> > { new SearchResult <LearningAimModel>(azureLearningAim) }, 1, 0, null, SearchContinuationToken.CreateTestToken("foo")); var mapperMock = new Mock <IMapper <LearningAimModel, Models.LearningAimModel> >(); mapperMock.Setup(m => m.Map(azureLearningAim)).Returns(apiLearningAim); var indexServiceMock = new Mock <ILearningDeliveryIndexService>(); var azureServiceMock = new Mock <IAzureService>(); azureServiceMock .Setup(m => m.SearchIndexAsync <LearningAimModel>(indexServiceMock.Object, learnAimRef, It.IsAny <SearchParameters>())) .ReturnsAsync(searchResult); var queryServiceMock = new Mock <IODataQueryService>(); var searchTermFormattingServiceMock = new Mock <ISearchTermFormattingService>(); searchTermFormattingServiceMock .Setup(f => f.FormatSearchTerm(learnAimRef)) .Returns(learnAimRef); var service = new AzureLearningAimsService( mapperMock.Object, indexServiceMock.Object, queryServiceMock.Object, azureServiceMock.Object, searchTermFormattingServiceMock.Object); var result = (await service.GetLarsLearningDeliveries(searchModel)).ToList(); azureServiceMock.Verify(m => m.SearchIndexAsync <LearningAimModel>(indexServiceMock.Object, learnAimRef, It.IsAny <SearchParameters>()), Times.Once); result.Should().HaveCount(1); result.Single().Should().BeSameAs(apiLearningAim); }
private Task <AzureOperationResponse <DocumentSearchResult <T> > > DoContinueSearchAsync <T>( SearchContinuationToken continuationToken, SearchRequestOptions searchRequestOptions, Dictionary <string, List <string> > customHeaders, CancellationToken cancellationToken, JsonSerializerSettings deserializerSettings) { // Validate if (Client.ApiVersion == null) { throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ApiVersion"); } Throw.IfArgumentNull(continuationToken, nameof(continuationToken)); Guid?clientRequestId = searchRequestOptions?.ClientRequestId; // Tracing bool shouldTrace = ServiceClientTracing.IsEnabled; string invocationId = null; if (shouldTrace) { invocationId = ServiceClientTracing.NextInvocationId.ToString(); var tracingParameters = new Dictionary <string, object> { ["continuationToken"] = continuationToken, ["clientRequestId"] = clientRequestId, ["cancellationToken"] = cancellationToken }; ServiceClientTracing.Enter(invocationId, this, "ContinueSearch", tracingParameters); } bool useGet = continuationToken.NextPageParameters == null; if (useGet) { return(Client.DocumentsProxy.ContinueSearchGetWithHttpMessagesAsync <T>( continuationToken.NextLink, clientRequestId, EnsureCustomHeaders(customHeaders), shouldTrace, invocationId, cancellationToken, responseDeserializerSettings: deserializerSettings)); } else { return(Client.DocumentsProxy.ContinueSearchPostWithHttpMessagesAsync <T>( continuationToken.NextLink, continuationToken.NextPageParameters, clientRequestId, EnsureCustomHeaders(customHeaders), shouldTrace, invocationId, cancellationToken, responseDeserializerSettings: deserializerSettings)); } }
/// <summary> /// Get event list as per search and filter criteria. /// </summary> /// <param name="searchQuery">Query which the user had typed in Messaging Extension search field.</param> /// <param name="searchParameters">Search parameters for enhanced searching.</param> /// <returns>List of events.</returns> public async Task <IEnumerable <EventEntity> > GetEventsAsync( string searchQuery, SearchParameters searchParameters) { await this.EnsureInitializedAsync(); var postSearchResult = await this.searchIndexClient.Documents.SearchAsync <EventEntity>(searchQuery, searchParameters); SearchContinuationToken continuationToken = null; var events = new List <EventEntity>(); if (postSearchResult?.Results != null) { events.AddRange(postSearchResult.Results.Select(p => p.Document)); continuationToken = postSearchResult.ContinuationToken; } while (continuationToken != null) { var searchResult = await this.searchIndexClient.Documents.ContinueSearchAsync <EventEntity>(continuationToken); if (searchResult?.Results != null) { events.AddRange(searchResult.Results.Select(p => p.Document)); continuationToken = searchResult.ContinuationToken; } } return(events); }
/// <summary> /// Retrieves the next page of search results from the Azure Search index. (see /// <see href="https://msdn.microsoft.com/library/azure/dn798927.aspx"/> for more information) /// </summary> /// <param name='operations'> /// Reference to the Microsoft.Azure.Search.IDocumentOperations. /// </param> /// <param name="continuationToken"> /// Encapsulates the state required to fetch the next page of search results from the index. /// </param> /// <returns> /// Response containing the documents matching the query. /// </returns> /// <remarks> /// The non-generic overloads of the ContinueSearch and ContinueSearchAsync methods make a best-effort attempt /// to map JSON types in the response payload to .NET types. See /// <see cref="IDocumentOperations.GetAsync(string, System.Collections.Generic.IEnumerable<string>, CancellationToken)"/> /// for more information. /// </remarks> public static DocumentSearchResponse ContinueSearch( this IDocumentOperations operations, SearchContinuationToken continuationToken) { return(Task.Factory.StartNew((object s) => { return ((IDocumentOperations)s).ContinueSearchAsync(continuationToken); } , operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult()); }
public async Task <SearchResults <T> > Search(string searchText, SearchParameters searchParameters = null, bool allResults = false) { ISearchIndexClient client = await GetOrCreateIndex(); try { searchText = ParseSearchText(searchText); DocumentSearchResult <T> azureSearchResult = await client.Documents.SearchAsync <T>(searchText, searchParameters ?? DefaultParameters); IEnumerable <SearchResult <T> > results = azureSearchResult.Results.Select(x => new SearchResult <T> { HitHighLights = x.Highlights, Result = x.Document, Score = x.Score }); SearchContinuationToken continuationToken = azureSearchResult.ContinuationToken; // only keep querying to return all items if we want all results to be returned while (allResults && continuationToken != null) { DocumentSearchResult <T> continuationResult = await client.Documents.ContinueSearchAsync <T>(continuationToken); results = results.Concat(continuationResult.Results.Select(x => new SearchResult <T> { HitHighLights = x.Highlights, Result = x.Document, Score = x.Score })); continuationToken = continuationResult.ContinuationToken; } var response = new SearchResults <T> { SearchTerm = searchText, TotalCount = azureSearchResult.Count, Facets = azureSearchResult.Facets?.Select(x => new Facet { Name = x.Key, FacetValues = x.Value.Where(f => !string.IsNullOrWhiteSpace(f.Value.ToString())).Select(m => new FacetValue { Name = m.Value.ToString(), Count = (int)(m.Count ?? 0) }) }).ToList(), Results = results.ToList() }; return(response); } catch (Exception ex) { throw new FailedToQuerySearchException("Failed to query search", ex); } }
public async Task <Result <T> > RunQuery <T>(string indexName, SearchParameters searchParameters, string key, SearchContinuationToken continuationToken, bool highScoring = false) where T : class { var results = new List <T>(); var queryResult = new Result <T>(); try { var searchIndexClient = searchServiceClient.Indexes.GetClient(indexName); var headers = new Dictionary <string, List <string> > { { "x-ms-azs-return-searchid", new List <string> { "true" } } }; AzureOperationResponse <DocumentSearchResult <T> > azureOperationsResponse; if (continuationToken != null) { azureOperationsResponse = await searchIndexClient.Documents.ContinueSearchWithHttpMessagesAsync <T>(continuationToken).ConfigureAwait(false); } else { azureOperationsResponse = await searchIndexClient.Documents.SearchWithHttpMessagesAsync <T>(key, searchParameters, customHeaders : headers).ConfigureAwait(false); } if (azureOperationsResponse.Response.IsSuccessStatusCode) { var searchResults = azureOperationsResponse.Body.Results; queryResult.Facet = azureOperationsResponse.Body.Facets; if (highScoring && searchResults.Any(i => i.Score > 2)) { results.AddRange(searchResults.Where(i => i.Score > 2).Select(i => i.Document)); } else { results.AddRange(searchResults.Select(i => i.Document)); } queryResult.ContinuationToken = azureOperationsResponse.Body.ContinuationToken; queryResult.Collection = results; } } catch (Exception ex) { } return(queryResult); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { SearchContinuationToken token = (SearchContinuationToken)value; var payload = new SearchContinuationTokenPayload() { NextLink = token.NextLink, NextPageParameters = token.NextPageParameters }; serializer.Serialize(writer, payload); }
public Task <DocumentSearchResponse <T> > ContinueSearchAsync <T>( SearchContinuationToken continuationToken, CancellationToken cancellationToken) where T : class { string invocationId; bool shouldTrace = ValidateAndTraceContinueSearch(continuationToken, out invocationId); return(DoContinueSearchAsync <DocumentSearchResponse <T>, SearchResult <T>, T>( continuationToken.NextLink, shouldTrace, invocationId, cancellationToken, DeserializeForSearch <T>)); }
public Task <DocumentSearchResponse> ContinueSearchAsync( SearchContinuationToken continuationToken, CancellationToken cancellationToken) { string invocationId; bool shouldTrace = ValidateAndTraceContinueSearch(continuationToken, out invocationId); return(DoContinueSearchAsync <DocumentSearchResponse, SearchResult, Document>( continuationToken.NextLink, shouldTrace, invocationId, cancellationToken, DeserializeForSearch)); }
public Task <AzureOperationResponse <DocumentSearchResult <Document> > > ContinueSearchWithHttpMessagesAsync( SearchContinuationToken continuationToken, SearchRequestOptions searchRequestOptions = default(SearchRequestOptions), Dictionary <string, List <string> > customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) { var deserializerSettings = JsonUtility.CreateDocumentDeserializerSettings(Client.DeserializationSettings); return(DoContinueSearchAsync <Document>( continuationToken, searchRequestOptions, customHeaders, cancellationToken, deserializerSettings)); }
/// <summary> /// Provide search result for table to be used by user's based on Azure Search service. /// </summary> /// <param name="searchScope">Scope of the search.</param> /// <param name="searchQuery">Query which the user had typed in Messaging Extension search field.</param> /// <param name="userObjectId">Azure Active Directory object id of user.</param> /// <param name="count">Number of search results to return.</param> /// <param name="skip">Number of search results to skip.</param> /// <param name="filterQuery">Filter bar based query.</param> /// <returns>List of search results.</returns> public async Task <IEnumerable <ProjectEntity> > GetProjectsAsync( ProjectSearchScope searchScope, string searchQuery, string userObjectId, int?count = null, int?skip = null, string filterQuery = null) { await this.EnsureInitializedAsync(); IEnumerable <ProjectEntity> projects = new List <ProjectEntity>(); var searchParameters = this.InitializeSearchParameters(searchScope, userObjectId, count, skip, filterQuery); SearchContinuationToken continuationToken = null; var projectsCollection = new List <ProjectEntity>(); if (searchScope == ProjectSearchScope.SearchProjects && !string.IsNullOrWhiteSpace(searchQuery)) { searchQuery = this.projectHelper.EscapeCharactersForSearchQuery(searchQuery); } var projectResult = await this.searchIndexClient.Documents.SearchAsync <ProjectEntity>(searchQuery, searchParameters); if (projectResult?.Results != null) { projectsCollection.AddRange(projectResult.Results.Select(p => p.Document)); continuationToken = projectResult.ContinuationToken; } if (continuationToken == null) { return(projectsCollection); } do { var projectNextResult = await this.searchIndexClient.Documents.ContinueSearchAsync <ProjectEntity>(continuationToken); if (projectNextResult?.Results != null) { projectsCollection.AddRange(projectNextResult.Results.Select(p => p.Document)); continuationToken = projectNextResult.ContinuationToken; } }while (continuationToken != null); return(projectsCollection); }
/// <summary> /// Provides idea search results based on query details provided by the user. /// </summary> /// <param name="searchScope">Scope of the search.</param> /// <param name="searchQuery">Query which the user had typed in Messaging Extension search field.</param> /// <param name="userObjectId">Azure Active Directory object id of the user.</param> /// <param name="count">Number of search results to return.</param> /// <param name="skip">Number of search results to skip.</param> /// <param name="sortBy">Represents sorting type like: Popularity or Newest.</param> /// <param name="filterQuery">Filter bar based query.</param> /// <returns>List of search results.</returns> public async Task <IEnumerable <IdeaEntity> > GetTeamIdeasAsync( IdeaSearchScope searchScope, string searchQuery, string userObjectId, int?count = null, int?skip = null, string sortBy = null, string filterQuery = null) { await this.EnsureInitializedAsync(); var searchParameters = this.InitializeSearchParameters(searchScope, userObjectId, count, skip, sortBy, filterQuery); SearchContinuationToken continuationToken = null; var ideas = new List <IdeaEntity>(); if (searchScope == IdeaSearchScope.SearchTeamPostsForTitleText && !string.IsNullOrWhiteSpace(searchQuery)) { searchQuery = searchQuery.EscapeCharactersInQuery(); } var ideaSearchResult = await this.searchIndexClient.Documents.SearchAsync <IdeaEntity>(searchQuery, searchParameters); if (ideaSearchResult?.Results != null) { ideas.AddRange(ideaSearchResult.Results.Select(p => p.Document)); continuationToken = ideaSearchResult.ContinuationToken; } if (continuationToken == null) { return(ideas); } do { var searchResult = await this.searchIndexClient.Documents.ContinueSearchAsync <IdeaEntity>(continuationToken); if (searchResult?.Results != null) { ideas.AddRange(searchResult.Results.Select(p => p.Document)); continuationToken = searchResult.ContinuationToken; } }while (continuationToken != null); return(ideas); }
private static async Task <TResult> DoSearch <TResult>(ISearchIndexClient indexClient, string searchText, string[] facetFields, SearchParameters searchParameters, Func <SearchResults, TResult> searchResults, Func <string, TResult> onFailure, SearchContinuationToken continuationToken = default(SearchContinuationToken)) { try { //searchParameters. var response = await indexClient.Documents.SearchAsync(searchText, searchParameters); var sR = new SearchResults(); sR.Results = await GetAllResultsAsync(indexClient, response); if (facetFields.NullToEmpty().Any()) { sR.Facets = response.Facets.Select(facet => { return(new KeyValuePair <string, Dictionary <string, long?> >(facet.Key, facet.Value.ToDictionary(item => item.Value.ToString(), item => item.Count))); }); } sR.Count = response.Count; return(searchResults(sR)); } catch (Microsoft.Rest.Azure.CloudException clEx) { if (clEx.Response.StatusCode == HttpStatusCode.BadRequest && clEx.Body.Message.Contains("Only filterable fields can be used in filter expressions")) { // This is a property that didn't get marked filterable. // However, this is probably not something we should add to the TResult options } return(onFailure(clEx.Message)); } //var continuationItems = new List<TResult>() as IEnumerable<TResult>; //if (null != response.ContinuationToken) //{ // continuationItems = await DoSearch(indexClient, response.ContinuationToken, facetFields, convertFunc, facetFunc, count.Invoke); //} //return items.Concat(continuationItems); }
/// <summary> /// Provide search result for table to be used by user's based on Azure Search service. /// </summary> /// <param name="searchScope">Scope of the search.</param> /// <param name="searchQuery">Query which the user had typed in Messaging Extension search field.</param> /// <param name="userObjectId">Azure Active Directory object id of the user.</param> /// <param name="count">Number of search results to return.</param> /// <param name="skip">Number of search results to skip.</param> /// <param name="sortBy">Represents sorting type like: Popularity or Newest.</param> /// <param name="filterQuery">Filter bar based query.</param> /// <returns>List of search results.</returns> public async Task <IEnumerable <PostEntity> > GetPostsAsync( PostSearchScope searchScope, string searchQuery, string userObjectId, int?count = null, int?skip = null, int?sortBy = null, string filterQuery = null) { await this.EnsureInitializedAsync(); var searchParameters = this.InitializeSearchParameters(searchScope, userObjectId, count, skip, sortBy, filterQuery); SearchContinuationToken continuationToken = null; var posts = new List <PostEntity>(); var postSearchResult = await this.searchIndexClient.Documents.SearchAsync <PostEntity>(searchQuery, searchParameters); if (postSearchResult?.Results != null) { posts.AddRange(postSearchResult.Results.Select(p => p.Document)); continuationToken = postSearchResult.ContinuationToken; } if (continuationToken == null) { return(posts); } do { var searchResult = await this.searchIndexClient.Documents.ContinueSearchAsync <PostEntity>(continuationToken); if (searchResult?.Results != null) { posts.AddRange(searchResult.Results.Select(p => p.Document)); continuationToken = searchResult.ContinuationToken; } }while (continuationToken != null); return(posts); }
public async Task <IEnumerable <TEntityType> > GetCompleteSet(string searchKey, string filter, string[] select = null, int takeCount = 1000) { var skippy = 0; SearchParameters parameters = new SearchParameters() { Filter = string.IsNullOrEmpty(filter) ? string.Empty : filter, Select = @select ?? new string[] { "*" }, IncludeTotalResultCount = true, SearchMode = SearchMode.All, Skip = skippy, Top = takeCount }; SearchContinuationToken continuationToken = null; var result = new List <TEntityType>(); var limitSearch = await IndexClient.Documents.SearchAsync <TEntityType>( string.IsNullOrWhiteSpace(searchKey)? "*" : searchKey, parameters); var upperLimit = limitSearch.Count > 100000 ? 100000 : limitSearch.Count; parameters.IncludeTotalResultCount = false; while (skippy < upperLimit) { parameters.Skip = skippy; var thisResult = await IndexClient.Documents.SearchAsync <TEntityType>(string.IsNullOrEmpty(searchKey)? "*" : searchKey, parameters); result.AddRange(thisResult.Results.Select(x => x.Document)); continuationToken = thisResult.ContinuationToken; skippy += thisResult.Results.Count; } return(result); }
/// <summary> /// Retrieves the next page of search results from the Azure Search index. (see /// <see href="https://msdn.microsoft.com/library/azure/dn798927.aspx"/> for more information) /// </summary> /// <param name='operations'> /// Reference to the Microsoft.Azure.Search.IDocumentOperations. /// </param> /// <param name="continuationToken"> /// Encapsulates the state required to fetch the next page of search results from the index. /// </param> /// <returns> /// Response containing the documents matching the query. /// </returns> /// <remarks> /// The non-generic overloads of the ContinueSearch and ContinueSearchAsync methods make a best-effort attempt /// to map JSON types in the response payload to .NET types. See /// <see cref="IDocumentOperations.GetAsync(string, System.Collections.Generic.IEnumerable<string>, CancellationToken)"/> /// for more information. /// </remarks> public static Task <DocumentSearchResponse> ContinueSearchAsync( this IDocumentOperations operations, SearchContinuationToken continuationToken) { return(operations.ContinueSearchAsync(continuationToken, CancellationToken.None)); }
/// <summary> /// Retrieves the next page of search results from the Azure Search index. (see /// <see href="https://msdn.microsoft.com/library/azure/dn798927.aspx"/> for more information) /// </summary> /// <typeparam name="T"> /// The CLR type that maps to the index schema. Instances of this type can be retrieved as documents /// from the index. /// </typeparam> /// <param name='operations'> /// Reference to the Microsoft.Azure.Search.IDocumentOperations. /// </param> /// <param name="continuationToken"> /// Encapsulates the state required to fetch the next page of search results from the index. /// </param> /// <returns> /// Response containing the documents matching the query. /// </returns> /// <remarks> /// The generic overloads of the ContinueSearch and ContinueSearchAsync methods support mapping of /// Azure Search field types to .NET types via the type parameter T. See /// <see cref="IDocumentOperations.GetAsync<T>(string, System.Collections.Generic.IEnumerable<string>, CancellationToken)"/> /// for more details on the type mapping. /// </remarks> public static Task <DocumentSearchResponse <T> > ContinueSearchAsync <T>( this IDocumentOperations operations, SearchContinuationToken continuationToken) where T : class { return(operations.ContinueSearchAsync <T>(continuationToken, CancellationToken.None)); }
/// <summary> /// Continues searching documents. /// </summary> /// <returns>The documents.</returns> /// <param name="searchContinuationToken">Continuation token.</param> /// <typeparam name="T">The type of object.</typeparam> public async Task <DocumentSearchResult <T> > ContinueSearchDocumentsAsync <T>(SearchContinuationToken searchContinuationToken) where T : class, new() { var originalSearchResult = await _searchClient.Documents.ContinueSearchAsync <ShushuIndex>(searchContinuationToken).ConfigureAwait(false); var newSearchResult = new List <SearchResult <T> >(); if (originalSearchResult != null) { foreach (var result in originalSearchResult.Results) { var newResult = new SearchResult <T>(result.Document.MapFromIndex <T>(), result.Score, result.Highlights); newSearchResult.Add(newResult); } } return(new DocumentSearchResult <T>(newSearchResult, originalSearchResult.Count, originalSearchResult.Coverage, originalSearchResult.Facets, originalSearchResult.ContinuationToken )); }
private async Task <IEnumerable <TResult> > DoSearch <TResult>(ISearchIndexClient indexClient, SearchContinuationToken continuationToken, string[] facetFields, Func <TResult, TResult> convertFunc, Action <string, Dictionary <string, long?> > facetFunc, Action <long?> count) where TResult : class, new() { var response = await indexClient.Documents.ContinueSearchAsync <TResult>(continuationToken); var items = response.Results.Select(item => convertFunc(item.Document)); if (default(string[]) != facetFields) { foreach (var facet in response.Facets) { var facetValues = facet.Value.ToDictionary(item => item.Value.ToString(), item => item.Count); facetFunc.Invoke(facet.Key, facetValues); } } count.Invoke(response.Count); var continuationItems = new List <TResult>() as IEnumerable <TResult>; if (null != response.ContinuationToken) { continuationItems = await DoSearch(indexClient, response.ContinuationToken, facetFields, convertFunc, facetFunc, count.Invoke); } return(items.Concat(continuationItems)); }