From 1498f99cdffe3097997363b6dfff72f2d41a3351 Mon Sep 17 00:00:00 2001 From: Raif Atef Date: Fri, 27 Apr 2018 13:54:02 +0200 Subject: [PATCH 1/4] tsvector concat operator support. --- .../NpgsqlFullTextSearchLinqExtensions.cs | 9 +++++++++ .../NpgsqlFullTextSearchMethodTranslator.cs | 5 +++++ .../Internal/FullTextSearchExpression.cs | 6 ++++++ .../FullTextSearchDbFunctionsNpgsqlTest.cs | 17 +++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs index 507675734..70845feae 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. diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs index d057d1f5d..45449f89f 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs @@ -105,6 +105,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; } 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..ff42ef253 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs @@ -429,6 +429,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() { From 704678d0743f93db8ed9f8dd71088920f6f046ab Mon Sep 17 00:00:00 2001 From: Raif Atef Date: Fri, 27 Apr 2018 14:29:26 +0200 Subject: [PATCH 2/4] ts_delete function support. --- .../NpgsqlFullTextSearchLinqExtensions.cs | 14 ++++++++ .../NpgsqlFullTextSearchMethodTranslator.cs | 6 ++++ .../FullTextSearchDbFunctionsNpgsqlTest.cs | 34 +++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs index 70845feae..78e9d6006 100644 --- a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs +++ b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs @@ -188,6 +188,20 @@ 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 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 45449f89f..e29b1dce6 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs @@ -201,6 +201,12 @@ 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.GetLength): return new SqlFunctionExpression( "length", diff --git a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs index ff42ef253..d6ac3fb2c 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs @@ -514,6 +514,40 @@ 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 GetLength() { From 9314ce0629a611e7abec5d255feaf8b1641f273d Mon Sep 17 00:00:00 2001 From: Raif Atef Date: Sat, 28 Apr 2018 00:29:13 +0200 Subject: [PATCH 3/4] ts_filter function support. --- .../NpgsqlFullTextSearchLinqExtensions.cs | 7 +++++++ .../NpgsqlFullTextSearchMethodTranslator.cs | 10 ++++++++++ .../FullTextSearchDbFunctionsNpgsqlTest.cs | 17 +++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs index 78e9d6006..01cba0d5d 100644 --- a/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs +++ b/src/EFCore.PG/FullTextSearch/NpgsqlFullTextSearchLinqExtensions.cs @@ -202,6 +202,13 @@ public static NpgsqlTsVector Delete(this NpgsqlTsVector vector, string lexeme) = 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 e29b1dce6..31b27b3a2 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs @@ -207,6 +207,16 @@ static Expression TryTranslateFunction(MethodCallExpression methodCallExpression 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/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs index d6ac3fb2c..85554e36e 100644 --- a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs @@ -548,6 +548,23 @@ public void Delete_With_Multiple_Lexemes() 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() { From edf63deeb5370bef4235686ba7e9f735abfcaea3 Mon Sep 17 00:00:00 2001 From: Raif Atef Date: Sat, 28 Apr 2018 00:46:18 +0200 Subject: [PATCH 4/4] array_to_tsvector function support (table columns in array are not supported). --- ...gsqlFullTextSearchDbFunctionsExtensions.cs | 7 +++++ .../NpgsqlFullTextSearchMethodTranslator.cs | 1 + .../FullTextSearchDbFunctionsNpgsqlTest.cs | 31 ++++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) 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/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlFullTextSearchMethodTranslator.cs index 31b27b3a2..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", diff --git a/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/FullTextSearchDbFunctionsNpgsqlTest.cs index 85554e36e..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() {