Skip to content

Commit 22cbd96

Browse files
Einar Egilssoncraiggwilson
authored andcommitted
Add .Explain() extension method to IQueryable<T> to explain Linq queries.
The extension method operates on IQueryable<T> and can therefore not operate on .Max, Min, First, Last or any methods that return a scalar. It can also not operate on .Distinct queries since they call a specific method on the collection and don't give us access to any cursor.
1 parent 503adf0 commit 22cbd96

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

DriverUnitTests/Linq/ExplainTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
???/* Copyright 2010-2012 10gen Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Linq;
18+
using NUnit.Framework;
19+
20+
using MongoDB.Bson;
21+
using MongoDB.Driver;
22+
using MongoDB.Driver.Builders;
23+
using MongoDB.Driver.Linq;
24+
25+
namespace MongoDB.DriverUnitTests.Linq
26+
{
27+
[TestFixture]
28+
public class ExplainTests
29+
{
30+
private class C
31+
{
32+
public ObjectId Id { get; set; }
33+
public int X { get; set; }
34+
public int Y { get; set; }
35+
}
36+
37+
private MongoServer _server;
38+
private MongoCollection _collection;
39+
40+
[TestFixtureSetUp]
41+
public void Setup()
42+
{
43+
_server = Configuration.TestServer;
44+
_server.Connect();
45+
_collection = Configuration.TestCollection;
46+
}
47+
48+
[Test]
49+
public void TestExplainFromLinqQueryEqualsExplainFromCursor()
50+
{
51+
var linqExplain = _collection.AsQueryable<C>().Where(c => c.X == 2 && c.Y == 1).Take(1).Explain();
52+
var queryExplain =
53+
_collection.FindAs<C>(Query.And(Query.EQ("X", 2), Query.EQ("Y", 1))).SetLimit(1).Explain();
54+
Assert.AreEqual(linqExplain, queryExplain);
55+
}
56+
57+
[Test]
58+
public void TestVerboseExplainFromLinqQueryEqualsVerboseExplainFromCursor()
59+
{
60+
var linqExplain = _collection.AsQueryable<C>().Where(c => c.X == 2 && c.Y == 1).Take(1).Explain(true);
61+
var queryExplain =
62+
_collection.FindAs<C>(Query.And(Query.EQ("X", 2), Query.EQ("Y", 1))).SetLimit(1).Explain(true);
63+
Assert.AreEqual(linqExplain, queryExplain);
64+
}
65+
66+
[Test]
67+
public void TestDistinctQueryCannotBeExplained()
68+
{
69+
Assert.Throws<NotSupportedException>(()=> _collection.AsQueryable<C>().Select(c=>c.X).Distinct().Explain());
70+
}
71+
72+
[Test]
73+
public void TestTakeZeroQueriesCannotBeExplained()
74+
{
75+
Assert.Throws<NotSupportedException>(() => _collection.AsQueryable<C>().Take(0).Explain());
76+
}
77+
}
78+
}

MongoDB.Driver/Linq/LinqToMongo.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2013 10gen Inc.
1+
/* Copyright 2010-2012 10gen Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19+
using MongoDB.Bson;
1920

2021
namespace MongoDB.Driver.Linq
2122
{
@@ -48,6 +49,7 @@ public static bool ContainsAny<TSource>(this IEnumerable<TSource> source, IEnume
4849
return source.Any(s => values.Contains(s));
4950
}
5051

52+
5153
/// <summary>
5254
/// Determines whether a specified value is contained in a sequence.
5355
/// </summary>
@@ -69,5 +71,38 @@ public static bool Inject(this IMongoQuery query)
6971
{
7072
throw new InvalidOperationException("The LinqToMongo.Inject method is only intended to be used in LINQ Where clauses.");
7173
}
74+
75+
/// <summary>
76+
/// Returns an explanation of how the query was executed (instead of the results).
77+
/// </summary>
78+
/// <param name="query">The LINQ query to explain</param>
79+
/// <returns>An explanation of thow the query was executed.</returns>
80+
public static BsonDocument Explain<T>(this IQueryable<T> query)
81+
{
82+
return Explain(query, false);
83+
}
84+
85+
/// <summary>
86+
/// Returns an explanation of how the query was executed (instead of the results).
87+
/// </summary>
88+
/// <param name="query">The LINQ query to explain</param>
89+
/// <param name="verbose">Whether the explanation should contain more details.</param>
90+
/// <returns>An explanation of thow the query was executed.</returns>
91+
public static BsonDocument Explain<T>(this IQueryable<T> query, bool verbose)
92+
{
93+
var selectQuery = (SelectQuery)MongoQueryTranslator.Translate(query);
94+
if (selectQuery.Take.HasValue && selectQuery.Take.Value == 0)
95+
{
96+
throw new NotSupportedException("A query that has a .Take(0) expression will not be sent to the server and can't be explained");
97+
}
98+
var projector = selectQuery.Execute() as IProjector;
99+
if (projector == null)
100+
{
101+
//This is mainly for .Distinct() queries. First, Last, FirstOrDefault, LastOrDefault don't return
102+
//IQueryable<T>, so .Explain() can't be called on them anyway.
103+
throw new NotSupportedException("Explain can only be called on Linq queries that return an IProjector");
104+
}
105+
return projector.Cursor.Explain(verbose);
106+
}
72107
}
73108
}

MongoDB.DriverUnitTests/MongoDB.DriverUnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
<Compile Include="Jira\CSharp93Tests.cs" />
185185
<Compile Include="Jira\CSharp98Tests.cs" />
186186
<Compile Include="Jira\CSharp100Tests.cs" />
187+
<Compile Include="Linq\ExplainTests.cs" />
187188
<Compile Include="Linq\SelectDictionaryTests.cs" />
188189
<Compile Include="Linq\SelectNullableTests.cs" />
189190
<Compile Include="Linq\SelectOfTypeHierarchicalTests.cs" />

0 commit comments

Comments
 (0)