Skip to content

Commit b8f2a4b

Browse files
author
rstam
committed
Added support for string IsNullOrEmpty in LINQ queries.
1 parent 58bdc72 commit b8f2a4b

File tree

2 files changed

+90
-38
lines changed

2 files changed

+90
-38
lines changed

Driver/Linq/Translators/SelectQuery.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,27 @@ private IMongoQuery BuildIsMatchQuery(MethodCallExpression methodCallExpression)
629629
return null;
630630
}
631631

632+
private IMongoQuery BuildIsNullOrEmptyQuery(MethodCallExpression methodCallExpression)
633+
{
634+
if (methodCallExpression.Method.DeclaringType == typeof(string) && methodCallExpression.Object == null)
635+
{
636+
var arguments = methodCallExpression.Arguments.ToArray();
637+
if (arguments.Length == 1)
638+
{
639+
var serializationInfo = GetSerializationInfo(arguments[0]);
640+
if (serializationInfo != null)
641+
{
642+
return Query.Or(
643+
Query.Type(serializationInfo.ElementName, BsonType.Null), // this is the safe way to test for null
644+
Query.EQ(serializationInfo.ElementName, "")
645+
);
646+
}
647+
}
648+
}
649+
650+
return null;
651+
}
652+
632653
private IMongoQuery BuildMethodCallQuery(MethodCallExpression methodCallExpression)
633654
{
634655
switch (methodCallExpression.Method.Name)
@@ -642,6 +663,7 @@ private IMongoQuery BuildMethodCallQuery(MethodCallExpression methodCallExpressi
642663
case "In": return BuildInQuery(methodCallExpression);
643664
case "Inject": return BuildInjectQuery(methodCallExpression);
644665
case "IsMatch": return BuildIsMatchQuery(methodCallExpression);
666+
case "IsNullOrEmpty": return BuildIsNullOrEmptyQuery(methodCallExpression);
645667
case "StartsWith": return BuildStringQuery(methodCallExpression);
646668
}
647669
return null;

DriverUnitTests/Linq/SelectQueryTests.cs

Lines changed: 68 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4492,36 +4492,36 @@ public void TestWhereSEndsWithAbcNot()
44924492
[Test]
44934493
public void TestWhereSIndexOfAnyBC()
44944494
{
4495-
var collection = _database.GetCollection("temp");
4496-
collection.Drop();
4497-
collection.Insert(new C { S = "bxxx" });
4498-
collection.Insert(new C { S = "xbxx" });
4499-
collection.Insert(new C { S = "xxbx" });
4500-
collection.Insert(new C { S = "xxxb" });
4501-
collection.Insert(new C { S = "bxbx" });
4502-
collection.Insert(new C { S = "xbbx" });
4503-
collection.Insert(new C { S = "xxbb" });
4495+
var tempCollection = _database.GetCollection("temp");
4496+
tempCollection.Drop();
4497+
tempCollection.Insert(new C { S = "bxxx" });
4498+
tempCollection.Insert(new C { S = "xbxx" });
4499+
tempCollection.Insert(new C { S = "xxbx" });
4500+
tempCollection.Insert(new C { S = "xxxb" });
4501+
tempCollection.Insert(new C { S = "bxbx" });
4502+
tempCollection.Insert(new C { S = "xbbx" });
4503+
tempCollection.Insert(new C { S = "xxbb" });
45044504

45054505
var query1 =
4506-
from c in collection.AsQueryable<C>()
4506+
from c in tempCollection.AsQueryable<C>()
45074507
where c.S.IndexOfAny(new char[] { 'b', 'c' }) == 2
45084508
select c;
45094509
Assert.AreEqual(2, Consume(query1));
45104510

45114511
var query2 =
4512-
from c in collection.AsQueryable<C>()
4512+
from c in tempCollection.AsQueryable<C>()
45134513
where c.S.IndexOfAny(new char[] { 'b', 'c' }, 1) == 2
45144514
select c;
45154515
Assert.AreEqual(3, Consume(query2));
45164516

45174517
var query3 =
4518-
from c in collection.AsQueryable<C>()
4518+
from c in tempCollection.AsQueryable<C>()
45194519
where c.S.IndexOfAny(new char[] { 'b', 'c' }, 1, 1) == 2
45204520
select c;
45214521
Assert.AreEqual(0, Consume(query3));
45224522

45234523
var query4 =
4524-
from c in collection.AsQueryable<C>()
4524+
from c in tempCollection.AsQueryable<C>()
45254525
where c.S.IndexOfAny(new char[] { 'b', 'c' }, 1, 2) == 2
45264526
select c;
45274527
Assert.AreEqual(3, Consume(query4));
@@ -4599,36 +4599,36 @@ where c.S.IndexOfAny(new char[] { 'b', '-', 'c' }, 1, 2) == 1
45994599
[Test]
46004600
public void TestWhereSIndexOfB()
46014601
{
4602-
var collection = _database.GetCollection("temp");
4603-
collection.Drop();
4604-
collection.Insert(new C { S = "bxxx" });
4605-
collection.Insert(new C { S = "xbxx" });
4606-
collection.Insert(new C { S = "xxbx" });
4607-
collection.Insert(new C { S = "xxxb" });
4608-
collection.Insert(new C { S = "bxbx" });
4609-
collection.Insert(new C { S = "xbbx" });
4610-
collection.Insert(new C { S = "xxbb" });
4602+
var tempCollection = _database.GetCollection("temp");
4603+
tempCollection.Drop();
4604+
tempCollection.Insert(new C { S = "bxxx" });
4605+
tempCollection.Insert(new C { S = "xbxx" });
4606+
tempCollection.Insert(new C { S = "xxbx" });
4607+
tempCollection.Insert(new C { S = "xxxb" });
4608+
tempCollection.Insert(new C { S = "bxbx" });
4609+
tempCollection.Insert(new C { S = "xbbx" });
4610+
tempCollection.Insert(new C { S = "xxbb" });
46114611

46124612
var query1 =
4613-
from c in collection.AsQueryable<C>()
4613+
from c in tempCollection.AsQueryable<C>()
46144614
where c.S.IndexOf('b') == 2
46154615
select c;
46164616
Assert.AreEqual(2, Consume(query1));
46174617

46184618
var query2 =
4619-
from c in collection.AsQueryable<C>()
4619+
from c in tempCollection.AsQueryable<C>()
46204620
where c.S.IndexOf('b', 1) == 2
46214621
select c;
46224622
Assert.AreEqual(3, Consume(query2));
46234623

46244624
var query3 =
4625-
from c in collection.AsQueryable<C>()
4625+
from c in tempCollection.AsQueryable<C>()
46264626
where c.S.IndexOf('b', 1, 1) == 2
46274627
select c;
46284628
Assert.AreEqual(0, Consume(query3));
46294629

46304630
var query4 =
4631-
from c in collection.AsQueryable<C>()
4631+
from c in tempCollection.AsQueryable<C>()
46324632
where c.S.IndexOf('b', 1, 2) == 2
46334633
select c;
46344634
Assert.AreEqual(3, Consume(query4));
@@ -4706,35 +4706,35 @@ where c.S.IndexOf('b', 1, 2) == 1
47064706
[Test]
47074707
public void TestWhereSIndexOfXyz()
47084708
{
4709-
var collection = _database.GetCollection("temp");
4710-
collection.Drop();
4711-
collection.Insert(new C { S = "xyzaaa" });
4712-
collection.Insert(new C { S = "axyzaa" });
4713-
collection.Insert(new C { S = "aaxyza" });
4714-
collection.Insert(new C { S = "aaaxyz" });
4715-
collection.Insert(new C { S = "aaaaxy" });
4716-
collection.Insert(new C { S = "xyzxyz" });
4709+
var tempCollection = _database.GetCollection("temp");
4710+
tempCollection.Drop();
4711+
tempCollection.Insert(new C { S = "xyzaaa" });
4712+
tempCollection.Insert(new C { S = "axyzaa" });
4713+
tempCollection.Insert(new C { S = "aaxyza" });
4714+
tempCollection.Insert(new C { S = "aaaxyz" });
4715+
tempCollection.Insert(new C { S = "aaaaxy" });
4716+
tempCollection.Insert(new C { S = "xyzxyz" });
47174717

47184718
var query1 =
4719-
from c in collection.AsQueryable<C>()
4719+
from c in tempCollection.AsQueryable<C>()
47204720
where c.S.IndexOf("xyz") == 3
47214721
select c;
47224722
Assert.AreEqual(1, Consume(query1));
47234723

47244724
var query2 =
4725-
from c in collection.AsQueryable<C>()
4725+
from c in tempCollection.AsQueryable<C>()
47264726
where c.S.IndexOf("xyz", 1) == 3
47274727
select c;
47284728
Assert.AreEqual(2, Consume(query2));
47294729

47304730
var query3 =
4731-
from c in collection.AsQueryable<C>()
4731+
from c in tempCollection.AsQueryable<C>()
47324732
where c.S.IndexOf("xyz", 1, 4) == 3
47334733
select c;
47344734
Assert.AreEqual(0, Consume(query3)); // substring isn't long enough to match
47354735

47364736
var query4 =
4737-
from c in collection.AsQueryable<C>()
4737+
from c in tempCollection.AsQueryable<C>()
47384738
where c.S.IndexOf("xyz", 1, 5) == 3
47394739
select c;
47404740
Assert.AreEqual(2, Consume(query4));
@@ -4926,6 +4926,36 @@ where Regex.IsMatch(c.S, "^abc", RegexOptions.IgnoreCase)
49264926
Assert.AreEqual(1, Consume(query));
49274927
}
49284928

4929+
[Test]
4930+
public void TestWhereSIsNullOrEmpty()
4931+
{
4932+
var tempCollection = _database.GetCollection("temp");
4933+
tempCollection.Drop();
4934+
tempCollection.Insert(new C()); // serialized document will have no "s" field
4935+
tempCollection.Insert(new BsonDocument("s", BsonNull.Value)); // work around [BsonIgnoreIfNull] on S
4936+
tempCollection.Insert(new C { S = "" });
4937+
tempCollection.Insert(new C { S = "x" });
4938+
4939+
var query = from c in tempCollection.AsQueryable<C>()
4940+
where string.IsNullOrEmpty(c.S)
4941+
select c;
4942+
4943+
var translatedQuery = MongoQueryTranslator.Translate(query);
4944+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
4945+
Assert.AreSame(tempCollection, translatedQuery.Collection);
4946+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
4947+
4948+
var selectQuery = (SelectQuery)translatedQuery;
4949+
Assert.AreEqual("(C c) => String.IsNullOrEmpty(c.S)", ExpressionFormatter.ToString(selectQuery.Where));
4950+
Assert.IsNull(selectQuery.OrderBy);
4951+
Assert.IsNull(selectQuery.Projection);
4952+
Assert.IsNull(selectQuery.Skip);
4953+
Assert.IsNull(selectQuery.Take);
4954+
4955+
Assert.AreEqual("{ \"$or\" : [{ \"s\" : { \"$type\" : 10 } }, { \"s\" : \"\" }] }", selectQuery.BuildQuery().ToJson());
4956+
Assert.AreEqual(2, Consume(query));
4957+
}
4958+
49294959
[Test]
49304960
public void TestWhereSLengthEquals3()
49314961
{

0 commit comments

Comments
 (0)