Skip to content

Commit 0b83874

Browse files
committed
Add regex and regex_phrase
1 parent c2ea97c commit 0b83874

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

src/Internal/Query/Translator.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ IRelationalTypeMappingSource typeMappingSource
8686
argumentsPropagateNullability: [false],
8787
returnType: typeof(bool)
8888
),
89+
nameof(Pdb.Regex) => _sqlExpressionFactory.Function(
90+
name: "pdb.regex",
91+
nullable: false,
92+
arguments: [arguments[0]],
93+
argumentsPropagateNullability: [false],
94+
returnType: typeof(bool)
95+
),
96+
nameof(Pdb.RegexPhrase) => BuildRegexPhrase(arguments),
8997
nameof(Pdb.RangeTerm) => BuildRangeTerm(arguments),
9098
nameof(Pdb.PhrasePrefix) => BuildPhrasePrefix(arguments),
9199
nameof(ParadeDbFunctionsExtensions.Snippet) => BuildSnippet(arguments),
@@ -149,6 +157,35 @@ IRelationalTypeMappingSource typeMappingSource
149157
);
150158
}
151159

160+
private SqlExpression? BuildRegexPhrase(IReadOnlyList<SqlExpression> arguments)
161+
{
162+
List<SqlExpression> args = [_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0])];
163+
List<string?> argNames = [null];
164+
165+
if (arguments[1] is not SqlConstantExpression { Value: null })
166+
{
167+
args.Add(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[1]));
168+
argNames.Add("slop");
169+
}
170+
171+
if (arguments[2] is not SqlConstantExpression { Value: null })
172+
{
173+
args.Add(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[2]));
174+
argNames.Add("max_expansions");
175+
}
176+
177+
return PgFunctionExpression.CreateWithNamedArguments(
178+
name: "pdb.regex_phrase",
179+
arguments: args,
180+
argumentNames: argNames,
181+
nullable: false,
182+
argumentsPropagateNullability: new bool[args.Count],
183+
builtIn: false,
184+
type: typeof(bool),
185+
typeMapping: null
186+
);
187+
}
188+
152189
private SqlExpression? BuildRangeTerm(IReadOnlyList<SqlExpression> arguments)
153190
{
154191
List<SqlExpression> args = [_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0])];

src/Pdb.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ public static PdbQuery Exists() =>
7979
public static PdbQuery Parse(string pattern) =>
8080
throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Parse)));
8181

82+
public static PdbQuery Regex([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) =>
83+
throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Regex)));
84+
8285
public static PdbQuery RangeTerm<T>(T value) =>
8386
throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeTerm)));
8487

@@ -91,4 +94,10 @@ [NotParameterized] RangeTermRelation relation
9194

9295
public static PdbQuery PhrasePrefix(string[] tokens, int? maxExpansions = null) =>
9396
throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(PhrasePrefix)));
97+
98+
public static PdbQuery RegexPhrase(
99+
[StringSyntax(StringSyntaxAttribute.Regex)] string[] phrases,
100+
int? slop = null,
101+
int? maxExpansions = null
102+
) => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RegexPhrase)));
94103
}

tests/QueryTests.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,94 @@ WHERE m.description @@@ pdb.parse('description:(sleek shoes) AND rating:>3')
16381638
await query.ToListAsync();
16391639
}
16401640

1641+
[Test]
1642+
public async Task RegexQuery()
1643+
{
1644+
await using var context = DbFixture.CreateContext();
1645+
1646+
var query = context
1647+
.MockItems.Where(p => EF.Functions.Query(p.Description, Pdb.Regex("ru.*")))
1648+
.Select(p => p.Description);
1649+
1650+
var sql = """
1651+
SELECT m.description
1652+
FROM mock_items AS m
1653+
WHERE m.description @@@ pdb.regex('ru.*')
1654+
""";
1655+
1656+
AssertSql(query, sql);
1657+
await query.ToListAsync();
1658+
}
1659+
1660+
[Test]
1661+
public async Task RegexPhrase()
1662+
{
1663+
await using var context = DbFixture.CreateContext();
1664+
1665+
var query = context
1666+
.MockItems.Where(p =>
1667+
EF.Functions.Query(p.Description, Pdb.RegexPhrase(new[] { "ru.*", "shoes" }))
1668+
)
1669+
.Select(p => p.Description);
1670+
1671+
var sql = """
1672+
SELECT m.description
1673+
FROM mock_items AS m
1674+
WHERE m.description @@@ pdb.regex_phrase(ARRAY['ru.*','shoes']::text[])
1675+
""";
1676+
1677+
AssertSql(query, sql);
1678+
await query.ToListAsync();
1679+
}
1680+
1681+
[Test]
1682+
public async Task RegexPhraseSlopAndMaxExpansions()
1683+
{
1684+
await using var context = DbFixture.CreateContext();
1685+
1686+
var query = context
1687+
.MockItems.Where(p =>
1688+
EF.Functions.Query(
1689+
p.Description,
1690+
Pdb.RegexPhrase(new[] { "ru.*", "shoes" }, 2, 100)
1691+
)
1692+
)
1693+
.Select(p => p.Description);
1694+
1695+
var sql = """
1696+
SELECT m.description
1697+
FROM mock_items AS m
1698+
WHERE m.description @@@ pdb.regex_phrase(ARRAY['ru.*','shoes']::text[], slop => 2, max_expansions => 100)
1699+
""";
1700+
1701+
AssertSql(query, sql);
1702+
await query.ToListAsync();
1703+
}
1704+
1705+
[Test]
1706+
public async Task RegexPhraseMaxExpansions()
1707+
{
1708+
await using var context = DbFixture.CreateContext();
1709+
1710+
var query = context
1711+
.MockItems.Where(p =>
1712+
EF.Functions.Query(
1713+
p.Description,
1714+
Pdb.RegexPhrase(new[] { "ru.*", "shoes" }, null, 100)
1715+
)
1716+
)
1717+
.Select(p => p.Description);
1718+
1719+
var sql = """
1720+
SELECT m.description
1721+
FROM mock_items AS m
1722+
WHERE m.description @@@ pdb.regex_phrase(ARRAY['ru.*','shoes']::text[], max_expansions => 100)
1723+
""";
1724+
1725+
AssertSql(query, sql);
1726+
await query.ToListAsync();
1727+
}
1728+
16411729
[Test]
16421730
public async Task PhrasePrefix()
16431731
{

0 commit comments

Comments
 (0)