How to remove a value from an array attribute


#1

Imagine the following (simplified) Json Doc:

{
    "data": {
        "type": "persons",
        "id": "ae1212145fsd",
        "attributes" {
            "emailAddresses": [
                "email1@domain.com",
                "email2@domain.com"
            ]
        }
    }
}

How would I remove

email2@domain.com

from the attribute

emailAddresses

I was thinking of taking a simular approach to relationships, where you would have something like:

PATCH /persons/ae1212145fsd/attributes/email-addresses/email-address1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": null
}

but since

emailAddresses

is an array, I feel the structure of the URI isn’t correct.

The alternative below would clear out the complete array all together, which is not the intended behaviour.

PATCH /persons/ae1212145fsd/attributes/email-addresses HTTP/1.1
    Content-Type: application/vnd.api+json
    Accept: application/vnd.api+json

    {
      "data": null
    }

Any thoughts?

Many thanks!


#2

I think you answered your own question on accident.

I think this is a classic case of an incorrectly designed resource. The email addresses are resources related to the primary resource. Take a look at this thread for more details.


#3

Thanks a lot Michael!

I started to question whether the email addresses weren’t actually relationships, after my post. Glad to have it confirmed by your reply!


#4

Glad to help! So often we just need to try to collate our thoughts to explain to someone else to realize what we already know. :smiley:


#5

Very true, well said :grinning:

Apart from that it’s very helpful to have someone more experienced on the subject like yourself to confirm or argue whether that realization makes sense. So thanks again!


#6

Sorry to bother you again Michael, but there’s still something not quite “clicking” in my head (probably healthy if you’d take it literally :-D)

Although I can see how the email addresses are relationships to the resource object the way I modelled it, there is the practical issue that in my application they are not stored separately (nor can they, nor do I want to). I’m using MongoDB as my datastore and I’m utilizing it’s feature of embedded documents to store the email addresses with the particular person. I know the spec is meant to be agnostic to these kind of details, but I feel I have to mention it in order to explain the problem I’m having.

If I’d choose the route of treating the email addresses as relationships, creating a person would require the POST to look something like:

POST /persons HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "persons",
      "relationships" {
        "emailAddresses": 
          "data": [
            {
              "type": "emailAddresses",
              "id": "1"                           
            },
            {
              "type": "emailAddresses",
              "id": "2"                           
            },
         ] 
      }
   }
}

Now, that would be fine in a situation where emailAddresses.id = 1 and emailAddresses.id = 2 actually exist. But as explained above, they do not exist, because they are created at the same time as the resource.

So I am kind of back to the idea that the email addresses are in fact attributes to the resource object, but they should not appear as an array in the JSON doc, even though that’s how they are stored. The only problem left in that case, is how to name those attribute fields in order to be able to identify them for deletion/updating. I was thinking of the solution below, which may not win a beauty contest, but as far as I can see does the job without breaking the rules of the spec:

GET /persons/ae1212145fsd HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

Response:

 {
    "data": {
        "type": "persons",
        "id": "ae1212145fsd",
        "attributes" {
            "emailAddresses": { 
                "email1@domain.com": "email1@domain.com", 
                "email2@domain.com": "email2@domain.com"
            }
        }
    }
}

Then to update/delete

    PATCH /persons/ae1212145fsd HTTP/1.1
        Content-Type: application/vnd.api+json
        Accept: application/vnd.api+json

    {
      "data": {
        "type": "persons",
        "id": "ae1212145fsd",
        "attributes" {
          emailAddresses: {
            "email1@domain.com": null  //to delete, or else the updated e-mail address
          }
        }
    }

Does this argumentation make sense, or am I missing something?

Thanks again!


#7

Just realized that the attribute names themselves do actually break the spec rules, in terms of reserved characters. So in order for it the be compliant, the attribute names should be filtered:

{
    "data": {
        "type": "persons",
        "id": "ae1212145fsd",
        "attributes" {
            "emailAddresses": { 
                "email1DomainCom": "email1@domain.com", 
                "email2DomainCom": "email2@domain.com"
            }
        }
    }
}

and

 {
      "data": {
        "type": "persons",
        "id": "ae1212145fsd",
        "attributes" {
          emailAddresses: {
            "email1DomainCom": null  //to delete, or else the updated e-mail address
          }
        }
    }

#8

You’re point on the agnosticism is relevant here, you’re leaking too much information about your implementation by concerning yourself about this.

If I put my architect hat on, I would be very wary of this as my implementation, because now you are going to have to search all embedded documents in every person in Mongo to verify the email address isn’t associated with another person, this not be performant. You may not have a choice, but document stores are best when you have the unstructured data or non relational data, dumping relational data into a document store usually isn’t the best solution.

That being said, on to the specific problem you mention. Take a look at the compound documents section, and the user generated IDs section which would allow you to create a person AND the related emails within the same request.

Hope that helps!


#9

Thanks for you quick reply once again, I really appreciate you’re taking the time!

… you’re leaking too much information about your implementation by concerning yourself about this.

I’m not sure I understand what information about the implementation I am leaking. In the implementation, I am storing the email addresses in an array . I’ll be returning them as each email address being seperate attributes to the resource object. Is that not abstraction enough?

If I put my architect hat on, I would be very wary of this as my implementation, because now you are going to have to search all embedded documents in every person in Mongo to verify the email address isn’t associated with another person, this not be performant.

I fully agree with you, but in this case I’ll have to support the use case of the same email address (like an info@domain.com address) being used for multiple users anyway. So the email address only has to be unique within that particular resource object.
I could still very well solve that with a seperate collection/table with a 1-n relation of course , but that does not give me advantages over embedded documents in this case (since I would have to embedd the foreign data anyway in MongoDB).

Take a look at the compound documents section, and the user generated IDs section which would allow you to create a person AND the related emails within the same request.

Using compound documents for a POST would certainly make things easier! However, it was my understanding that the “includes” member is not allowed for request documents, only for responses. Am I wrong?

Edit
Just to make clear, I’m not trying to stubborn about the email addresses being attributes. If they should be relationships, I’m glad to find out. I just want to make sure I understand the reasoning behind it.


#10

Embedding something which should be a relationship is telling and the ‘leaking implementation details’ isn’t so much about keeping people unaware, as it is to prevent external binding to unintended internal behavior (think natural ordering or something like that).

It seems you’re fully aware of why you’re storing duplicate emails, so while I was wary, you clearly know what you’re doing here :+1:.

Compound documents can be POSTed when you use User Generated IDs, it’s why they exist :smiley:

Hope that helps!


#11

It really does. Thanks for your help, it gave me a lot more understanding on the subject!