question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Allow compound keys

See original GitHub issue

Apologies in advance for the length of this issue…

We’re trying to get an implementation working where one of our resources uses a compound primary key.

We have a parent model (lets call it Collection) which has a relation to a child model (lets call it Document). Documents represent resources that come from an external site. We use the Document ID from the external site in our URLs so users can correlate the resource on our site with the resource on the external site.

When viewing one of the child models in our system in the context of one of these “collections” the user facing URL looks like this: /collection/:collection_id/document/:document_id.

We can’t use only the document_id as the primary key for Documents because a single document may exit in multiple “collections”, so to uniquely identify a “document” instance from within our system you need to know both collection_id and document_id. Thus the API endpoint to fetch details for a specific Document instance is /api/collection/:collection_id/document/:document_id.

It sounds strange but there are reasons why documents exist in the context of collections. For example users can override certain data on a specific Document, but this edited data is only relevant for the Document instance within the users Collection, so users which have another copy of the same original Document (remember, Documents originate from an external site) in another collection don’t see this overridden/edited data.

If it helps I can try and set up a Plunkr with this scenario (of collections and documents) to show the resource definitions we have, but essentially we have a belongsTo relation in our Document resource definition that looks like this:

DS.defineResource({
    name: 'document',

    relations: {
        belongsTo: {
            collection: {
                parent: true,
                localField: 'collection',
                localKey: 'collection_id'
            }
        }
    }   
});

We currently have nothing special (in fact very little at all) in our Collection resource definition:

DS.defineResource({
    name: 'collection'
});

(We haven’t added the reverse hasMany relation here yet as we may not need it).

Our difficulty comes in trying to fetch a Document from the server using .find(). We don’t have a single field on the Document model that can be used to uniquely identify a given instance of Document within a collection. However given that the document view exists within a Collection (user facing URL /collection/:collection_id/document/:document_id) we know both the collection_id and document_id, so to fetch the Document instance we need to perform a GET to /api/collection/:collection_id/document/:document_id/. This is the key part of the issue.

As far as I can tell, there is no way to inform js-data that the only way to be able to fetch a Document is by combining the document_id with the collection_id. js-data is obviously aware of the relation between Document and Collection, and if document_id itself were unique js-data would be able to handle the call to /api/collection/:collection_id/document/:document_id/ because of the parent: true declaration in the resource config, but currently js-data doesn’t know that both the collection_id and document_id comprise the compound primary key.

Is there some way that js-data could be configured so that it can understand this sort of compound key? Perhaps something like this would be possible:

DS.defineResource({
    name: 'document',
    compoundKey: {
        parts: {
            collection: 'id',
            self: 'id'
        }
    }

    relations: {
        belongsTo: {
            collection: {
                parent: true,
                localField: 'collection',
                localKey: 'collection_id'
            }
        }
    }
});

In this example the parts object lists the relations and the field on the relation that is used as part of the compound key. self is a special case key which refers to the current resource type (it could just as easily be document: 'id')

Maybe something even simpler is possible:

DS.defineResource({
    name: 'document',
    relations: {
        belongsTo: {
            collection: {
                parent: true,
                compoundKey: true,
                localField: 'collection',
                localKey: 'collection_id'
            }
        }
    }
});

In this case perhaps the compoundKey setting could be checked if parent: true is set. If both parent: true and compoundKey: true are set, then js-data would know that in order to work with Document resources it needs to use both the Document id and id of the relation that has the parent: true setting.

Both of these are just quick suggestions, no doubt they’d need more thought/refinement.

We had tried setting the id attribute of our Document model to a computed property which used the collection_id and document_id combined with a separator, something like this:

DS.defineResource({
    name: 'document',

    relations: {
        belongsTo: {
            collection: {
                parent: true,
                localField: 'collection',
                localKey: 'collection_id'
            }
        }
    },

    computed: {
        c_id: ['collection_id', 'document_id', function (collectionId, documentId) {
                    return collectionId + ':' + documentId;
                }]
    }

});

But of course when trying to fetch or save a Document resource the requests look something like this for a document with id A12345 in a Collection with id 12: /api/collection/12/document/12:A12345 which is obviously incorrect.

What do you think? Do you think it will be possible for js-data to be able to handle this kind of relationship?

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
jreddcommented, Dec 15, 2016

Yes, there are other fields in the record that together make up the unique key and the end point is accessed via those keys in the url and not as optional properties in the url.

1reaction
jmdobrycommented, Dec 12, 2016

So a record of some type does not have a single field that acts as a primary key, but one can be derived from several other fields of the record?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Composite Key in SQL: Your Ultimate Guide to Mastery
A composite key in SQL can be defined as a combination of multiple columns, and these columns are used to identify all the...
Read more >
Composite Key in SQL - GeeksforGeeks
A composite key is made by the combination of two or more columns in a table that can be used to uniquely identify...
Read more >
How to Create a Composite Primary Key in MS Access Table
... Primary Key in MS Access Table - Office 365. This video explains how you can create a composite primary key in Microsoft...
Read more >
How to Use Composite Keys in Microsoft Access - YouTube
How to Use Composite Keys in Microsoft AccessIn this episode, we learn about composite keys in MS Access, and how to spot them...
Read more >
Defining Composite Primary and Foreign Keys - IBM
A composite key specifies multiple columns for a primary-key or foreign-key constraint. The next example creates two tables. The first table has a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found