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.