Skip to content

Commit 21b6b06

Browse files
CSHARP-2743: Update type validation and add tests
1 parent 22eb59f commit 21b6b06

File tree

4 files changed

+394
-6
lines changed

4 files changed

+394
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver.
150150
* staywellandy https://github.com/staywellandy
151151
* Vyacheslav Stroy https://github.com/kreig
152152
153+
* TimTim https://github.com/wegylexy
153154
* Zhmayev Yaroslav https://github.com/salaros
154155
* Aristarkh Zagorodnikov https://github.com/onyxmaster
155156

src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ public static PipelineStageDefinition<TInput, TOutput> GraphLookup<TInput, TFrom
489489
Ensure.IsNotNull(connectToField, nameof(connectToField));
490490
Ensure.IsNotNull(startWith, nameof(startWith));
491491
Ensure.IsNotNull(@as, nameof(@as));
492-
Ensure.That(IsTConnectToEnumerableTConnectToOrViceVersa<TConnectFrom, TConnectTo>(), "TConnectFrom must be either TConnectTo or a type that implements IEnumerable<TConnectTo> unless TConnectTo is a type that implements IEnumerable<TConnectFrom>.", nameof(TConnectFrom));
493-
Ensure.That(IsTConnectToEnumerableTConnectToOrViceVersa<TStartWith, TConnectTo>(), "TStartWith must be either TConnectTo or a type that implements IEnumerable<TConnectTo> unless TConnectTo is a type that implements IEnumerable<TConnectStart>.", nameof(TStartWith));
492+
Ensure.That(AreGraphLookupFromAndToTypesCompatible<TConnectFrom, TConnectTo>(), "TConnectFrom and TConnectTo are not compatible", nameof(TConnectFrom));
493+
Ensure.That(AreGraphLookupFromAndToTypesCompatible<TStartWith, TConnectTo>(), "TStartWith and TConnectTo are not compatible", nameof(TStartWith));
494494

495495
const string operatorName = "$graphLookup";
496496
var stage = new DelegatedPipelineStageDefinition<TInput, TOutput>(
@@ -1429,11 +1429,26 @@ public static PipelineStageDefinition<TInput, TOutput> Unwind<TInput, TOutput>(
14291429
}
14301430

14311431
// private methods
1432-
private static bool IsTConnectToEnumerableTConnectToOrViceVersa<TConnectFrom, TConnectTo>()
1432+
private static bool AreGraphLookupFromAndToTypesCompatible<TConnectFrom, TConnectTo>()
14331433
{
1434-
return typeof(TConnectFrom) == typeof(TConnectTo) ||
1435-
typeof(TConnectFrom).GetTypeInfo().GetInterfaces().Contains(typeof(IEnumerable<>).MakeGenericType(typeof(TConnectTo)))) ||
1436-
typeof(TConnectTo).GetTypeInfo().GetInterfaces().Contains(typeof(IEnumerable<>).MakeGenericType(typeof(TConnectFrom))));
1434+
if (typeof(TConnectFrom) == typeof(TConnectTo))
1435+
{
1436+
return true;
1437+
}
1438+
1439+
var ienumerableTConnectTo = typeof(IEnumerable<>).MakeGenericType(typeof(TConnectTo));
1440+
if (ienumerableTConnectTo.GetTypeInfo().IsAssignableFrom(typeof(TConnectFrom)))
1441+
{
1442+
return true;
1443+
}
1444+
1445+
var ienumerableTConnectFrom = typeof(IEnumerable<>).MakeGenericType(typeof(TConnectFrom));
1446+
if (ienumerableTConnectFrom.GetTypeInfo().IsAssignableFrom(typeof(TConnectTo)))
1447+
{
1448+
return true;
1449+
}
1450+
1451+
return false;
14371452
}
14381453
}
14391454

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/* Copyright 2020-present MongoDB 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.Collections.Generic;
17+
using FluentAssertions;
18+
using MongoDB.Bson;
19+
using MongoDB.Driver.Core.Misc;
20+
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
21+
using Xunit;
22+
23+
namespace MongoDB.Driver.Tests
24+
{
25+
public class AggregateGraphLookupEnumerableFromOrToTests
26+
{
27+
// public methods
28+
[SkippableFact]
29+
public void GraphLookup_with_many_to_one_parameters_should_return_expected_result()
30+
{
31+
RequireServer.Check().Supports(Feature.AggregateGraphLookupStage);
32+
var database = GetDatabase();
33+
var collectionName = "collectionManyToOne";
34+
EnsureTestDataManyToOne(database, collectionName);
35+
var expectedResult = new ManyToOneResult[]
36+
{
37+
new ManyToOneResult
38+
{
39+
Id = 1,
40+
From = new int[] { 2, 3 },
41+
To = 1,
42+
Matches = new List<ManyToOne> { new ManyToOne { Id = 2, From = new[] { 3, 4 }, To = 2 } }
43+
},
44+
new ManyToOneResult
45+
{
46+
Id = 2,
47+
From = new[] { 3, 4 },
48+
To = 2,
49+
Matches = new List<ManyToOne>()
50+
}
51+
};
52+
var collection = database.GetCollection<ManyToOne>(collectionName);
53+
54+
var result = collection
55+
.Aggregate()
56+
.GraphLookup(
57+
from: collection,
58+
connectFromField: x => x.From,
59+
connectToField: x => x.To,
60+
startWith: x => x.From,
61+
@as: (ManyToOneResult x) => x.Matches)
62+
.ToList();
63+
64+
result.Count.Should().Be(2);
65+
result[0].ToBsonDocument().Should().Be(expectedResult[0].ToBsonDocument());
66+
result[1].ToBsonDocument().Should().Be(expectedResult[1].ToBsonDocument());
67+
}
68+
69+
[SkippableFact]
70+
public void GraphLookup_with_one_to_many_parameters_should_return_expected_result()
71+
{
72+
RequireServer.Check().Supports(Feature.AggregateGraphLookupStage);
73+
var database = GetDatabase();
74+
var collectionName = "collectionOneToMany";
75+
EnsureTestDataOneToMany(database, collectionName);
76+
var expectedResult = new OneToManyResult[]
77+
{
78+
new OneToManyResult
79+
{
80+
Id = 1,
81+
From = 1,
82+
To = new[] { 2, 3 },
83+
Matches = new List<OneToMany>()
84+
},
85+
new OneToManyResult
86+
{
87+
Id = 2,
88+
From = 2,
89+
To = new[] { 3, 4 },
90+
Matches = new List<OneToMany> { new OneToMany { Id = 1, From = 1, To = new[] { 2, 3 } } }
91+
}
92+
};
93+
var collection = database.GetCollection<OneToMany>(collectionName);
94+
95+
var result = collection
96+
.Aggregate()
97+
.GraphLookup(
98+
from: collection,
99+
connectFromField: x => x.From,
100+
connectToField: x => x.To,
101+
startWith: x => x.From,
102+
@as: (OneToManyResult x) => x.Matches)
103+
.ToList();
104+
105+
result.Count.Should().Be(2);
106+
result[0].ToBsonDocument().Should().Be(expectedResult[0].ToBsonDocument());
107+
result[1].ToBsonDocument().Should().Be(expectedResult[1].ToBsonDocument());
108+
}
109+
110+
[SkippableFact]
111+
public void GraphLookup_with_one_to_one_parameters_should_return_expected_result()
112+
{
113+
RequireServer.Check().Supports(Feature.AggregateGraphLookupStage);
114+
var database = GetDatabase();
115+
var collectionName = "collectionOneToOne";
116+
EnsureTestDataOneToOne(database, collectionName);
117+
var expectedResult = new OneToOneResult[]
118+
{
119+
new OneToOneResult
120+
{
121+
Id = 1,
122+
From = 1,
123+
To = 2,
124+
Matches = new List<OneToOne>()
125+
},
126+
new OneToOneResult
127+
{
128+
Id = 2,
129+
From = 2,
130+
To = 3,
131+
Matches = new List<OneToOne> { new OneToOne { Id = 1, From = 1, To = 2 } }
132+
}
133+
};
134+
var collection = database.GetCollection<OneToOne>(collectionName);
135+
136+
var result = collection
137+
.Aggregate()
138+
.GraphLookup(
139+
from: collection,
140+
connectFromField: x => x.From,
141+
connectToField: x => x.To,
142+
startWith: x => x.From,
143+
@as: (OneToOneResult x) => x.Matches)
144+
.ToList();
145+
146+
result.Count.Should().Be(2);
147+
result[0].ToBsonDocument().Should().Be(expectedResult[0].ToBsonDocument());
148+
result[1].ToBsonDocument().Should().Be(expectedResult[1].ToBsonDocument());
149+
}
150+
151+
// private methods
152+
private void EnsureTestDataOneToOne(IMongoDatabase database, string collectionName)
153+
{
154+
database.DropCollection(collectionName);
155+
var collection = database.GetCollection<OneToOne>(collectionName);
156+
var documents = new OneToOne[]
157+
{
158+
new OneToOne { Id = 1, From = 1, To = 2 },
159+
new OneToOne { Id = 2, From = 2, To = 3 },
160+
};
161+
collection.InsertMany(documents);
162+
}
163+
164+
private void EnsureTestDataOneToMany(IMongoDatabase database, string collectionName)
165+
{
166+
database.DropCollection(collectionName);
167+
var collection = database.GetCollection<OneToMany>(collectionName);
168+
var documents = new OneToMany[]
169+
{
170+
new OneToMany { Id = 1, From = 1, To = new[] { 2, 3 } },
171+
new OneToMany { Id = 2, From = 2, To = new[] { 3, 4 } },
172+
};
173+
collection.InsertMany(documents);
174+
}
175+
176+
private void EnsureTestDataManyToOne(IMongoDatabase database, string collectionName)
177+
{
178+
database.DropCollection(collectionName);
179+
var collection = database.GetCollection<ManyToOne>(collectionName);
180+
var documents = new ManyToOne[]
181+
{
182+
new ManyToOne { Id = 1, From = new[] { 2, 3 }, To = 1 },
183+
new ManyToOne { Id = 2, From = new[] { 3, 4 }, To = 2 },
184+
};
185+
collection.InsertMany(documents);
186+
}
187+
188+
private IMongoDatabase GetDatabase()
189+
{
190+
var client = DriverTestConfiguration.Client;
191+
var databaseName = CoreTestConfiguration.DatabaseNamespace.DatabaseName;
192+
return client.GetDatabase(databaseName);
193+
}
194+
195+
// nested types
196+
private class ManyToOne
197+
{
198+
public int Id { get; set; }
199+
public IEnumerable<int> From { get; set; }
200+
public int To { get; set; }
201+
}
202+
203+
private class ManyToOneResult
204+
{
205+
public int Id { get; set; }
206+
public IEnumerable<int> From { get; set; }
207+
public int To { get; set; }
208+
public List<ManyToOne> Matches { get; set; }
209+
}
210+
211+
private class OneToMany
212+
{
213+
public int Id { get; set; }
214+
public int From { get; set; }
215+
public IEnumerable<int> To { get; set; }
216+
}
217+
218+
private class OneToManyResult
219+
{
220+
public int Id { get; set; }
221+
public int From { get; set; }
222+
public IEnumerable<int> To { get; set; }
223+
public List<OneToMany> Matches { get; set; }
224+
}
225+
226+
private class OneToOne
227+
{
228+
public int Id { get; set; }
229+
public int From { get; set; }
230+
public int To { get; set; }
231+
}
232+
233+
private class OneToOneResult
234+
{
235+
public int Id { get; set; }
236+
public int From { get; set; }
237+
public int To { get; set; }
238+
public List<OneToOne> Matches { get; set; }
239+
}
240+
}
241+
}

0 commit comments

Comments
 (0)