How do i change an Object in a usercollection?


#1

So i have an Array inside my user collection (i guess thats what you call it)

How do i do, if i want to change for example “owned” inside the user Glutch?

useraccounts.upsert({_id: Meteor.userId().estate[1]}, {$set: {owned: 100}});

This didnt seem to work. Anyone have a suggestion?

Thanks in advance


#2

I wouldn’t try to edit a field of a object part of an array nested as a field in your user object. That is very complex stuff…

I see two solutions here
1.

  • Create a estate collection. 1 user to many estate so your user object will have an array of estate _ids.
  • estate.upsert({_id:<estate_id>, {$set:owned:100}});
  • Keeping the schema as nested, pull your whole array out, traverse it to find the object you want, modify the field and set your whole array again (js doens’t guarantee array ordering so doing estate[1] is very fragile.

#3

Here’s what worked for me:

useraccounts.update({_id:Meteor.userId(), "estate.index": 2}, {$set: {"estate.$.owned": 100}});

The document selector part ({_id:Meteor.userId(), "estate.index": 2} identifies the document (_id) and the position in the estate array we are targetting ("estate.index": 2).

The update part ({$set: {"estate.$.owned": 100}}) uses the $ positional operator to reference the array element we found and then sets the owned value to 100.


#4

Hmm this is what i get

Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]

oliverUsers.upsert({_id:Meteor.userId(), "estate.index": target}, {$set: {"estate.$.owned": 50}});

I’m creating a game where users can buy 3 different houses, and they can own how many they want. Maybe there is an easier way to store this data? Maybe in a new collection as mordax suggestions, however i dont understand his solution


#5

You need to do this on the server. Plus, not too sure about the use of upsert in this context.


#6

The houses has its own fields, “price: 200, name: ‘kiosk’, userOwns: 10, income: 10”. For example.

I wish to print out the houses to the user using
{{#each realestate}}
{{price}}
{{name}}
etc, etc
{{/each}}

And allow the user to press a buy button on the site.

I got it all down but i couldnt figure out how to update the userOwns variable to the client view. I stored the houses in a json file, and i used 3 different variables in the user collection. thisUser.estate1 = 5, thisUser.estate2 = 10, etc. (But how would i show this in the view? It was in a different collection and how do you sync them up)

Maybe there is a simpler way to do this, i thought that would be to store the whole object at the user collection, but that seemed unnecessary complex?

Do you have any simpler suggestion? I appreciate it a lot, ive been stuck on this problem for a while :smile:

EDIT: This seems so hard to code, but for the user its very simple. They only want to see a list of houses, the price, how many they own, and the ability to buy them - and see it update in realtime


#7

place only owned houses in the user object and than $push object in case they buy it.

and check $ and $slice projection operators in mongodb manual


#8

Okey $push allows me to insert arrays, and $slice allows me to select only the latest X, (why would i need $slice?)

Also, how do i display the userOwns in the view with this? How do i find the correct house in the json file?


#9

OK, now I’m confused :confused:

Are you now not asking how to change an object in a usercollection?


#10

I wonder if there is any simpler solution, since @mordrax said it was a very complex and weird way to solve it, i explained my situation and im asking for your expert advice on how to solve this problem the best way :blush:


#11

Well, updating an array element buried in a mongo document is neither difficult nor weird. The code I provided works. However, it will only work on the server, because the find component is not simply _id (this is a Meteor client restriction). The code could go in a Meteor method, for example and your client could call it to do the update.

Having said that, it may not be the optimum solution for your requirement, but I am still unclear as to how your application code and data is organised.

Can you provide a minimum repo which works where you’ve got it to work?


#12

Thank you, ill work on it. Never used methods before so… see you in a couple of days :wink:


#13

yes, $slice is not exactly related to this, it was more like pointing to projection learning.

regarding querying the houses owned, I would use something like this, but I cant test it atm.
It is in coffeescript :smiley:

useraccounts.find {
  _id: Meteor.userId(), 
  estate: 
    $elemMatch:
      owned:
        $gt:
          0
},
  'estate.$': 1

And for update targeting using these methods seems safer than hardcoding indexes.


#14

Thanks, i think i still have a bunch of basic stuff to learn before i get back to this, haha :sunny:


#15

the selector usually looks something like this

{
  "_id": "someuserid",
  "estate.index": 2
}

Then have a projector to reference it

Meteor.users.update({
  _id: Meteor.userId(),
  "estate.index": 2
}, {
  $set: {
    "estate.$.owned": 100
  }
});

#16

Or you could use my package https://atmospherejs.com/vjau/mongo-extend which allow to do it easily client side :

var user = useraccounts.findOne({_id: userid});
user.estate[1].owned = 100;
useraccounts.extend(user);

Hopefully it should work.


#17

That does work :smile:

How do I secure it from malicious client access - allow/deny on the publication?


#18

The package is just building an update query behind the scene. All the usual allow/deny rules apply. The update is done through minimongo, it doesn’t use a method or other mean to directly alter the server db.


#19

That looks amazing, ill give it a shot and report back! :smile:

EDIT: It works nicely, thank you a lot for this package. Super simple!


#20

nice! awww package didn’t have any stars, fixed that :slight_smile: