Skip to content

Commit 12f9bfc

Browse files
author
Robert Stam
committed
Implemented support for LINQ queries involving string length.
1 parent e2e5ecb commit 12f9bfc

File tree

2 files changed

+178
-2
lines changed

2 files changed

+178
-2
lines changed

Driver/Linq/Translators/SelectQuery.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ private IMongoQuery BuildComparisonQuery(BinaryExpression binaryExpression)
361361
return query;
362362
}
363363

364+
query = BuildStringLengthQuery(variableExpression, operatorType, constantExpression);
365+
if (query != null)
366+
{
367+
return query;
368+
}
369+
364370
BsonSerializationInfo serializationInfo = null;
365371
var value = constantExpression.Value;
366372

@@ -744,6 +750,60 @@ private IMongoQuery BuildQuery(Expression expression)
744750
return query;
745751
}
746752

753+
private IMongoQuery BuildStringLengthQuery(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
754+
{
755+
if (constantExpression.Type != typeof(int))
756+
{
757+
return null;
758+
}
759+
760+
BsonSerializationInfo serializationInfo = null;
761+
var value = ToInt32(constantExpression);
762+
763+
var lengthPropertyExpression = variableExpression as MemberExpression;
764+
if (lengthPropertyExpression != null)
765+
{
766+
if (lengthPropertyExpression.Member.Name == "Length")
767+
{
768+
var memberExpression = lengthPropertyExpression.Expression as MemberExpression;
769+
if (memberExpression != null)
770+
{
771+
serializationInfo = GetSerializationInfo(memberExpression);
772+
if (serializationInfo != null && serializationInfo.NominalType != typeof(string))
773+
{
774+
serializationInfo = null;
775+
}
776+
}
777+
}
778+
}
779+
780+
if (serializationInfo != null)
781+
{
782+
string regex = null;
783+
switch (operatorType)
784+
{
785+
case ExpressionType.NotEqual: case ExpressionType.Equal: regex = @"/^.{" + value.ToString() + "}$/s"; break;
786+
case ExpressionType.GreaterThan: regex = @"/^.{" + (value + 1).ToString() + ",}$/s"; break;
787+
case ExpressionType.GreaterThanOrEqual: regex = @"/^.{" + value.ToString() + ",}$/s"; break;
788+
case ExpressionType.LessThan: regex = @"/^.{0," + (value - 1).ToString() + "}$/s"; break;
789+
case ExpressionType.LessThanOrEqual: regex = @"/^.{0," + value.ToString() + "}$/s"; break;
790+
}
791+
if (regex != null)
792+
{
793+
if (operatorType == ExpressionType.NotEqual)
794+
{
795+
return Query.Not(serializationInfo.ElementName).Matches(regex);
796+
}
797+
else
798+
{
799+
return Query.Matches(serializationInfo.ElementName, regex);
800+
}
801+
}
802+
}
803+
804+
return null;
805+
}
806+
747807
private IMongoQuery BuildStringQuery(MethodCallExpression methodCallExpression)
748808
{
749809
if (methodCallExpression.Method.DeclaringType == typeof(string))

DriverUnitTests/Linq/SelectQueryTests.cs

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public void Setup()
153153
_collection.Insert(new C { Id = _id1, X = 1, Y = 11, D = new D { Z = 11 }, S = "abc", SA = new string[] { "Tom", "Dick", "Harry" } });
154154
_collection.Insert(new C { Id = _id3, X = 3, Y = 33, D = new D { Z = 33 }, B = true, BA = new bool[] { true }, E = E.A, EA = new E[] { E.A, E.B } });
155155
_collection.Insert(new C { Id = _id5, X = 5, Y = 44, D = new D { Z = 55 }, DBRef = new MongoDBRef("db", "c", 1) });
156-
_collection.Insert(new C { Id = _id4, X = 4, Y = 44, D = new D { Z = 44 } });
156+
_collection.Insert(new C { Id = _id4, X = 4, Y = 44, D = new D { Z = 44 }, S = " xyz " });
157157
}
158158

159159
[Test]
@@ -517,8 +517,9 @@ public void TestDistinctS()
517517
var query = (from c in _collection.AsQueryable<C>()
518518
select c.S).Distinct();
519519
var results = query.ToList();
520-
Assert.AreEqual(1, results.Count);
520+
Assert.AreEqual(2, results.Count);
521521
Assert.IsTrue(results.Contains("abc"));
522+
Assert.IsTrue(results.Contains(" xyz "));
522523
}
523524

524525
[Test]
@@ -4421,6 +4422,121 @@ where Regex.IsMatch(c.S, "^abc", RegexOptions.IgnoreCase)
44214422
Assert.AreEqual(1, Consume(query));
44224423
}
44234424

4425+
[Test]
4426+
public void TestWhereSLengthEquals3()
4427+
{
4428+
var query = from c in _collection.AsQueryable<C>()
4429+
where c.S.Length == 3
4430+
select c;
4431+
4432+
var translatedQuery = MongoQueryTranslator.Translate(query);
4433+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4434+
Assert.AreSame(_collection, translatedQuery.Collection);
4435+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4436+
4437+
var selectQuery = (SelectQuery)translatedQuery;
4438+
Assert.AreEqual("(C c) => (c.S.Length == 3)", ExpressionFormatter.ToString(selectQuery.Where));
4439+
Assert.IsNull(selectQuery.OrderBy);
4440+
Assert.IsNull(selectQuery.Projection);
4441+
Assert.IsNull(selectQuery.Skip);
4442+
Assert.IsNull(selectQuery.Take);
4443+
4444+
Assert.AreEqual("{ \"s\" : /^.{3}$/s }", selectQuery.BuildQuery().ToJson());
4445+
Assert.AreEqual(1, Consume(query));
4446+
}
4447+
4448+
[Test]
4449+
public void TestWhereSLengthGreaterThan3()
4450+
{
4451+
var query = from c in _collection.AsQueryable<C>()
4452+
where c.S.Length > 3
4453+
select c;
4454+
4455+
var translatedQuery = MongoQueryTranslator.Translate(query);
4456+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4457+
Assert.AreSame(_collection, translatedQuery.Collection);
4458+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4459+
4460+
var selectQuery = (SelectQuery)translatedQuery;
4461+
Assert.AreEqual("(C c) => (c.S.Length > 3)", ExpressionFormatter.ToString(selectQuery.Where));
4462+
Assert.IsNull(selectQuery.OrderBy);
4463+
Assert.IsNull(selectQuery.Projection);
4464+
Assert.IsNull(selectQuery.Skip);
4465+
Assert.IsNull(selectQuery.Take);
4466+
4467+
Assert.AreEqual("{ \"s\" : /^.{4,}$/s }", selectQuery.BuildQuery().ToJson());
4468+
Assert.AreEqual(1, Consume(query));
4469+
}
4470+
4471+
[Test]
4472+
public void TestWhereSLengthGreaterThanOrEquals3()
4473+
{
4474+
var query = from c in _collection.AsQueryable<C>()
4475+
where c.S.Length >= 3
4476+
select c;
4477+
4478+
var translatedQuery = MongoQueryTranslator.Translate(query);
4479+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4480+
Assert.AreSame(_collection, translatedQuery.Collection);
4481+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4482+
4483+
var selectQuery = (SelectQuery)translatedQuery;
4484+
Assert.AreEqual("(C c) => (c.S.Length >= 3)", ExpressionFormatter.ToString(selectQuery.Where));
4485+
Assert.IsNull(selectQuery.OrderBy);
4486+
Assert.IsNull(selectQuery.Projection);
4487+
Assert.IsNull(selectQuery.Skip);
4488+
Assert.IsNull(selectQuery.Take);
4489+
4490+
Assert.AreEqual("{ \"s\" : /^.{3,}$/s }", selectQuery.BuildQuery().ToJson());
4491+
Assert.AreEqual(2, Consume(query));
4492+
}
4493+
4494+
[Test]
4495+
public void TestWhereSLengthLessThan3()
4496+
{
4497+
var query = from c in _collection.AsQueryable<C>()
4498+
where c.S.Length < 3
4499+
select c;
4500+
4501+
var translatedQuery = MongoQueryTranslator.Translate(query);
4502+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4503+
Assert.AreSame(_collection, translatedQuery.Collection);
4504+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4505+
4506+
var selectQuery = (SelectQuery)translatedQuery;
4507+
Assert.AreEqual("(C c) => (c.S.Length < 3)", ExpressionFormatter.ToString(selectQuery.Where));
4508+
Assert.IsNull(selectQuery.OrderBy);
4509+
Assert.IsNull(selectQuery.Projection);
4510+
Assert.IsNull(selectQuery.Skip);
4511+
Assert.IsNull(selectQuery.Take);
4512+
4513+
Assert.AreEqual("{ \"s\" : /^.{0,2}$/s }", selectQuery.BuildQuery().ToJson());
4514+
Assert.AreEqual(0, Consume(query));
4515+
}
4516+
4517+
[Test]
4518+
public void TestWhereSLengthLessThanOrEquals3()
4519+
{
4520+
var query = from c in _collection.AsQueryable<C>()
4521+
where c.S.Length <= 3
4522+
select c;
4523+
4524+
var translatedQuery = MongoQueryTranslator.Translate(query);
4525+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4526+
Assert.AreSame(_collection, translatedQuery.Collection);
4527+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4528+
4529+
var selectQuery = (SelectQuery)translatedQuery;
4530+
Assert.AreEqual("(C c) => (c.S.Length <= 3)", ExpressionFormatter.ToString(selectQuery.Where));
4531+
Assert.IsNull(selectQuery.OrderBy);
4532+
Assert.IsNull(selectQuery.Projection);
4533+
Assert.IsNull(selectQuery.Skip);
4534+
Assert.IsNull(selectQuery.Take);
4535+
4536+
Assert.AreEqual("{ \"s\" : /^.{0,3}$/s }", selectQuery.BuildQuery().ToJson());
4537+
Assert.AreEqual(1, Consume(query));
4538+
}
4539+
44244540
[Test]
44254541
public void TestWhereSStartsWithAbc()
44264542
{

0 commit comments

Comments
 (0)