[CSHARP-2308] Nested select which reference parent select actually still references child Created: 21/Jun/18  Updated: 28/Oct/23  Resolved: 07/Oct/21

Status: Closed
Project: C# Driver
Component/s: Linq
Affects Version/s: 2.6.0
Fix Version/s: 2.14.0

Type: Bug Priority: Critical - P2
Reporter: Mårten Byström Assignee: Robert Stam
Resolution: Fixed Votes: 2
Labels: rp-track
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: PNG File Screen Shot 2019-09-02 at 7.57.12 PM.png    
Epic Link: CSHARP-3615

 Description   

Consider the program below:

static void Main()
{
		var server = new MongoClient(new MongoUrl("mongodb://localhost:27017"));
		var db = server.GetDatabase("FooBar"); 
		var foos = db.GetCollection<FooNest>("Foos");
		
		foos.DeleteMany((x) => true);
		
		foos.InsertOne(
			new FooNest{ 
				Name = "Parent", 
				NestedCollection = new []{
					new FooNest { 
						Name = "Child"
					}
				}
			});
		
		var itms2 = foos.AsQueryable()
			.Select(top => top.NestedCollection
				.Select(child => new {ParentName = top.Name, child.Name}));
				
		Console.WriteLine("Items returned: " + itms2.ToList().ToJson());
		Console.WriteLine();
		Console.WriteLine("Query translation: " + itms2.ToString());
}
 
class FooNest
{
	public string Name;
	public IEnumerable<FooNest> NestedCollection;
}

The result of running this program is:

Items returned: [[{ "ParentName" : "Child", "Name" : "Child" }]]

It seems that the driver does not take into account that we want the Name property of the top object instead of the child object. The translation is printed out like this:

Query translation: aggregate([{ "$project" : { "__fld0" : { "$map" : { "input" : "$NestedCollection", "as" : "child", "in" : { "ParentName" : "$$child.Name", "Name" : "$$child.Name" } } }, "_id" : 0 } }])

Obviously the $map operator has no reference to the "parent" object. This behavior is wrong. The best would be if the driver used the "parent" object. But if that is not possible it would be better that the driver threw an error.



 Comments   
Comment by Robert Stam [ 07/Oct/21 ]

This issue has been fixed in the new LINQ provider (known as LINQ3) which will be included in the upcoming 2.14 release.

Configure your MongoClientSettings to use LinqProvider.V3 if you want to use this functionality.

To configure a client to use the LINQ3 provider use code like the following

var connectionString = "mongodb://localhost";
var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
clientSettings.LinqProvider = LinqProvider.V3;
var client = new MongoClient(clientSettings);

Comment by Githook User [ 07/Oct/21 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-2308: CSHARP-2308: Nested select with multiple variables in scope should translate correctly.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/3dd319b17fdbb5238de0076baf8da5fc908ab505

Comment by Robert Stam [ 22/Sep/21 ]

I have verified that the new LINQ implementation handles this correctly.

The generated pipeline is almost the same as the one rcollette@yahoo.com suggested above. It is slightly different but the same in essential ways:

{ $project : { _v : { $map : { input : '$NestedCollection', as : 'child', in : { ParentName : '$Name', Name : '$$child.Name' } } }, _id : 0 } } 

Comment by Robert Stam [ 22/Sep/21 ]

https://github.com/rstam/mongo-csharp-driver/pull/193

Comment by Richard Collette [ 17/Feb/20 ]

There are only 3 critical bugs in this tracker and yet this has sat for 19 months?   I guess that would be fine if this project were solely an open source endeavor but as an employee at a paying Atlas customer, I have to wonder what is going on here?

Comment by Richard Collette [ 03/Sep/19 ]

It seems the Expression (binder?) for the outer select replaces the "top" lambda parameter in both the outer select and the inner select with {document}.

The expression binder for the inner select also replaces the "child" lambda parameter with {document}

The SelectTranslator process the inner select.  In that context, {document} is referring to $$child and it replaces both instances of {document} in the inner select with $$child.

It's hard enough to follow the code for me.   I'm not even sure how this gets resolved.   It seems like expression binding maybe needs to tree down to leaf nodes first and keep track of document references by name {document-child} or a tree node indexing scheme like some databases use {document-1}  {document-1-1} {document-1-2}.   But this is beyond my time availability to look into further.

(Scroll right to see full image)

Comment by Richard Collette [ 02/Sep/19 ]

It's certainly possible it is just the references need to be worked out correctly

db.foos.aggregate(
[
   {
      "$project":{
         "__fld0":{
            "$map":{
               "input":"$nestedCollection",
               "as":"child",
               "in":{
                  "ParentName":"$name",
                  "Name":"$$child.name"
               }
            }
         },
         "_id":0
      }
   }
]
);

Comment by Richard Collette [ 31/Aug/19 ]

Possibly related to CSHARP-2723

Generated at Wed Feb 07 21:42:11 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.