Key Paths
You must have at least one key path when defining an Item type. The first Key Path is the primary Key Path; additional Key Paths are aliases. Key Paths are used to locate Items and require careful consideration as they affect how data is laid out in the Store and how it can be accessed. In your Schema, a Key Path for an Item Type is a template composed of segments of (namespace, :field) pairs delimited by slashes. For example: /namespace1-:fieldReference1/namespace2-:pathTo.fieldRef2/...
.
Let’s see how this looks with example Item types Student
and Course
:
Using the schema above, we can retrieve the Student
Item with studentId
of 1234
by getting /student-1234
. We can also fetch information about a particular course in a particular quarter of a particular year using a complete keypath such as /course-MATH321/year-2023/quarter-1
, which will fetch the Item for Math-321 in the Autumn quarter of 2023. But we can also fetch all occurrences of a given course across all years and quarters via a List operation with prefix /course-MATH321
or all offerings of a course in a given year via a List with prefix /course-MATH321/year-2023
.
Multiple Key Paths
Let’s continue this example to answer the question below:
Why would I want more than one Key Path (an alias Key Path)?
Defining more than one Key Path for an Item is a way to make it possible to access those Items using different fields. In this way, it’s like an index in other databases, though unlike a traditional database, you can also access multiple items at once by Listing with a key path prefix.
StatelyDB guarantees that all Key Paths for a single Item are put, updated or deleted atomically with any change to the Item. Consider the relationship between a student and a class: a class may have many students and a student may be a member of many classes. Our application requires that we are able to answer the questions “Which classes is a student taking?” and “Which students are taking a given class?” Let’s build on the previous example and introduce a third Item type that will act as the glue between Students and Courses — the EnrolledStudent
:
Putting an EnrolledStudent
Item will result in two records. One under the Student’s [Group] and one under the Course’s [Group]. This allows us to answer the questions above:
- “Which classes is a student taking?” - A List with prefix
/student-123
will give us all the courses a student has ever participated in. - “Which students are taking a given class?” - A List with prefix
/course-PHYS341/year-2019
will return all the instancesPHYS341
was offered in2019
as well as all students who enrolled in those courses.
Beyond those questions, we can exploit the structure of these key paths to answer more specific questions. Listing with a prefix of /student-123/year-2019/quarter-3
will return only the courses student 123
participated in the spring quarter of 2019.
Again, you can think of these as a sort of index:
/course-:courseId/year-:year/quarter-:quarter/student-:studentId
is similar toindex EnrolledStudent on (courseId, year, quarter, studentId)
/student-:studentId/year-:year/quarter-:quarter/course-:courseId
is similar to `index EnrolledStudent on (studentId, year, quarter, courseId)
The wild part is that all Items share the same Key Path space. Since we also have key paths of /course-:courseId/year-:year/quarter-:quarter
(for Course) and /student-:studentId
(for Student), a single List operation can pick up Students, EnrolledStudents, and Courses all at once.
Frequently Asked Questions
Being able to fetch multiple items using List is the power of an Item type with more than one key path, though it is worth answering a few related questions:
If an Item has more than one Key Path, what happens when I attempt to delete an Item by an ‘alias’ (eg: instead of the primary Key Path)?
Since an alias is a way to address an Item, if you attempt to delete any Item by an alias it will delete the Item. StatelyDB maintains consistency of an Item across all of its Key Paths. In our example above a delete issued to /student-123/year-2019/quarter-3/course-MATH321
will also delete /course-MATH321/year-2019/quarter-3/student-123
and vice versa.
What happens when a field used to calculate an ‘alias’ changes during an Item update?
To better frame this question, let’s continue to examine the example above and imagine there was a typo in the year when writing a EnrolledStudent
Item. We meant to use the year 2023
but had accidentally typed 223
. Thus we one Item with two Key Paths in our database:
/student-123/year-223/quarter-3/course-MATH321
/course-MATH321/year-223/quarter-3/student-123
…but want the Key Paths:
/student-123/year-2023/quarter-3/course-MATH321
/course-MATH321/year-2023/quarter-3/student-123
How can we correct this problem? If we attempt to write a new EnrolledStudent
with an updated year, the primary Key Path of this Item is different than the original Item’s Key Path and thus the write will create a new item. The way to correct the problem is to write a new Item and then delete the incorrect Item.
Let’s modify our schema to make this more interesting by adding a new /classof-:graduatingYear/student-:studentId
alias to the Student
Item type:
Now we have the Student Item stored at the Key Paths:
/student-123
/classof-223/student-123
…but we want the Item stored at the Key Paths:
/student-123
/classof-2023/student-123
How can we correct the problem? Easy! Correct the graduatingYear
field in the offending Student
Item and write it back to the store. Since graduatingYear
is not the unique component of the primary Key Path, StatelyDB understands student 123
should only have one /classof-*/student-123
alias, and will atomically “move” the previous Item /classof-223/student-123
to /classof-2023/student-123
.
Is metadata the same between aliased items?
Timestamp metadata is guaranteed to be consistent across all aliases of an Item. However, version metadata may differ depending on the originating Key Path as version is a property of the Group an Item is in.