0

I have the following Meteor.js collection:

Game: { 
  _id: 1,
  rounds: {
     round: {
        number: 1,
        players: {
           player: {
             _id: someId,
             cardSelection: someCard
           }
           player: {
             _id: someId2,
             cardSelection: someCard2
           }
        }
     }
  }

There are some other fields in the collection but these are the important ones. I'm trying to update a player's selection when they click a card. I'm unsure how to do this in MongoDB/Meteor.

So far, I've tried: var currentRound = Games.findOne(gameId).rounds.length;

Games.update({_id: gameId,
              "rounds.round": currentRound,
              "rounds.round.$.players": playerId
              }, 
              {$set: 
                  {"rounds.$.players.$": {id: playerId, selection: selection}}
              }
             );

The problem is that when calling the update, I don't know the rounds position (well I do, its the last round, but the round number changes), and I don't know the player's position inside the Players object inside the Round object.

Any help?

2
  • 1
    I think there is a mistake in your Game document. Should rounds and players both be Arrays? Commented Aug 22, 2014 at 3:15
  • they are arrays of objects Commented Aug 22, 2014 at 5:15

1 Answer 1

1

Assuming this structure for a Games document, you cannot update a player directly using the positional update operator.

{
    _id: 1,
    rounds: [{
        number: 1,
        players: [{
            _id: 'someId',
            cardSelection: 'someCard'
        }, {
            _id: 'someId2',
            cardSelection: 'someCard2'
        }]
    }, {
        number: 2,
        players: []
    }]
}

From the mongodb docs:

The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value.

It's not as elegant, but you could set the entire players array. Luckily, underscore makes things easy.

var game = Games.findOne(gameId);
var currentRound = game.rounds.length;

var round = _.findWhere(game.rounds, {number: currentRound});

// find and modify the affected player
round.players = _.map(round.players, function (player) {
  if (player.id === playerId) {
    player.cardSelection = selection;
  }
  return player;
});

Games.update({
  _id: gameId,
  "rounds.number": currentRound
}, {
  $set: {"rounds.$.players": round.players}
});

Alternatively, you could break rounds into a separate collection.

Sign up to request clarification or add additional context in comments.

1 Comment

I'm currently setting the entire players array using underscore. I was just wondering if there was an easier way. I thought about making a rounds collection as well, but decided against it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.