I'm using EF Core 2.2 .FromSql() method to run a full text search query using CONTAINS() predicate.

I want to search on all full text indexed columns.

According to documentation (link), to perform full text search query you can specify:

  • column_name
  • ( column_list )
  • " * " (query searches all full-text indexed columns)

There is a method 'EF.Functions.Contains()' but in only allows to input a single propertyReference:

Expression<Func<T, bool>> searchPredicate = x =>
     EF.Functions.Contains(EF.Property<string>(x, "FirstName"), searchKeyword);

I can construct a query using OR statement like this:

Expression<Func<T, bool>> searchPredicate = x =>
     EF.Functions.Contains(EF.Property<string>(x, "FirstName"), searchKeyword) ||
     EF.Functions.Contains(EF.Property<string>(x, "LastName"), searchKeyword);

But looking into profiler it constructs the following query which is undesired:

SELECT [x].[Id],
       [x].[FirstName],
       [x].[LastName],
       [x].[Address]
FROM   [Users] AS [x]
WHERE  (CONTAINS([x].[FirstName], 'John' /* @__searchKeyword_1 */))
        OR (CONTAINS([x].[LastName], 'John' /* @__searchKeyword_1 */))

So, I should use a custom query in .FromSql() to perform a search on all full text indexed columns like this:

var customQuery = "SELECT * FROM Users WHERE CONTAINS(*, '\"John\"')";
var users = context.Users.FromSql(customQuery);

Expected query:

SELECT * FROM Users WHERE CONTAINS(*, '\"John\"');

Actual query:

SELECT [p].[Id],
       [p].[FirstName],
       [p].[LastName],
       [p.Address].[Id],
       [p.Address].[Street],
       [p.Address].[StreetNumber]
FROM   (SELECT * FROM Users WHERE CONTAINS(*, '\"John\"')) AS [p]
       LEFT JOIN [Users] AS [p.Address]
         ON [p].[Id] = [p.Address].[Id]

Entity, ValueObject and OnModelCreating() looks like this:

public class User : IEntity
{
   public Guid Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public Address Address { get; set; }
}

public class Address: IValueObject
{
   public string Street { get; set; }
   public string StreetNumber { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<User>().ToTable("Users");
    modelBuilder.Entity<User>().OwnsOne(e => e.Address);
}

I am saving Address ValueObject on the same Users table with .OwnsOne().

Using .FromSql() on a different entity which doesn't have ValueObject in it works OK and doesn't include LEFT JOIN in the query.

What should I do to omit LEFT JOIN in the query and perform full text index search correctly?

0

There are 0 best solutions below