2

Each Team document contains a list of Player documents. Each Player contains a list of strings named PlayerColors.

The need is to update the PlayerColors of a specific Player within the specific team. So I thought I could make one filter for the parent (Team) level and combine that with a filter for the child (Player) level.

But I haven't seen any examples that mirror this approach, so perhaps this is not possible?

var filterTeam = Builders<Team>.Filter.Eq("TeamName", "GoldDigger");

var filterPlayer = Builders<Player>.Filter.Eq("PlayerName", "Greg");

//   var combinedFilter = filterTeam & filterPlayer; // erroneous 

List<string> newColors = new List<string>() { "peach", "periwinkle" };

UpdateDefinition<Player> updateDefinition = Builders<Player>.Update.Set(doc => doc.PlayerColors, newColors); 

//   collection.UpdateOne(combinedFilter, updateDefinition);

A copy of this project is here in GitHub.

2 Answers 2

2

As the Team document consists of Players array, you can't use the player filter with FilterDefinition<Player> type with the team filter with FilterDefinition<Team>.

What you need to achieve is with the $elemMatch operator.

Builders<Team>.Filter.ElemMatch(x => x.Players, filterPlayer)

Next same to the update definition, you need to provide the value with UpdateDefinition<Team>. Since you are updating the nested matching element(s), you need to looking for the filtered positional update operator $[<identifier>].

The MongoDB .NET Driver Fluent code for this update matching element is slightly complex, which I wrote the similar answer in MongoDB - How to filter and update on a child of a child.

In summary, you need:

  • AllMatchingElements(<identifier>) method in the updateDefinition
  • The UpdateOptions object with ArrayFilters property as the third arguments in the UpdateOne() method.

Full code:

var filterTeam = Builders<Team>.Filter.Eq("TeamName", "GoldDiggers");
var filterPlayer = Builders<Player>.Filter.Eq("PlayerName", "Greg");
var filterTeamPlayers = Builders<Team>.Filter.ElemMatch(x => x.Players,
    filterPlayer);

var combinedFilter = filterTeam & filterTeamPlayers;

List<string> newColors = new List<string>() { "peach", "periwinkle" };

UpdateDefinition<Team> updateDefinition = Builders<Team>.Update
    .Set(doc => doc.Players.AllMatchingElements("p").PlayerColors, newColors);

var updateResult = collection.UpdateOne(combinedFilter, updateDefinition,
    new UpdateOptions
    {
        ArrayFilters = new ArrayFilterDefinition[]
        {
            new BsonDocumentArrayFilterDefinition<Player>
            (
                new BsonDocument("p.PlayerName", "Greg")
            )
        }
    });
Sign up to request clarification or add additional context in comments.

1 Comment

I had assumed the ElemMatch projection would only work for arrays, and not lists. Your comment also refers to arrays but my implementation was done only with Lists. However, it worked - so now I'm assuming the driver treats C# Lists as arrays.
1

As two filters are of different types (here team and player) belong to different collection, we cannot combine those filters. So need to use two different filters- one to retrieve specific team(team filter) and second to get specific player using team filter.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.