Repository: ignite Updated Branches: refs/heads/master 69eced659 -> 1ebeee00e
IGNITE-6907 .NET: Fix LINQ multitable conditional joins This closes #3049 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/1ebeee00 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/1ebeee00 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/1ebeee00 Branch: refs/heads/master Commit: 1ebeee00e96f18fab851930e672525a62c29fdd0 Parents: 69eced6 Author: Alexey Popov <tank2.a...@gmail.com> Authored: Fri Nov 17 12:52:38 2017 +0300 Committer: Pavel Tupitsyn <ptupit...@apache.org> Committed: Fri Nov 17 12:52:38 2017 +0300 ---------------------------------------------------------------------- .../Cache/Query/Linq/CacheLinqTest.Base.cs | 3 +- .../Cache/Query/Linq/CacheLinqTest.Join.cs | 110 +++++++++++++++++-- .../Cache/Query/Linq/CacheLinqTest.Misc.cs | 11 +- .../Impl/CacheQueryModelVisitor.cs | 28 ++++- 4 files changed, 133 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/1ebeee00/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs index ae9fd5f..9132de7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Base.cs @@ -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) http://git-wip-us.apache.org/repos/asf/ignite/blob/1ebeee00/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs index f589329..4192fb0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Join.cs @@ -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> http://git-wip-us.apache.org/repos/asf/ignite/blob/1ebeee00/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs index c4ef11f..3585281 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Misc.cs @@ -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> http://git-wip-us.apache.org/repos/asf/ignite/blob/1ebeee00/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs index 3a3e5fd..56c3ff5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryModelVisitor.cs @@ -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>