C# - Update field in array with the latest version of driver (2.24.0)

After upgrade the driver version from 2.18.0 to 2.24.0 the index based “LINQ Expression” used to update a specific item in array do not works anymore:

Used expression:

Builders<Traveler>.Update.Set(t => t.VisitedCountries[-1].Name, "Hellas")

Full example:

var travelersCollection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

// create an elemMatch operator
var visitedGreeceExactly3Times = Builders<Traveler>.Filter
    .ElemMatch(t => t.VisitedCountries,
        country => country.Name == "Greece" 
        && country.TimesVisited == 3);

// create the update definition
var updateDefinition = Builders<Traveler>.Update.Set(t => t.VisitedCountries[-1].Name, "Hellas");

// this will update only the first matching array element! 
// ($) refers to the first match
var updateHellasResult = await travelersCollection
    .UpdateManyAsync(visitedGreeceExactly3Times, 
    updateDefinition);

After research, the current suggestion is:

Builders<Traveler>.Update.Set("visitedCountries.$.Name", "Hellas");

But in this case, we need to put “some magic MQL string expression” into code, is there another way to do this with full “LINQ Expression”, like before?

Builders<Traveler>.Update.Set(t => t.VisitedCountries.<something-new>, "Hellas");

It’s a breaking change on LinqProvider.V3 used by this driver version?

UPDATE:

There are a related ISSUE CSHARP-4079, but apparently its closed marked as solved due to lack of documentation of the differences between LINQ2 and LINQ3 providers. A new “sub issue” has been created about it: CSHARP-4618.

Any suggestions or considerations?

Thanks!

Hi, @Igor_N_A,

You are correct that CSHARP-4079 changed the positional update operators to use a different syntax in 2.16.0, but it only applied to LINQ3. LINQ3 was made the default in 2.19.0 which is why you only observed the problem when you upgraded to 2.24.0. We could have done a better job documenting this change.

In LINQ2, you can use the magic number -1 to indicate a positional match. In LINQ3, you can use the more expressive (with additional positional operators) syntax:

x.A.FirstMatchingElement() => "A.$"
x.A.AllElements() => "A.$[]"
x.A.AllMatchingElements("identifier") => "A.$[identifier]"

Hope that helps.

Sincerely,
James

Hi @James_Kovacs

Sorry for it, I didn’t check this mention on issue.

The Builder:
Builders<Traveler>.Update.Set(t => t.VisitedCountries.FirstMatchingElement().Name, "Hellas") works nicely!

Off-topic question…

Is there any extra documentation about this? Maybe something in the “Release Notes” of the version that includes this new methods? :slight_smile: I’ve already checked all “Release Notes” since v2.18.0. Any clue?

Thank you!

Happy that I could point you in the right direction. We won’t currently have this documented, but I’ve filed an internal documentation request to do so. Always glad to have user feedback regarding where our documentation needs improving. Thank you!

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.