diff --git a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchDbFunctionsExtensions.cs b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchDbFunctionsExtensions.cs
index 6ebcc207d..62d0bde0a 100644
--- a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchDbFunctionsExtensions.cs
+++ b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchDbFunctionsExtensions.cs
@@ -31,6 +31,13 @@ namespace Microsoft.EntityFrameworkCore
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
public static class NpgsqlFullTextSearchDbFunctionsExtensions
{
+ ///
+ /// Convert to a tsvector.
+ /// https://www.postgresql.org/docs/current/static/functions-textsearch.html
+ ///
+ public static NpgsqlTsVector ArrayToTsVector(this DbFunctions _, string[] lexemes) =>
+ throw new NotSupportedException();
+
///
/// Reduce to tsvector.
/// http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-DOCUMENTS
diff --git a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs
index 507675734..01cba0d5d 100644
--- a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs
+++ b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs
@@ -144,6 +144,15 @@ public static NpgsqlTsQuery ToPhrase(this NpgsqlTsQuery query1, NpgsqlTsQuery qu
public static bool Matches(this NpgsqlTsVector vector, NpgsqlTsQuery query) =>
throw new NotSupportedException();
+ ///
+ /// Returns a vector which combines the lexemes and positional information of
+ /// and using the || tsvector operator. Positions and weight labels are retained
+ /// during the concatenation.
+ /// https://www.postgresql.org/docs/10/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR
+ ///
+ public static NpgsqlTsVector Concat(this NpgsqlTsVector vector1, NpgsqlTsVector vector2) =>
+ throw new NotSupportedException();
+
///
/// Assign weight to each element of and return a new
/// weighted tsvector.
@@ -179,6 +188,27 @@ public static NpgsqlTsVector SetWeight(this NpgsqlTsVector vector, char weight)
public static NpgsqlTsVector SetWeight(this NpgsqlTsVector vector, char weight, string[] lexemes) =>
throw new NotSupportedException();
+ ///
+ /// Return a new vector with removed from
+ /// https://www.postgresql.org/docs/current/static/functions-textsearch.html
+ ///
+ public static NpgsqlTsVector Delete(this NpgsqlTsVector vector, string lexeme) =>
+ throw new NotSupportedException();
+
+ ///
+ /// Return a new vector with removed from
+ /// https://www.postgresql.org/docs/current/static/functions-textsearch.html
+ ///
+ public static NpgsqlTsVector Delete(this NpgsqlTsVector vector, string[] lexemes) =>
+ throw new NotSupportedException();
+
+ ///
+ /// Returns a new vector with only lexemes having weights specified in .
+ /// https://www.postgresql.org/docs/current/static/functions-textsearch.html
+ ///
+ public static NpgsqlTsVector Filter(this NpgsqlTsVector vector, char[] weights) =>
+ throw new NotSupportedException();
+
///
/// Returns the number of lexemes in .
/// http://www.postgresql.org/docs/current/static/textsearch-features.html#TEXTSEARCH-MANIPULATE-TSVECTOR
diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs
index d057d1f5d..44ecba056 100644
--- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs
+++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs
@@ -47,6 +47,7 @@ public class NpgsqlFullTextSearchMethodTranslator : IMethodCallTranslator
static readonly IReadOnlyDictionary _sqlNameByMethodName =
new Dictionary
{
+ [nameof(NpgsqlFullTextSearchDbFunctionsExtensions.ArrayToTsVector)] = "array_to_tsvector",
[nameof(NpgsqlFullTextSearchDbFunctionsExtensions.ToTsVector)] = "to_tsvector",
[nameof(NpgsqlFullTextSearchDbFunctionsExtensions.PlainToTsQuery)] = "plainto_tsquery",
[nameof(NpgsqlFullTextSearchDbFunctionsExtensions.PhraseToTsQuery)] = "phraseto_tsquery",
@@ -105,6 +106,11 @@ static Expression TryTranslateOperator(MethodCallExpression methodCallExpression
methodCallExpression.Arguments[0],
methodCallExpression.Arguments[1]);
+ case nameof(NpgsqlFullTextSearchLinqExtensions.Concat):
+ return FullTextSearchExpression.TsVectorConcat(
+ methodCallExpression.Arguments[0],
+ methodCallExpression.Arguments[1]);
+
default:
return null;
}
@@ -196,6 +202,22 @@ static Expression TryTranslateFunction(MethodCallExpression methodCallExpression
methodCallExpression.Method.ReturnType,
arguments);
+ case nameof(NpgsqlFullTextSearchLinqExtensions.Delete):
+ return new SqlFunctionExpression(
+ "ts_delete",
+ methodCallExpression.Method.ReturnType,
+ methodCallExpression.Arguments);
+
+ case nameof(NpgsqlFullTextSearchLinqExtensions.Filter):
+ return new SqlFunctionExpression(
+ "ts_filter",
+ methodCallExpression.Method.ReturnType,
+ new[]
+ {
+ methodCallExpression.Arguments[0],
+ new ExplicitStoreTypeCastExpression(methodCallExpression.Arguments[1], typeof(char[]), "\"char\"[]")
+ });
+
case nameof(NpgsqlFullTextSearchLinqExtensions.GetLength):
return new SqlFunctionExpression(
"length",
diff --git a/src/EFCore.PG/Query/Expressions/Internal/FullTextSearchExpression.cs b/src/EFCore.PG/Query/Expressions/Internal/FullTextSearchExpression.cs
index 650fedf23..0b4666d11 100644
--- a/src/EFCore.PG/Query/Expressions/Internal/FullTextSearchExpression.cs
+++ b/src/EFCore.PG/Query/Expressions/Internal/FullTextSearchExpression.cs
@@ -92,6 +92,12 @@ public static FullTextSearchExpression TsVectorMatches([NotNull] Expression left
return new FullTextSearchExpression("@@", left, right, typeof(bool));
}
+ public static FullTextSearchExpression TsVectorConcat([NotNull] Expression left, [NotNull] Expression right)
+ {
+ ValidateArguments(left, typeof(NpgsqlTsVector), right, typeof(NpgsqlTsVector));
+ return new FullTextSearchExpression("||", left, right, typeof(NpgsqlTsVector));
+ }
+
private static void ValidateArguments(
[NotNull] Expression left,
[NotNull] Type validLeftType,
diff --git a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs
index 5438e9045..3fd4f0f8c 100644
--- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs
+++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -32,6 +33,34 @@ public void TsVectorParse_converted_to_cast()
LIMIT 1");
}
+ [Fact]
+ public void ArrayToTsVector()
+ {
+ using (var context = CreateContext())
+ {
+ var tsvector = context.Customers.Select(c => EF.Functions.ArrayToTsVector(new[] { "b", "c", "d" }))
+ .First();
+ Assert.Equal(NpgsqlTsVector.Parse("b c d").ToString(), tsvector.ToString());
+ }
+
+ AssertSql(
+ @"SELECT array_to_tsvector(ARRAY['b','c','d'])
+FROM ""Customers"" AS c
+LIMIT 1");
+ }
+
+ [Fact]
+ public void ArrayToTsVector_From_Columns_Throws_NotSupportedException()
+ {
+ using (var context = CreateContext())
+ {
+ Assert.Throws(
+ () => context.Customers
+ .Select(c => EF.Functions.ArrayToTsVector(new[] { c.CompanyName, c.Address }))
+ .First());
+ }
+ }
+
[Fact]
public void ToTsVector()
{
@@ -429,6 +458,23 @@ public void Matches_With_Tsquery()
LIMIT 1");
}
+ [Fact]
+ public void TsVectorConcat()
+ {
+ using (var context = CreateContext())
+ {
+ var tsVector = context.Customers
+ .Select(c => EF.Functions.ToTsVector("b").Concat(EF.Functions.ToTsVector("c")))
+ .First();
+ Assert.Equal(NpgsqlTsVector.Parse("b:1 c:2").ToString(), tsVector.ToString());
+ }
+
+ AssertSql(
+ @"SELECT (to_tsvector('b') || to_tsvector('c'))
+FROM ""Customers"" AS c
+LIMIT 1");
+ }
+
[Fact]
public void Setweight_With_Enum()
{
@@ -497,6 +543,57 @@ public void Setweight_With_Char_And_Lexemes()
LIMIT 1");
}
+ [Fact]
+ public void Delete_With_Single_Lexeme()
+ {
+ using (var context = CreateContext())
+ {
+ var tsVector = context.Customers
+ .Select(c => EF.Functions.ToTsVector("b c").Delete("c"))
+ .First();
+ Assert.Equal(NpgsqlTsVector.Parse("b:1").ToString(), tsVector.ToString());
+ }
+
+ AssertSql(
+ @"SELECT ts_delete(to_tsvector('b c'), 'c')
+FROM ""Customers"" AS c
+LIMIT 1");
+ }
+
+ [Fact]
+ public void Delete_With_Multiple_Lexemes()
+ {
+ using (var context = CreateContext())
+ {
+ var tsVector = context.Customers
+ .Select(c => EF.Functions.ToTsVector("b c d").Delete(new[] { "c", "d" }))
+ .First();
+ Assert.Equal(NpgsqlTsVector.Parse("b:1").ToString(), tsVector.ToString());
+ }
+
+ AssertSql(
+ @"SELECT ts_delete(to_tsvector('b c d'), ARRAY['c','d'])
+FROM ""Customers"" AS c
+LIMIT 1");
+ }
+
+ [Fact]
+ public void Filter()
+ {
+ using (var context = CreateContext())
+ {
+ var tsVector = context.Customers
+ .Select(c => NpgsqlTsVector.Parse("b:1A c:2B d:3C").Filter(new[] { 'B', 'C' }))
+ .First();
+ Assert.Equal(NpgsqlTsVector.Parse("c:2B d:3C").ToString(), tsVector.ToString());
+ }
+
+ AssertSql(
+ @"SELECT ts_filter(CAST('b:1A c:2B d:3C' AS tsvector), CAST(ARRAY['B','C'] AS ""char""[]))
+FROM ""Customers"" AS c
+LIMIT 1");
+ }
+
[Fact]
public void GetLength()
{