Search code examples
c#asp.net-mvclinqpagedlist

LINQ multiple keyword search to PagedList


I'm a bit lost here and I've tried a few different ways to tackle it. So far I'm having a hard time writing out the LINQ to do what I want.

I want to take the user input string which can be multiple keywords split either by whitespace or ",".

This here works grabs the whole search term and compares it to the title in the Post or any tag I may have. I want the user to type in "HTML Preview" which would match a post called, "Preview the World" with the tags "HTML", "CSS", etc....

This query won't work...but I'm trying to modify it so that it does work.

public IPagedList<Post> SearchResultList(string searchTerm, int resultsPerPage, int page)
{
    string[] terms = searchTerm.Split(null);

    TNDbContext context = DataContext;
    return context.Posts
        .Include(a => a.Tags)
        .Include(b => b.Comments)
        .Where(c => (c.Title.Contains(searchTerm) || c.Tags.Any(d => d.Name.StartsWith(searchTerm))) || searchTerm == null)
        .OrderByDescending(x => x.Views)
        .ToPagedList(page, resultsPerPage);

}

I tried writing this instead of the other "Where" statement

.Where(x => (terms.All(y => x.Title.Contains(y))) || terms == null)

but it keeps throwing this error

Cannot compare elements of type 'System.String[]'. Only primitive types, enumeration types and entity types are supported.

FOR REFERENCE:

public class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
        Comments = new HashSet<Comment>();
    }

    public int Id { get; set; }
    public string Title { get; set; }
    public string UrlTitle { get; set; }
    public DateTime Date { get; set; }

    public DateTime DateEdited { get; set; }

    public string Body { get; set; }

    public string Preview { get; set; }

    public string PhotoPath { get; set; }

    public int Views { get; set; }



    //Navigational

    public ICollection<Tag> Tags { get; set; }

    public ICollection<Comment> Comments { get; set; }

}

public class Tag
{
    public Tag()
    {
        Post = new HashSet<Post>();
    }


    public int Id { get; set; }
    public string Name { get; set; }

    public int TimesTagWasUsed { get; set; }


    //Navigational

    public ICollection<Post> Post { get; set; }


}

Solution

  • You need to start with a base query, and then keep adding where clauses to it for each search term. Try this:

    TNDbContext context = DataContext;
    
    //Create the base query:
    var query = context.Posts
            .Include(a => a.Tags)
            .Include(b => b.Comments)
            .OrderByDescending(x => x.Views);
    
    //Refine this query by adding "where" filters for each search term:
    if(!string.IsNullOrWhitespace(searchTerm))
    {
        string[] terms = searchTerm.Split(" ,".ToCharArray(),
                                          StringSplitOptions.RemoveEmptyEntries);
        foreach(var x in terms)
        {
            string term = x;
            query = query.Where(post => (post.Title.Contains(term) ||
                                         post.Tags.Any(tag => tag.Name.StartsWith(term))));
        }
    }
    
    //Run the final query to get some results:
    var result = query.ToPagedList(page, resultsPerPage);
    
    return result;