Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OData V6.6.0 is not allowing $search

Tags:

search

odata

I'm trying to use OData V6.6.0 $search functionality. I initially had version 5.2 which I updated to version 6.6.0. However, I still am not able to use $search. When I use it in url

odata/Prescribers?$inlinecount=allpages&$top=20&$search=TEST';

I get this error message 'The query parameter '$search' is not supported.'

Do I need to do something else in order to add $search functionality. Btw, I'm also using EnableQuery in my Controller like this.

    [EnableQuery(AllowedQueryOptions = System.Web.Http.OData.Query.AllowedQueryOptions.All)]

 public IQueryable<ContactList> Get()
        {
            return dbContext.ContactsList.AsQueryable();
        }
like image 879
user1828605 Avatar asked Nov 01 '22 19:11

user1828605


2 Answers

I figured it out. If substringof is used in the filter then it returns the records of value containing the string. I used $filter with 'or' operator to check multiple columns.

'&$filter=substringof(\'' + key + '\', NPI)' +
  'or substringof(\'' + key + '\', Zip)' +                        
  'or substringof(\'' + key + '\', PrescriberName)';

This displayed all the records that contains the string in the filter.

like image 126
user1828605 Avatar answered Dec 07 '22 23:12

user1828605


Although this is kind of old, I wanted to give you my take of an implementation of a "$search to $filter" mapping based on an AllowSearchAttribute on the model. It is implemented as an attribute derived from EnableQueryAttribute. The "magic" is to take the $search part of the query, convert it to a $filter clause, and remove it from the parameters before further processing is done by the infrastructure. This is achieved by replacing request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] with a changed array. This is the cache, from which later processing will fetch the query fields (see request.GetQueryNameValuePairs).

public class EnableQueryWithSearchAttribute : EnableQueryAttribute
{
    public Type ModelType { get; set; }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var request = actionExecutedContext.Request;
        var query = request.GetQueryNameValuePairs().ToList();
        var searchParam = query.FirstOrDefault(q => q.Key == "$search");
        if (!String.IsNullOrWhiteSpace(searchParam.Key))
        {
            if (!String.IsNullOrWhiteSpace(searchParam.Value))
            {
                string filterString = null;
                var filter = query.FirstOrDefault(q => q.Key == "$filter");
                if (!String.IsNullOrWhiteSpace(filter.Key))
                {
                    filterString = filter.Value;
                }

                var odataSearchValue = searchParam.Value.Replace("'", "''");
                var props = this.ModelType
                    .GetProperties()
                    .Where(pi => pi.GetCustomAttribute<AllowSearchAttribute>() != null)
                    .Select(pi => pi.Name)
                    .ToList();

                if (props.Count > 0)
                {
                    if (!String.IsNullOrWhiteSpace(filterString))
                    {
                        filterString = "(" + filterString + ") and ";
                    }

                    filterString += "(contains(" +
                                    String.Join(", '" + odataSearchValue + "') or contains(", props) + ", '" +
                                    odataSearchValue + "'))";
                }

                request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] =
                    query.Where(q => q.Key != "$search" && q.Key != "$filter").Concat(new[] { new KeyValuePair<string, string>("$filter", filterString) }).ToArray();
            }
            else
            {
                request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] =
                    query.Where(q => q.Key != "$search").ToArray();
            }
        }

        base.OnActionExecuted(actionExecutedContext);
    }
}

The AllowSearchAttribute is a descriptive attribute with no extra info.

[AttributeUsage(AttributeTargets.Property)]
public class AllowSearchAttribute : Attribute { }

On the model class, you can specify which properties should be searched, by applying the [AllowSearch] attribute to the desired properties. In the example, only Title and Description will be searched:

public class MyModel
{
    public int Id { get; set; }
    [AllowSearch]
    public string Title { get; set; }
    [AllowSearch]
    public string Description { get; set; }
    public string SomethingNotSearchable { get; set; }
}

And on the method where you typically apply the [EnableQuery] attribute, you will use the derived one and specify the model type:

[EnableQueryWithSearch(EnsureStableOrdering = true, ModelType = typeof(MyModel))]
[HttpGet]
public IQueryable<MyModel> Get()
{
    // your code here.
}

You can now call your OData endpoint with a $search parameter, which will be mapped to a corresponding $filter parameter internally. The current implementation always uses a contains(...) operation, but you could easily enhance the implementation with custom [AllowSearch] parameters, which will issue other operations.

Hope this helps someone. I couldn't find any other implementation of this sort anywhere, so I thought I'd share.

like image 29
Christoph Herold Avatar answered Dec 07 '22 23:12

Christoph Herold