Permalink
Browse files

Additional work on CSHARP-433. Implemented "is" operator in LINQ quer…

…ies.
  • Loading branch information...
rstam
rstam committed Apr 13, 2012
1 parent 3b46a19 commit 8bf97a816a3b189ec44a725f7f9f2164f68f3e64
@@ -362,7 +362,11 @@ protected override Expression VisitParameter(ParameterExpression node)
/// <returns>The TypeBinaryExpression.</returns>
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
{
- _sb.Append("<TypeBinaryExpression>");
+ _sb.Append("(");
+ Visit(node.Expression);
+ _sb.Append(" is ");
+ _sb.Append(FriendlyClassName(node.TypeOperand));
+ _sb.Append(")");
return node;
}
@@ -332,7 +332,14 @@ protected override Expression VisitParameter(ParameterExpression node)
/// <returns>The TypeBinaryExpression.</returns>
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
{
- throw new NotImplementedException();
+ WriteHeader(node);
+ using (new Indentation(this))
+ {
+ WriteLine("TypeOperand={0}", FriendlyClassName(node.TypeOperand));
+ WriteLine("Expression:");
+ VisitIndented(node.Expression);
+ }
+ return node;
}
/// <summary>
@@ -844,6 +844,9 @@ private IMongoQuery BuildQuery(Expression expression)
case ExpressionType.OrElse:
query = BuildOrElseQuery((BinaryExpression)expression);
break;
+ case ExpressionType.TypeIs:
+ query = BuildTypeIsQuery((TypeBinaryExpression)expression);
+ break;
}
if (query == null)
@@ -1248,6 +1251,26 @@ private IMongoQuery BuildStringQuery(MethodCallExpression methodCallExpression)
return null;
}
+ private IMongoQuery BuildTypeIsQuery(TypeBinaryExpression typeBinaryExpression)
+ {
+ var nominalType = typeBinaryExpression.Expression.Type;
+ var actualType = typeBinaryExpression.TypeOperand;
+
+ var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType);
+ var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType);
+ if (discriminator == null)
+ {
+ return Query.Not("_").Mod(1, 2); // best query I could come up with that's always true
+ }
+
+ if (discriminator.IsBsonArray)
+ {
+ discriminator = discriminator.AsBsonArray[discriminator.AsBsonArray.Count - 1];
+ }
+
+ return Query.EQ(discriminatorConvention.ElementName, discriminator);
+ }
+
private void CombinePredicateWithWhereClause(MethodCallExpression methodCallExpression, LambdaExpression predicate)
{
if (predicate != null)
@@ -1870,25 +1893,25 @@ private void TranslateOfType(MethodCallExpression methodCallExpression)
}
var nominalType = sourceExpression.Type.GetGenericArguments()[0];
- if (nominalType == actualType)
- {
- return; // nothing to do
- }
-
if (_projection != null)
{
throw new NotSupportedException("OfType after a projection is not supported.");
}
var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType);
var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType);
+ if (discriminator == null)
+ {
+ return; // nothing to do
+ }
+
if (discriminator.IsBsonArray)
{
discriminator = discriminator.AsBsonArray[discriminator.AsBsonArray.Count - 1];
}
+ var query = Query.EQ(discriminatorConvention.ElementName, discriminator);
var injectMethodInfo = typeof(LinqToMongo).GetMethod("Inject");
- var query = Query.EQ("_t", discriminator);
var body = Expression.Call(injectMethodInfo, Expression.Constant(query));
var parameter = Expression.Parameter(nominalType, "x");
var predicate = Expression.Lambda(body, parameter);
@@ -75,14 +75,14 @@ public void TestOfTypeB()
Assert.AreSame(typeof(B), translatedQuery.DocumentType);
var selectQuery = (SelectQuery)translatedQuery;
- Assert.IsNull(selectQuery.Where);
- Assert.AreEqual(null, selectQuery.OfType); // OfType ignored because <T> was the same as <TDocument>
+ Assert.AreEqual("(B x) => LinqToMongo.Inject({ \"_t\" : \"B\" })", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(typeof(B), selectQuery.OfType);
Assert.IsNull(selectQuery.OrderBy);
Assert.IsNull(selectQuery.Projection);
Assert.IsNull(selectQuery.Skip);
Assert.IsNull(selectQuery.Take);
- Assert.IsNull(selectQuery.BuildQuery());
+ Assert.AreEqual("{ \"_t\" : \"B\" }", selectQuery.BuildQuery().ToJson());
Assert.AreEqual(3, Consume(query));
}
@@ -174,6 +174,81 @@ public void TestWhereBGreaterThan0OfTypeCWhereCGreaterThan0()
Assert.AreEqual(2, Consume(query));
}
+ [Test]
+ public void TestWhereBIsB()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is B
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is B)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType); // OfType ignored because <T> was the same as <TDocument>
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_t\" : \"B\" }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(3, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereBIsC()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is C
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is C)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType);
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_t\" : \"C\" }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(2, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereBIsD()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is D
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is D)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType);
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_t\" : \"D\" }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
private int Consume<T>(IQueryable<T> query)
{
var count = 0;
@@ -173,6 +173,81 @@ public void TestWhereBGreaterThan0OfTypeCWhereCGreaterThan0()
Assert.AreEqual(1, Consume(query)); // should match 2 but for that you need to use the hierarchical discriminator
}
+ [Test]
+ public void TestWhereBIsB()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is B
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is B)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType); // OfType ignored because <T> was the same as <TDocument>
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_\" : { \"$not\" : { \"$mod\" : [1, 2] } } }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(3, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereBIsC()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is C
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is C)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType);
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_t\" : \"C\" }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query)); // should match 2 but for that you need to use the hierarchical discriminator
+ }
+
+ [Test]
+ public void TestWhereBIsD()
+ {
+ var query =
+ from b in _collection.AsQueryable<B>()
+ where b is D
+ select b;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(B), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(B b) => (b is D)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.AreEqual(null, selectQuery.OfType);
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"_t\" : \"D\" }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
private int Consume<T>(IQueryable<T> query)
{
var count = 0;

0 comments on commit 8bf97a8

Please sign in to comment.