META in sparse fieldsets


#1

Let’s say I have the following resource:

"data": [
    {
        "relationships": {
            "borrower": {
                "data": {
                    "id": "17D899D0-9D49-11E0-8793-50509DFF4B22",
                    "type": "people"
                },
                "meta": {
                    "date_borrowed": "2017-07-01",
                    "date_returned": "2017-08-01",
                    "late_fee": "$1.00"
                }
            }
        },
        "attributes": {
            "title": "Ready Player One"
        },
        "type": "library_books",
        "id": "17D90EE2-9D49-11E0-8793-50509DFF4B22"
    }
  ]

Now I want to make a call and only get back the book’s title and amount of fees owed on it by borrowers. What does the call look like?

Some options (of which may or may not break the spec):

A. ?fields[library_books]=title,borrower.late_fee

B. ?fields[library_books]=title,borrower.meta.late_fee

C. ?fields[library_books]=title&fields[relationships.borrower]=late_fee

Are there other options? I’m having trouble finding a precedence for this type of call.

Really what we need is to have data in the meta that is hidden unless specifically asked for, but we’re having trouble determining the right way to call for it.


#2

Why are fields in your META block? You have truncated a resource into a relationship. Relationships don’t have data, what you are trying to do is essentially use an intermediary resource as a ‘mapping’ relationship with content.

You should have a loan(? better name?) resource with a relationship to a book resource and a relationship to a borrower, along with the actual attributes of the data you have in your meta block.

If you want to hide the details for the loan unless specifically requested, you can embed them within a sub-resource, and not include that resource by default, which would then allow you to link to the details resource but not include it in the payload.


#3

This was just meant to be an example. We have a real need to include relationship metadata. This was originally brought up 2 years ago here:

The only solution without creating a Cartesian product of relationship resources was to go the meta route. With that being case, the question stands.


#4

I believe one of us is not understanding the results of the thread you linked. I think it may be you, but I may still be missing something. My question wasn’t directed at the validity of a meta field in the relationship object, but your example including semantic data. Date_borrowed is not metadata, it is an attribute.

In the linked thread, the source, a weight, etc… are all metadata items as they modify the context of a relationship, but are not new relationship types. These make cosmetic changes to the relationship to add context to describe the extent or degree but not change the underlying meaning.

In my previous post, I was able to abstract a resource from the metadata and context, which means you didn’t just add metadata to the relationship, you hid a resource inside. In the case of this example, it’s just bad resource design and normalization which is the problem. It’s very easy to include the related resource within a compound document. JSONAPI is a resource based format, and semantic data should be included within the format as a resource. The Meta object compromise is to allow for the rare edge cases when a resource can’t or shouldn’t be created like a very simple weighted graph attribute.

If you are going to create a cartesian product of the relationships resources, I don’t think you have properly abstracted the relationship data resource. I think @ethanresnick could have saved you a bunch of time if he had addressed this concern directly. If you’re creating a weighted_concept resource, you don’t define a relation as all combinations, because those are just attributes like the weight. If you want to add origin, well it’s just a field so you can add it.

I may have used different words in my explanation to get there, but the weighted_concept resource just defined, is not any different of an approach to what I suggested with the loan resource above.

The reason this is important and stated again, is because you are going against the JSONAPI way of including semantic data. You are asking how to search against a non-referenceable metadata resource in a format which specifically requires data to be served within a resource. You can even make your implementation dereference the generic relationship to include the target resource within the compound document.

Add ONE resource, include it in your compound documents to reference and then by default include your target resource. You add one resource, not a cartesian product of them.


#5

Let’s say I have this edge case and I want to include it in my list of fields. Is there a convention for this? Is it “not allowed” or “not supported”? Can I filter on it?


#6

This isn’t an edge case. If you are filtering on it, it’s not metadata.

Lets consider a bit more the example I gave of a weighted graph. You don’t search the weighted graph for edges of a certain weight, because that would have no meaning outside of the context of the vertices and edge itself making it truly metadata. You might however, care if the edge relationship is a “to_node” or “from_node”, which would make it a resource as these are attributes.

If you need to interact with the relationship attributes in a way like filtering, then this is a resource and should be designed and included as one. Using some standard path conventions for clarity, you would end up with a document that looks something like this.

{
  "data":{
    "type":"node",
    "id":"13",
    "attributes":{
      "node_name": "step5",
      "value":"something"
    },
    "relationships":{
      "edge":{
        "links":{
          "self":"http://example.org/node/13/relationships/edge",
          "related":"http://example.org/edge?filter[node]=13"
        },
        "data":[
          {"type": "edge", "id":"25"},
          {"type": "edge", "id":"28"}
        ]
      }
    },
    "links":[
      {
        "self":"http://example.org/node/13"
      }
    ]
  },
  "included":[
    {
      "type":"edge",
      "id":"25",
      "attributes":{
        "weight":"15"
      },
      "relationships":{
        "from_node": { "type": "node","id":"16"},
        "to_node": { "type": "node","id":"13"}
      }
    },
    {
      "type":"edge",
      "id":"28",
      "attributes":{
        "weight":"20"
      },
      "relationships":{
        "from_node": { "type": "node","id":"13"},
        "to_node": { "type": "node","id":"17"}
      }
    },
    {
      "type":"node",
      "id":"16",
      "attributes":{
        "node_name": "step4",
        "value":"something"
      },
      "relationships":{
        "edge":{
          "links":{
            "self":"http://example.org/node/16/relationships/edge",
            "related":"http://example.org/edge?filter[node]=16"
          }
        }
      },
      "links":[
        {
          "self":"http://example.org/node/16"
        }
      ]
    },
    {
      "type":"node",
      "id":"17",
      "attributes":{
        "node_name": "step6",
        "value":"something"
      },
      "relationships":{
        "edge":{
          "links":{
            "self":"http://example.org/node/17/relationships/edge",
            "related":"http://example.org/edge?filter[node]=17"
          }
        }
      },
      "links":[
        {
          "self":"http://example.org/node/17"
        }
      ]
    }
  ]
}

Once you create this resource, the answer is simple, you use the conventions outlined in the specification for filtering, querying, inclusion, and sparse fieldsets.

If you only needed the weight as a bit of information to come along with the relationship then you can use the meta field. If you need to operate on the attributes, you are talking about a resource and this should be accounted for in the design of your API using the standard conventions of JSONAPI.

As a general rule, you should use meta if and only if it is not reasonably feasible to do what you want to do within the normally defined bounds of the specification.