Skip to content
This repository
Browse code

Fixed issue with SimpleQuery joins... it was complicated.

  • Loading branch information...
commit bd4d0f40cd64c10f7b7d9e20e0402968d21a54c4 1 parent 927cc4c
Mark Rendle authored December 04, 2012
26  Simple.Data.InMemoryTest/InMemoryTests.cs
@@ -697,5 +697,31 @@ public void JoinTest()
697 697
             Assert.That(detail.Id, Is.EqualTo(masterId));
698 698
             Assert.That(detail.Box, Is.EqualTo(999));
699 699
         }
  700
+
  701
+        [Test]
  702
+        public void LeftJoinTest()
  703
+        {
  704
+            var adapter = new InMemoryAdapter();
  705
+            adapter.SetKeyColumn("Events", "Id");
  706
+            adapter.SetAutoIncrementColumn("Events", "Id");
  707
+            adapter.SetKeyColumn("Doors", "Id");
  708
+            adapter.SetAutoIncrementColumn("Doors", "Id");
  709
+            adapter.Join.Master("Events", "Id").Detail("Doors", "EventId");
  710
+            Database.UseMockAdapter(adapter);
  711
+            var db = Database.Open();
  712
+            db.Events.Insert(Id: 1, Code: "CodeMash2013", Name: "CodeMash 2013");
  713
+            db.Events.Insert(Id: 2, Code: "SomewhereElse", Name: "Some Other Conf");
  714
+            db.Doors.Insert(Id: 1, Code: "F7E08AC9-5E75-417D-A7AA-60E88B5B99AD", EventID: 1);
  715
+            db.Doors.Insert(Id: 2, Code: "0631C802-2748-4C63-A6D9-CE8C803002EB", EventID: 1);
  716
+            db.Doors.Insert(Id: 3, Code: "281ED88F-677D-49B9-84FA-4FAE022BBC73", EventID: 1);
  717
+            db.Doors.Insert(Id: 4, Code: "9DF7E964-1ECE-42E3-8211-1F2BF7054A0D", EventID: 2);
  718
+            db.Doors.Insert(Id: 5, Code: "9418123D-312A-4E8C-8807-59F0A63F43B9", EventID: 2);
  719
+
  720
+            List<dynamic> actual = db.Doors.FindAll(db.Doors.Events.Code == "CodeMash2013")
  721
+                           .Select(db.Doors.Id, db.Events.Name)
  722
+                           .ToList();
  723
+
  724
+            Assert.AreEqual(3, actual.Count);
  725
+        }
700 726
     }
701 727
 }
40  Simple.Data.UnitTest/DictionaryQueryRunnerTest.cs
@@ -14,7 +14,7 @@ public class DictionaryQueryRunnerTest
14 14
         [Test]
15 15
         public void DistinctShouldRemoveDuplicateRows()
16 16
         {
17  
-            var runner = new DictionaryQueryRunner(DuplicatingSource(), new DistinctClause());
  17
+            var runner = new DictionaryQueryRunner("FooTable", DuplicatingSource(), new DistinctClause());
18 18
             var actual = runner.Run().ToList();
19 19
             Assert.AreEqual(2, actual.Count);
20 20
             Assert.AreEqual(1, actual.Count(d => d.ContainsKey("Foo") && (string)d["Foo"] == "bar"));
@@ -24,7 +24,7 @@ public void DistinctShouldRemoveDuplicateRows()
24 24
         [Test]
25 25
         public void ShouldNotRemoveDistinctRows()
26 26
         {
27  
-            var runner = new DictionaryQueryRunner(NonDuplicatingSource(), new DistinctClause());
  27
+            var runner = new DictionaryQueryRunner("FooTable", NonDuplicatingSource(), new DistinctClause());
28 28
             var actual = runner.Run().ToList();
29 29
             Assert.AreEqual(2, actual.Count);
30 30
             Assert.AreEqual(1, actual.Count(d => d.ContainsKey("Foo") && (string)d["Foo"] == "bar"));
@@ -34,7 +34,7 @@ public void ShouldNotRemoveDistinctRows()
34 34
         [Test]
35 35
         public void SkipShouldSkip()
36 36
         {
37  
-            var runner = new DictionaryQueryRunner(SkipTakeSource(), new SkipClause(1));
  37
+            var runner = new DictionaryQueryRunner("FooTable", SkipTakeSource(), new SkipClause(1));
38 38
             var actual = runner.Run().ToList();
39 39
             Assert.AreEqual(2, actual.Count);
40 40
             Assert.AreEqual(1, actual[0]["Row"]);
@@ -44,7 +44,7 @@ public void SkipShouldSkip()
44 44
         [Test]
45 45
         public void TakeShouldTake()
46 46
         {
47  
-            var runner = new DictionaryQueryRunner(SkipTakeSource(), new TakeClause(2));
  47
+            var runner = new DictionaryQueryRunner("FooTable", SkipTakeSource(), new TakeClause(2));
48 48
             var actual = runner.Run().ToList();
49 49
             Assert.AreEqual(2, actual.Count);
50 50
             Assert.AreEqual(0, actual[0]["Row"]);
@@ -54,7 +54,7 @@ public void TakeShouldTake()
54 54
         [Test]
55 55
         public void SkipAndTakeShouldSkipAndTake()
56 56
         {
57  
-            var runner = new DictionaryQueryRunner(SkipTakeSource(), new SkipClause(1), new TakeClause(1));
  57
+            var runner = new DictionaryQueryRunner("FooTable", SkipTakeSource(), new SkipClause(1), new TakeClause(1));
58 58
             var actual = runner.Run().ToList();
59 59
             Assert.AreEqual(1, actual.Count);
60 60
             Assert.AreEqual(1, actual[0]["Row"]);
@@ -64,7 +64,7 @@ public void SkipAndTakeShouldSkipAndTake()
64 64
         public void SkipAndTakeWithCountShouldSkipAndTakeAndGiveCount()
65 65
         {
66 66
             int count = 0;
67  
-            var runner = new DictionaryQueryRunner(SkipTakeSource(), new WithCountClause(n => count = n), new SkipClause(1), new TakeClause(1));
  67
+            var runner = new DictionaryQueryRunner("FooTable", SkipTakeSource(), new WithCountClause(n => count = n), new SkipClause(1), new TakeClause(1));
68 68
             var actual = runner.Run().ToList();
69 69
             Assert.AreEqual(3, count);
70 70
             Assert.AreEqual(1, actual.Count);
@@ -76,7 +76,7 @@ public void SelectShouldRestrictColumnList()
76 76
         {
77 77
             var tableRef = new ObjectReference("FooTable");
78 78
             var selectClause = new SelectClause(new SimpleReference[] { new ObjectReference("Id", tableRef), new ObjectReference("Name", tableRef) });
79  
-            var runner = new DictionaryQueryRunner(SelectSource(), selectClause);
  79
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), selectClause);
80 80
             var actual = runner.Run().ToList();
81 81
             Assert.AreEqual(4, actual.Count);
82 82
             Assert.AreEqual(2, actual[0].Count);
@@ -99,7 +99,7 @@ public void SelectLengthShouldUseLengthFunction()
99 99
             var tableRef = new ObjectReference("FooTable");
100 100
             var function = new FunctionReference("Length", new ObjectReference("Name", tableRef)).As("NameLength");
101 101
             var selectClause = new SelectClause(new SimpleReference[] { new ObjectReference("Name", tableRef), function });
102  
-            var runner = new DictionaryQueryRunner(SelectSource(), selectClause);
  102
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), selectClause);
103 103
             var actual = runner.Run().ToList();
104 104
             Assert.AreEqual(4, actual.Count);
105 105
             Assert.AreEqual(2, actual[0].Count);
@@ -121,7 +121,7 @@ public void BasicWhereEqualShouldWork()
121 121
         {
122 122
             var tableRef = new ObjectReference("FooTable");
123 123
             var whereClause = new WhereClause(new ObjectReference("Name", tableRef) == "Alice");
124  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  124
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
125 125
             var actual = runner.Run().ToList();
126 126
             Assert.AreEqual(1, actual.Count);
127 127
             Assert.AreEqual("Alice", actual[0]["Name"]);
@@ -143,7 +143,7 @@ public void WhereNullShouldWorkWhenValueExistsAndIsNull()
143 143
                                        {"Name", "Dave"}, { "Value", 42 }
144 144
                                    },
145 145
                            };
146  
-            var runner = new DictionaryQueryRunner(data, whereClause);
  146
+            var runner = new DictionaryQueryRunner("FooTable", data, whereClause);
147 147
             var actual = runner.Run().ToList();
148 148
             Assert.AreEqual(1, actual.Count);
149 149
             Assert.AreEqual("Steve", actual[0]["Name"]);
@@ -165,7 +165,7 @@ public void WhereNullShouldWorkWhenValueDoesNotExist()
165 165
                                        {"Name", "Dave"}, { "Value", 42 }
166 166
                                    },
167 167
                            };
168  
-            var runner = new DictionaryQueryRunner(data, whereClause);
  168
+            var runner = new DictionaryQueryRunner("FooTable", data, whereClause);
169 169
             var actual = runner.Run().ToList();
170 170
             Assert.AreEqual(1, actual.Count);
171 171
             Assert.AreEqual("Steve", actual[0]["Name"]);
@@ -187,7 +187,7 @@ public void WhereEqualWithByteArrayShouldWork()
187 187
                                        {"Name", "Dave"}, { "Array", new byte[] { 2, 3, 4}}
188 188
                                    },
189 189
                            };
190  
-            var runner = new DictionaryQueryRunner(data, whereClause);
  190
+            var runner = new DictionaryQueryRunner("FooTable", data, whereClause);
191 191
             var actual = runner.Run().ToList();
192 192
             Assert.AreEqual(1, actual.Count);
193 193
             Assert.AreEqual("Steve", actual[0]["Name"]);
@@ -198,7 +198,7 @@ public void BasicWhereNotEqualShouldWork()
198 198
         {
199 199
             var tableRef = new ObjectReference("FooTable");
200 200
             var whereClause = new WhereClause(new ObjectReference("Name", tableRef) != "Alice");
201  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  201
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
202 202
             var actual = runner.Run().ToList();
203 203
             Assert.AreEqual(3, actual.Count);
204 204
             Assert.False(actual.Any(a => (string)a["Name"] == "Alice"));
@@ -220,7 +220,7 @@ public void WhereNotNullShouldWork()
220 220
                                        {"Name", "Dave"}, { "Value", 42 }
221 221
                                    },
222 222
                            };
223  
-            var runner = new DictionaryQueryRunner(data, whereClause);
  223
+            var runner = new DictionaryQueryRunner("FooTable", data, whereClause);
224 224
             var actual = runner.Run().ToList();
225 225
             Assert.AreEqual(1, actual.Count);
226 226
             Assert.AreEqual("Dave", actual[0]["Name"]);
@@ -242,7 +242,7 @@ public void WhereNotEqualWithByteArrayShouldWork()
242 242
                                        {"Name", "Dave"}, { "Array", new byte[] { 2, 3, 4}}
243 243
                                    },
244 244
                            };
245  
-            var runner = new DictionaryQueryRunner(data, whereClause);
  245
+            var runner = new DictionaryQueryRunner("FooTable", data, whereClause);
246 246
             var actual = runner.Run().ToList();
247 247
             Assert.AreEqual(1, actual.Count);
248 248
             Assert.AreEqual("Dave", actual[0]["Name"]);
@@ -253,7 +253,7 @@ public void BasicWhereGreaterThanShouldWork()
253 253
         {
254 254
             var tableRef = new ObjectReference("FooTable");
255 255
             var whereClause = new WhereClause(new ObjectReference("Weight", tableRef) > 200M);
256  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  256
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
257 257
             var actual = runner.Run().ToList();
258 258
             Assert.AreEqual(1, actual.Count);
259 259
             Assert.AreEqual("David", actual[0]["Name"]);
@@ -264,7 +264,7 @@ public void BasicWhereLessThanShouldWork()
264 264
         {
265 265
             var tableRef = new ObjectReference("FooTable");
266 266
             var whereClause = new WhereClause(new ObjectReference("Weight", tableRef) < 150M);
267  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  267
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
268 268
             var actual = runner.Run().ToList();
269 269
             Assert.AreEqual(1, actual.Count);
270 270
             Assert.AreEqual("Alice", actual[0]["Name"]);
@@ -275,7 +275,7 @@ public void BasicWhereGreaterThanOrEqualShouldWork()
275 275
         {
276 276
             var tableRef = new ObjectReference("FooTable");
277 277
             var whereClause = new WhereClause(new ObjectReference("Weight", tableRef) >= 250M);
278  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  278
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
279 279
             var actual = runner.Run().ToList();
280 280
             Assert.AreEqual(1, actual.Count);
281 281
             Assert.AreEqual("David", actual[0]["Name"]);
@@ -286,7 +286,7 @@ public void BasicWhereLessThanOrEqualShouldWork()
286 286
         {
287 287
             var tableRef = new ObjectReference("FooTable");
288 288
             var whereClause = new WhereClause(new ObjectReference("Weight", tableRef) <= 100M);
289  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  289
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
290 290
             var actual = runner.Run().ToList();
291 291
             Assert.AreEqual(1, actual.Count);
292 292
             Assert.AreEqual("Alice", actual[0]["Name"]);
@@ -299,7 +299,7 @@ public void BasicLikeShouldWork()
299 299
             dynamic objRef = new ObjectReference("Name", tableRef);
300 300
             var expression = new SimpleExpression(objRef, new SimpleFunction("like", new[] {"A%"}), SimpleExpressionType.Function);
301 301
             var whereClause = new WhereClause(expression);
302  
-            var runner = new DictionaryQueryRunner(SelectSource(), whereClause);
  302
+            var runner = new DictionaryQueryRunner("FooTable", SelectSource(), whereClause);
303 303
             var actual = runner.Run().ToList();
304 304
             Assert.AreEqual(1, actual.Count);
305 305
             Assert.AreEqual("Alice", actual[0]["Name"]);
2  Simple.Data/InMemoryAdapter.cs
@@ -64,7 +64,7 @@ public override IList<string> GetKeyNames(string tableName)
64 64
 
65 65
         public override IEnumerable<IDictionary<string, object>> Find(string tableName, SimpleExpression criteria)
66 66
         {
67  
-            var whereClauseHandler = new WhereClauseHandler(new WhereClause(criteria));
  67
+            var whereClauseHandler = new WhereClauseHandler(tableName, new WhereClause(criteria));
68 68
             return whereClauseHandler.Run(GetTable(tableName));
69 69
         }
70 70
 
12  Simple.Data/QueryPolyfills/DictionaryQueryRunner.cs
@@ -17,20 +17,22 @@ private static readonly
17 17
                         { typeof(OrderByClause), (c, d) => new OrderByClauseHandler((OrderByClause)c).Run(d) }
18 18
                     };
19 19
 
  20
+        private readonly string _mainTableName;
20 21
         private readonly IEnumerable<IDictionary<string, object>> _source;
21 22
         private readonly IList<SimpleQueryClauseBase> _clauses;
22 23
         private readonly WithCountClause _withCountClause;
23 24
 
24  
-        public DictionaryQueryRunner(IEnumerable<IDictionary<string, object>> source, IEnumerable<SimpleQueryClauseBase> clauses)
  25
+        public DictionaryQueryRunner(string mainTableName, IEnumerable<IDictionary<string, object>> source, IEnumerable<SimpleQueryClauseBase> clauses)
25 26
         {
  27
+            _mainTableName = mainTableName;
26 28
             _source = source;
27 29
             _clauses = clauses.ToList();
28 30
             _withCountClause = _clauses.OfType<WithCountClause>().FirstOrDefault();
29 31
             if (_withCountClause != null) _clauses.Remove(_withCountClause);
30 32
         }
31 33
 
32  
-        public DictionaryQueryRunner(IEnumerable<IDictionary<string, object>> source, params SimpleQueryClauseBase[] clauses)
33  
-            : this(source, clauses.AsEnumerable())
  34
+        public DictionaryQueryRunner(string mainTableName, IEnumerable<IDictionary<string, object>> source, params SimpleQueryClauseBase[] clauses)
  35
+            : this(mainTableName, source, clauses.AsEnumerable())
34 36
         {
35 37
         }
36 38
 
@@ -63,7 +65,7 @@ public DictionaryQueryRunner(IEnumerable<IDictionary<string, object>> source, pa
63 65
         {
64 66
             foreach (var whereClause in _clauses.OfType<WhereClause>())
65 67
             {
66  
-                source = new WhereClauseHandler(whereClause).Run(source);
  68
+                source = new WhereClauseHandler(_mainTableName, whereClause).Run(source);
67 69
             }
68 70
             return source;
69 71
         }
@@ -99,7 +101,7 @@ public DictionaryQueryRunner(IEnumerable<IDictionary<string, object>> source, pa
99 101
             {
100 102
                 var criteria = HavingToWhere(clause.Criteria, selectReferences);
101 103
                 source = new SelectClauseHandler(new SelectClause(selectReferences)).Run(source).ToList();
102  
-                source = new WhereClauseHandler(new WhereClause(criteria)).Run(source);
  104
+                source = new WhereClauseHandler(_mainTableName, new WhereClause(criteria)).Run(source);
103 105
                 source = source.Select(d => d.Where(kvp => !kvp.Key.StartsWith(AutoColumnPrefix)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
104 106
             }
105 107
 
20  Simple.Data/QueryPolyfills/WhereClauseHandler.cs
@@ -5,15 +5,18 @@ namespace Simple.Data.QueryPolyfills
5 5
     using System.Collections.Generic;
6 6
     using System.Linq;
7 7
     using System.Text.RegularExpressions;
  8
+    using Extensions;
8 9
 
9 10
     internal class WhereClauseHandler
10 11
     {
11 12
         private readonly Dictionary<SimpleExpressionType, Func<SimpleExpression, Func<IDictionary<string, object>, bool>>> _expressionFormatters;
12 13
 
  14
+        private readonly string _mainTableName;
13 15
         private readonly WhereClause _whereClause;
14 16
 
15  
-        public WhereClauseHandler(WhereClause whereClause)
  17
+        public WhereClauseHandler(string mainTableName, WhereClause whereClause)
16 18
         {
  19
+            _mainTableName = mainTableName;
17 20
             _whereClause = whereClause;
18 21
             _expressionFormatters = new Dictionary<SimpleExpressionType, Func<SimpleExpression, Func<IDictionary<string, object>, bool>>>
19 22
                                         {
@@ -147,6 +150,21 @@ private IList<object> Resolve(IDictionary<string, object> dict, object operand,
147 150
                 return ResolveSubs(dict, objectReference.GetOwner(), key).ToList();
148 151
             }
149 152
 
  153
+            if (keys.Length == 2 && !HomogenizedEqualityComparer.DefaultInstance.Equals(keys[0].Singularize(), _mainTableName.Singularize()))
  154
+            {
  155
+                var joinedDict = dict[keys[0]] as IDictionary<string, object>;
  156
+                if (joinedDict != null && joinedDict.ContainsKey(keys[1]))
  157
+                {
  158
+                    return new[] { joinedDict[keys[1]] };
  159
+                }
  160
+
  161
+                var joinedDicts = dict[keys[0]] as IEnumerable<IDictionary<string, object>>;
  162
+                if (joinedDicts != null)
  163
+                {
  164
+                    return joinedDicts.Select(d => d.ContainsKey(keys[1]) ? d[keys[1]] : null).ToArray();
  165
+                }
  166
+            }
  167
+
150 168
             if (dict.ContainsKey(key))
151 169
                 return new[] { dict[key] };
152 170
 
59  Simple.Data/SimpleQuery.cs
@@ -312,7 +312,7 @@ protected IEnumerable<dynamic> Run()
312 312
                 var unhandledClausesList = unhandledClauses.ToList();
313 313
                 if (unhandledClausesList.Count > 0)
314 314
                 {
315  
-                    result = new DictionaryQueryRunner(result, unhandledClausesList).Run();
  315
+                    result = new DictionaryQueryRunner(_tableName, result, unhandledClausesList).Run();
316 316
                 }
317 317
             }
318 318
 
@@ -511,6 +511,55 @@ public SimpleQuery OuterJoin(ObjectReference objectReference, out dynamic queryO
511 511
             return this;
512 512
         }
513 513
 
  514
+        public SimpleQuery Join(DynamicTable dynamicTable, JoinType joinType)
  515
+        {
  516
+            if (ReferenceEquals(dynamicTable, null)) throw new ArgumentNullException("dynamicTable");
  517
+            _tempJoinWaitingForOn = new JoinClause(dynamicTable.ToObjectReference(), joinType, null);
  518
+
  519
+            return this;
  520
+        }
  521
+
  522
+        public SimpleQuery Join(DynamicTable dynamicTable, out dynamic queryObjectReference)
  523
+        {
  524
+            return Join(dynamicTable, JoinType.Inner, out queryObjectReference);
  525
+        }
  526
+
  527
+        public SimpleQuery Join(DynamicTable dynamicTable, JoinType joinType, out dynamic queryObjectReference)
  528
+        {
  529
+            if (ReferenceEquals(dynamicTable, null)) throw new ArgumentNullException("dynamicTable");
  530
+            var newJoin = new JoinClause(dynamicTable.ToObjectReference(), null);
  531
+            _tempJoinWaitingForOn = newJoin;
  532
+            queryObjectReference = dynamicTable.ToObjectReference();
  533
+
  534
+            return this;
  535
+        }
  536
+
  537
+        public SimpleQuery LeftJoin(DynamicTable dynamicTable)
  538
+        {
  539
+            return OuterJoin(dynamicTable);
  540
+        }
  541
+
  542
+        public SimpleQuery LeftJoin(DynamicTable dynamicTable, out dynamic queryObjectReference)
  543
+        {
  544
+            return OuterJoin(dynamicTable, out queryObjectReference);
  545
+        }
  546
+
  547
+        public SimpleQuery OuterJoin(DynamicTable dynamicTable)
  548
+        {
  549
+            if (ReferenceEquals(dynamicTable, null)) throw new ArgumentNullException("dynamicTable");
  550
+            _tempJoinWaitingForOn = new JoinClause(dynamicTable.ToObjectReference(), JoinType.Outer);
  551
+
  552
+            return this;
  553
+        }
  554
+
  555
+        public SimpleQuery OuterJoin(DynamicTable dynamicTable, out dynamic queryObjectReference)
  556
+        {
  557
+            _tempJoinWaitingForOn = new JoinClause(dynamicTable.ToObjectReference(), JoinType.Outer);
  558
+            queryObjectReference = dynamicTable;
  559
+
  560
+            return this;
  561
+        }
  562
+
514 563
         public SimpleQuery On(SimpleExpression joinExpression)
515 564
         {
516 565
             if (_tempJoinWaitingForOn == null)
@@ -542,6 +591,14 @@ public SimpleQuery WithTotalCount(out Promise<int> count)
542 591
         private SimpleQuery ParseJoin(InvokeMemberBinder binder, object[] args)
543 592
         {
544 593
             var tableToJoin = args[0] as ObjectReference;
  594
+            if (ReferenceEquals(tableToJoin, null))
  595
+            {
  596
+                var dynamicTable = args[0] as DynamicTable;
  597
+                if (!ReferenceEquals(dynamicTable, null))
  598
+                {
  599
+                    tableToJoin = dynamicTable.ToObjectReference();
  600
+                }
  601
+            }
545 602
             if (tableToJoin == null) throw new InvalidOperationException();
546 603
 
547 604
             SimpleExpression joinExpression = null;

0 notes on commit bd4d0f4

Please sign in to comment.
Something went wrong with that request. Please try again.