0

I'm rethinking how I want to structure some data which is currently being stored in the Users Collection. Previously, my server would receive messages, find the user document with the right profile.website field, and push an item into the profile.siteMessages array:

{
  "_id": "hr9ck5Fis5YuvqCqP",
  "profile": {
    "website": "localhost",
    "siteMessages": [
         {
            "text": "sddsd",
            "createdAt": 1482001227204
         },
     ]
  }
}

Id like to change the structure to look something like the following. Instead of storing all messages, of which multiple messages could come from the same user, in a top level array in profile, I would have a profile.siteVisitors field which contains a visitorId and then the array of messages:

{
  "_id": "dgfsdfdfsdf",
  "emails": [
    {
      "address": "[email protected]",
      "verified": false
    }
  ],
  "profile": {
    "website": "localhost",
    "siteVisitors:" [
        {
          "visitorId": "74585242",
          "messages": [
            {
              "text": "A string",
              "createdAt": 1482001260853
            },
            {
              "text": "Another string",
              "createdAt": 1482001260854
            }
          ]
        },
        {
          "visitorId": "76672242",
          "messages": [
            {
              "text": "A string",
              "createdAt": 1482001260855
            }
          ]
        }
     ]
  }
}

Keeping with the structure shown above, how would I query for and update the profile.siteVisitiors.messages array? Currently I query and update the Collection using something like the following:

Meteor.users.update(
  {'profile.website': url},
  {$push: {'profile.siteMessages': msgItem}},
  function(err) {
    if (err) {
        console.log('whoops!' + err)
    } else {
        //success                   
    }
});

How would I update the newly structured messages array? I would need to match a User documents profile.website field, match a visitorId in the profile.siteVisitors array, and then push a new element into the messages array, but I'm not sure how this would look as a MongoDB query.

EDIT I've hacked together the following which seems to work, but is very ugly. How can I improve this?

Meteor.users.update(
    {"profile.website" : "localhost" , "profile.siteVisitors" : {$elemMatch: {"visitorId" : data.chirpVisitorId} } },
    {$push: { "profile.siteVisitors.$.messages": {"text" : data.msg, "createdAt" : data.msg.createdAt} } },
    function(err, res) {
        if (err) {  
            console.log('failed to push ' + err)
        } else {
            console.log('success on new push ' + res)
            if (res < 1) {
                let item = {
                    "visitorId": data.chirpVisitorId,
                    "messages": [data.msg]
                }
                Meteor.users.update(
                    {"profile.website": "localhost"},
                    {$push: {'profile.siteVisitors': item}},
                    function(err, res) {
                        if (err) {
                            console.log(err)
                        } else {
                            console.log(res + " updated")
                        }
                    }
                )
            }
        }
    })

1 Answer 1

1

You can use the $(update) operator.

Try the following query:

db.collection.update(
    {"profile.website" : "localhost" , "profile.siteVisitors" : {$elemMatch: {"visitorId" :'76672242'} } }, 
    { $push: { "profile.siteVisitors.$.messages": {"text" : <newText>, "createdAt" : <newCreatedDate>} } } 
)

Hope this helps.

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

4 Comments

Yes, that helps immensely. I had tried doing the following: db.users.find({'profile.website': "localhost", 'profile.siteVisitors': { $elemMatch: { visitorID: "74585242" } } }), which is essentially the same as your query, but it uses single quotes. This was returning nothing in the console. Why might that be? I had assumed quotes did not matter in this scenario.
In your query you're using visitorID, instead of visitorId.
Oh, of course. Thanks. Also, your solution does not create the siteVisitors array if it does not exist. Rather, it uses profile.siteVisitors to find the document. How can I create the siteVisitors array (and add the new message) if it does not already exist?
You can't use $ operator with upsert. I recommend you to use two different queries for that.

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.