/// <summary>
        /// Adds number of items to take.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="take">Number of items to take.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> Take <TEntity>(this IODataQueryable <TEntity> query, int take)
        {
            var result = query.Clone();

            result.Take = take;
            return(result);
        }
        public async Task <ODataResult <TEntity> > RetrieveItemsAsync <TEntity>(IODataQueryable <TEntity> query, CancellationToken cancellation)
        {
            var info = GetEntityInfo <TEntity>();

            var parameters = new SearchOptions
            {
                Filter            = query.Filter,
                Size              = query.Take,
                Skip              = query.Skip,
                IncludeTotalCount = true,
            };

            AddRange(parameters.OrderBy, query.Order);
            AddRange(parameters.Select, query.Select);

            string search = null;

            if (query is AzureQueryable <TEntity> azq)
            {
                AddRange(parameters.SearchFields, azq.SearchFields);
                parameters.SearchMode = azq.SearchMode;
                parameters.QueryType  = azq.QueryType;
                search = azq.Search;
            }

            var response = await info.Index.SearchAsync <TEntity>(search, parameters, cancellation);

            return(await ODataAzureSearchResult <TEntity> .MakeResultAsync(response, cancellation));
        }
        /// <summary>
        /// Adds number items to skip.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="skip">Number of items to skip.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> Skip <TEntity>(this IODataQueryable <TEntity> query, int skip)
        {
            var result = query.Clone();

            result.Skip = skip;
            return(result);
        }
        /// <summary>
        /// Looks for the first item or returns null if no items.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query for items.</param>
        /// <param name="cancellation">Cancellation.</param>
        /// <returns>Item.</returns>
        public static async Task <TEntity> FirstOrDefaultAsync <TEntity>(
            this IODataQueryable <TEntity> query,
            CancellationToken cancellation = default)
        {
            var results = await query.Context.Provider.RetrieveItemsAsync(query.Take(1), cancellation);

            return(results.Items.FirstOrDefault());
        }
        /// <summary>
        /// Adds property as descending order.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="expression">Property expression.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> OrderByDescending <TEntity>(
            this IODataQueryable <TEntity> query,
            Expression <Func <TEntity, object> > expression)
        {
            var result = query.Clone();

            result.Order.Add(GetPropertyName(expression) + " desc");
            return(result);
        }
        /// <summary>
        /// Set selected properties.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="select">Column names.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> Select <TEntity>(
            this IODataQueryable <TEntity> query,
            IEnumerable <string> select)
        {
            var result = query.Clone();

            result.Select.AddRange(select);

            return(result);
        }
        /// <summary>
        /// Adds order columns by name.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="order">Column names.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> OrderBy <TEntity>(
            this IODataQueryable <TEntity> query,
            IEnumerable <string> order)
        {
            var result = query.Clone();

            result.Order.AddRange(order);

            return(result);
        }
        /// <summary>
        /// Sets mode of Search query.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="query">The query.</param>
        /// <param name="mode">The mode.</param>
        /// <returns>Query.</returns>
        public static IODataQueryable <TEntity> SearchMode <TEntity>(
            this IODataQueryable <TEntity> query,
            SearchMode mode)
        {
            var result = new AzureQueryable <TEntity>(query)
            {
                SearchMode = mode
            };

            return(result);
        }
        /// <summary>
        /// Sets type of Search query.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="query">The query.</param>
        /// <param name="queryType">Type of the query.</param>
        /// <returns>Query.</returns>
        public static IODataQueryable <TEntity> QueryType <TEntity>(
            this IODataQueryable <TEntity> query,
            SearchQueryType queryType)
        {
            var result = new AzureQueryable <TEntity>(query)
            {
                QueryType = queryType
            };

            return(result);
        }
        /// <summary>
        /// Filter condition.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to add to.</param>
        /// <param name="expression">Conditional expression.</param>
        /// <returns>New query.</returns>
        public static IODataQueryable <TEntity> Where <TEntity>(
            this IODataQueryable <TEntity> query,
            Expression <Func <TEntity, bool> > expression)
        {
            var result = query.Clone();

            result.Filter = query.Filter == null
                                ? $"({query.Context.FilterMaker.MakeFilter(expression)})"
                                : $"{query.Filter} and ({query.Context.FilterMaker.MakeFilter(expression)})";

            return(result);
        }
        /// <summary>
        /// Searches the specified search.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="query">The query.</param>
        /// <param name="search">The search.</param>
        /// <param name="searchFields">The search fields.</param>
        /// <returns>Queryable.</returns>
        public static IODataQueryable <TEntity> Search <TEntity>(
            this IODataQueryable <TEntity> query,
            string search,
            IEnumerable <string> searchFields = null)
        {
            var result = new AzureQueryable <TEntity>(query);

            result.Search = search;
            if (searchFields != null)
            {
                result.SearchFields = new List <string>(searchFields);
            }

            return(result);
        }
        /// <inheritdoc />
        public async IAsyncEnumerable <TEntity> QueryItemsAsync <TEntity>(
            IODataQueryable <TEntity> query,
            [EnumeratorCancellation] CancellationToken cancellation)
        {
            var info = GetEntityInfo <TEntity>();

            var parameters = new SearchOptions
            {
                Filter            = query.Filter,
                Size              = query.Take,
                Skip              = query.Skip,
                IncludeTotalCount = true,
            };

            AddRange(parameters.OrderBy, query.Order);
            AddRange(parameters.Select, query.Select);

            string search = null;

            if (query is AzureQueryable <TEntity> azq)
            {
                AddRange(parameters.SearchFields, azq.SearchFields);
                parameters.SearchMode = azq.SearchMode;
                parameters.QueryType  = azq.QueryType;
                search = azq.Search;
            }

            var response = await info.Index.SearchAsync <TEntity>(search, parameters, cancellation);

            var results = response.Value.GetResultsAsync();

            if (typeof(IResourceWithScore).IsAssignableFrom(typeof(TEntity)))
            {
                await foreach (var res in results)
                {
                    var result = res.Document;
                    ((IResourceWithScore)result).Score = res.Score;
                    yield return(result);
                }
            }
            else
            {
                await foreach (var res in results)
                {
                    yield return(res.Document);
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AzureQueryable{TEntity}"/> class.
        /// </summary>
        /// <param name="query">The query.</param>
        public AzureQueryable(IODataQueryable <TEntity> query)
        {
            Context = query.Context;
            Filter  = query.Filter;
            Order   = query.Order;
            Select  = query.Select;
            Take    = query.Take;
            Skip    = query.Skip;

            if (query is AzureQueryable <TEntity> azq)
            {
                Search       = azq.Search;
                SearchFields = azq.SearchFields;
                SearchMode   = azq.SearchMode;
                QueryType    = azq.QueryType;
            }
        }
        /// <summary>
        /// Returns page of items from the query.
        /// </summary>
        /// <typeparam name="TEntity">Type of items.</typeparam>
        /// <param name="query">Query to query.</param>
        /// <param name="continuationToken">Continuation token string.</param>
        /// <param name="cancellation">Cancellation.</param>
        /// <returns>A <see cref="Task{TResult}" /> representing the result of the asynchronous operation.</returns>
        public static async Task <PaginatedResponse <TEntity> > ToPageAsync <TEntity>(
            this IODataQueryable <TEntity> query,
            IPaginatedRequest request,
            CancellationToken cancellation = default)
        {
            var newQuery = query.Skip(request.Skip);

            newQuery.Take = request.Take;
            var items = await query.Context.Provider.RetrieveItemsAsync(newQuery, cancellation);

            return(new PaginatedResponse <TEntity>
            {
                Items = items.Items.ToList(),
                HasMore = items.HasMore,
                Take = request.Take,
                Skip = request.Skip,
                Total = items.Total,
            });
        }
 /// <summary>
 /// Returns items from the query.
 /// </summary>
 /// <typeparam name="TEntity">Type of items.</typeparam>
 /// <param name="query">Query to query.</param>
 /// <param name="cancellation">Cancellation.</param>
 /// <returns>A <see cref="Task{TResult}" /> representing the result of the asynchronous operation.</returns>
 public static async Task <List <TEntity> > ToListAsync <TEntity>(
     this IODataQueryable <TEntity> query,
     CancellationToken cancellation = default)
 {
     return(await query.ToAsyncEnumerable(cancellation).ToListAsync(cancellation));
 }
 /// <summary>
 /// Makes AsyncEnumerable.
 /// </summary>
 /// <typeparam name="TEntity">Type of items.</typeparam>
 /// <param name="query">Query to convert.</param>
 /// <param name="cancellation">Cancellation.</param>
 /// <returns>Async query.</returns>
 public static IAsyncEnumerable <TEntity> ToAsyncEnumerable <TEntity>(
     this IODataQueryable <TEntity> query,
     CancellationToken cancellation = default)
 {
     return(query.Context.Provider.QueryItemsAsync(query, cancellation));
 }