IGNITE-6907 .NET: Fix LINQ multitable conditional joins
authorAlexey Popov <tank2.alex@gmail.com>
Fri, 17 Nov 2017 09:52:38 +0000 (12:52 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Fri, 17 Nov 2017 09:52:38 +0000 (12:52 +0300)
This closes #3049

modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs
modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs

index ae9fd5f..9132de7 100644 (file)
@@ -295,7 +295,8 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
             var persons = GetPersonCache().AsCacheQueryable();
 
             var res = persons
-                .Select(x => new {Foo = x.Key % 2 == 0 ? even : odd, x.Value})
+                .Select(x => new { x.Key, Foo = x.Key % 2 == 0 ? even : odd, x.Value })
+                .OrderBy(x => x.Key)
                 .ToArray();
 
             if (comparer != null)
index f589329..4192fb0 100644 (file)
@@ -85,6 +85,23 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
         }
 
         /// <summary>
+        /// Tests the multi key join inline
+        /// </summary>
+        [Test]
+        public void TestMultiKeyJoinInline()
+        {
+            var organizations = GetOrgCache().AsCacheQueryable();
+            var persons = GetPersonCache().AsCacheQueryable();
+
+            var multiKey = persons.Where(p => p.Key == 1).Join(organizations,
+                p => new { OrgId = p.Value.OrganizationId, p.Key },
+                o => new { OrgId = o.Value.Id, Key = o.Key - 1000 },
+                (p, o) => new { PersonName = p.Value.Name, OrgName = o.Value.Name });
+
+            Assert.AreEqual(" Person_1  ", multiKey.Single().PersonName);
+        }
+
+        /// <summary>
         /// Tests the cross cache join.
         /// </summary>
         [Test]
@@ -243,17 +260,23 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
         [Test]
         public void TestMultipleFrom()
         {
-            var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < PersonCount);
-            var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Value.Name != "1");
-
-            var all = persons.SelectMany(person => roles.Select(role => new { role, person }));
-            Assert.AreEqual(RoleCount * PersonCount, all.Count());
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+            var organizations = GetOrgCache().AsCacheQueryable();
 
             var filtered =
                 from person in persons
                 from role in roles
-                where person.Key == role.Key.Foo
-                select new { Person = person.Value.Name, Role = role.Value.Name };
+                from org in organizations
+                where person.Key == role.Key.Foo && person.Value.OrganizationId == org.Value.Id
+                select new
+                {
+                    PersonKey = person.Key,
+                    Person = person.Value.Name,
+                    RoleFoo = role.Key.Foo,
+                    Role = role.Value.Name,
+                    Org = org.Value.Name
+                };
 
             var res = filtered.ToArray();
 
@@ -261,22 +284,85 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
         }
 
         /// <summary>
-        /// Tests query with multiple from clause with inline query sources.
+        /// Tests query with two from clause.
         /// </summary>
         [Test]
-        public void TestMultipleFromInline()
+        public void TestTwoFromSubquery()
         {
+            var persons = GetPersonCache().AsCacheQueryable();
+            var roles = GetRoleCache().AsCacheQueryable();
+            var personsSubquery = persons.Where(x => x.Key < PersonCount);
+            var rolesSubquery = roles.Where(x => x.Value.Name == "Role_2");
+
             var filtered =
-                from person in GetPersonCache().AsCacheQueryable()
-                from role in GetRoleCache().AsCacheQueryable()
+                from person in persons
+                from role in rolesSubquery
                 where person.Key == role.Key.Foo
-                select new { Person = person.Value.Name, Role = role.Value.Name };
+                select new
+                {
+                    PersonKey = person.Key, Person = person.Value.Name,
+                    RoleFoo = role.Key.Foo, Role = role.Value.Name
+                };
 
             var res = filtered.ToArray();
+            Assert.AreEqual(1, res.Length);
+
+            filtered =
+                from person in personsSubquery
+                from role in rolesSubquery
+                where person.Key == role.Key.Foo
+                select new
+                {
+                    PersonKey = person.Key, Person = person.Value.Name,
+                    RoleFoo = role.Key.Foo, Role = role.Value.Name
+                };
 
+            res = filtered.ToArray();
+            Assert.AreEqual(1, res.Length);
+
+            filtered =
+                from person in personsSubquery
+                from role in roles
+                where person.Key == role.Key.Foo
+                select new
+                {
+                    PersonKey = person.Key, Person = person.Value.Name,
+                    RoleFoo = role.Key.Foo, Role = role.Value.Name
+                };
+
+            res = filtered.ToArray();
             Assert.AreEqual(RoleCount, res.Length);
         }
 
+
+        /// <summary>
+        /// Tests query with two from clause.
+        /// </summary>
+        [Test]
+        public void TestMultipleFromSubquery()
+        {
+            var organizations = GetOrgCache().AsCacheQueryable().Where(x => x.Key == 1001);
+            var persons = GetPersonCache().AsCacheQueryable().Where(x => x.Key < 20);
+            var roles = GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo >= 0);
+
+            var filtered =
+                from person in persons
+                from role in roles
+                from org in organizations
+                where person.Key == role.Key.Foo && person.Value.OrganizationId == org.Value.Id
+                select new
+                {
+                    PersonKey = person.Key,
+                    Person = person.Value.Name,
+                    RoleFoo = role.Key.Foo,
+                    Role = role.Value.Name,
+                    Org = org.Value.Name
+                };
+
+            var res = filtered.ToArray();
+            Assert.AreEqual(2, res.Length);
+        }
+
         /// <summary>
         /// Tests the join of a table to itself.
         /// </summary>
index c4ef11f..3585281 100644 (file)
@@ -59,7 +59,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
 
             // Multiple values
             Assert.AreEqual(new[] { 0, 1, 2 },
-                cache.Where(x => x.Key < 3).Select(x => x.Value.Address.Zip).ToArray());
+                cache.Where(x => x.Key < 3).OrderBy(x => x.Key).Select(x => x.Value.Address.Zip).ToArray());
 
             // Single value
             Assert.AreEqual(0, cache.Where(x => x.Key < 0).Select(x => x.Value.Age).FirstOrDefault());
@@ -313,19 +313,20 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
             var persons = personCache.AsCacheQueryable();
             var roles = roleCache.AsCacheQueryable();
 
-            var res = persons.Join(roles, person => person.Key - PersonCount, role => role.Key, (person, role) => role)
+            // we have role.Keys = [1, 2, 3] and persons.Key = [0, .. PersonCount)
+            var res = persons.Join(roles, person => person.Key % 2, role => role.Key, (person, role) => role)
                 .ToArray();
 
-            Assert.AreEqual(res.Length, RoleCount);
+            Assert.IsTrue(PersonCount / 2 > res.Length);
 
             // Test distributed join: returns complete results
             persons = personCache.AsCacheQueryable(new QueryOptions { EnableDistributedJoins = true });
             roles = roleCache.AsCacheQueryable(new QueryOptions { EnableDistributedJoins = true });
 
-            res = persons.Join(roles, person => person.Key - PersonCount, role => role.Key, (person, role) => role)
+            res = persons.Join(roles, person => person.Key % 2, role => role.Key, (person, role) => role)
                 .ToArray();
 
-            Assert.AreEqual(RoleCount, res.Length);
+            Assert.AreEqual(PersonCount / 2, res.Length);
         }
 
         /// <summary>
index 3a3e5fd..56c3ff5 100644 (file)
@@ -394,11 +394,13 @@ namespace Apache.Ignite.Linq.Impl
             ValidateFromClause(fromClause);
             _aliases.AppendAsClause(_builder, fromClause).Append(" ");
 
+            var i = 0;
             foreach (var additionalFrom in queryModel.BodyClauses.OfType<AdditionalFromClause>())
             {
                 _builder.AppendFormat(", ");
                 ValidateFromClause(additionalFrom);
-                _aliases.AppendAsClause(_builder, additionalFrom).Append(" ");
+
+                VisitAdditionalFromClause(additionalFrom, queryModel, i++);
             }
         }
 
@@ -506,6 +508,30 @@ namespace Apache.Ignite.Linq.Impl
             _builder.Append(") ");
         }
 
+        /** <inheritdoc /> */
+        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
+        public override void VisitAdditionalFromClause(AdditionalFromClause fromClause, QueryModel queryModel,
+            int index)
+        {
+            base.VisitAdditionalFromClause(fromClause, queryModel, index);
+
+            var subQuery = fromClause.FromExpression as SubQueryExpression;
+            if (subQuery != null)
+            {
+                _builder.Append("(");
+
+                VisitQueryModel(subQuery.QueryModel, true);
+
+                var alias = _aliases.GetTableAlias(subQuery.QueryModel.MainFromClause);
+                _builder.AppendFormat(") as {0} ", alias);
+            }
+            else
+            {
+                _aliases.AppendAsClause(_builder, fromClause).Append(" ");
+            }
+        }
+
+
         /// <summary>
         /// Visists Join clause in case of join with local collection
         /// </summary>