I've got a PynamoDB table and index mapping defined as follows:
class Id2Index(LocalSecondaryIndex):
class Meta:
projection = AllProjection()
kind = UnicodeAttribute(hash_key=True)
id2 = UnicodeAttribute(range_key=True)
class Resource(Model):
kind = UnicodeAttribute(hash_key=True)
id = UnicodeAttribute(range_key=True)
id2 = UnicodeAttribute()
serial_number = NumberAttribute(attr_name='serialNumber')
created = JavaDateTimeAttribute()
updated = JavaDateTimeAttribute()
heartbeat = JavaDateTimeAttribute()
id2_index = Id2Index()
When I run the following query:
r = Resource.id2_index.query('job', Job.id2 > self.last_job_key))
The records I get back have Null values for the attributes created, updated and heartbeat. From all I read, and from code that ChatGPT gave me, it seems that specifying projection = AllProjection() should be all I need to do to cause the records that I get back from the query to have the data for these attributes filled in from the main table? In other words, this test fails even though the records I get back all have a value for the created attribute in the source table.
assert(next(r).created != None)
What am I missing. Isn't this supposed to just work?
If this isn't supposed to work, then how do I most efficiently go about getting the result that I'm looking for?
UPDATE:
It still isn't clear to me if I need to have copies of all of the attributes in the index to get the behavior I want. I've found some other threads on the net that suggest that this is true, but I haven't found a definitive word in the AWS docs. I asked Chat GPT to confirm that I don't, and it seems to confirm this:
You don't need to include the created and updated attributes in the secondary index table to retrieve their values in a query.
When you specify projection = AllProjection() in the index definition, it tells DynamoDB to include all attributes from the base table in the index. Therefore, when you perform a query on the index, you'll receive all of the attributes from the base table, including created and updated.
UPDATE 2:
I'll add some additional info just in case it will be useful. It will explain where the Job. references in my code come from. I have three subclasses of the Resource class. We are persisting these three specific types of resources into the same table, differentiated by the kind attribute, which is the hash key of the table. So the Job class looks something like this:
class Job(Resource):
_kind = 'job'
job_id = NumberAttribute(attr_name="jobId")
thread_id = NumberAttribute(attr_name="threadId")
....
All of the attributes involved in this question are defined in the Resource class, so I can't see how the subclassing of that class should make this problem any more interesting.
We don't mix queries across the three types, so all of our queries specify a single constant hash key ('job', 'thread', or 'instance').
Using
AllProjectionshould causecreated,updated, andheartbeatto carry over from the base table to the Local Secondary Index (LSI). If you don't useAllProjection, you have to useIncludeProjectionand list the desired attributes.I believe I am accomplishing what you want to accomplish with the code below.
Carrying over attributes from base table to LSI
Attributes are copied over from the base table to the LSI if they are key attributes, if they are non-key attributes that are listed specifically using the INCLUDE projection option, or if you used the ALL projection option.
The AWS docs say that here:
Example code
The main difference between mine and yours is that I am using
Resourceinstead ofJobin my query statement.Your query statement is:
I am not sure if the extra
)is there because that line is part of a larger statement, or if it was a typo.My query statement is:
I used
woodandid2 > "a"just as examples of an arbitrary resource kind and query condition intended to ensure my Item would be returned.If there is a reason for using
Job, please clarify it, and provide the source and explain the purpose of theJobclass, and I will attempt to adapt my answer accordingly.My code is as follows.
With the above files in place, I can run the following, which I believe shows the result you are seeking.
I'm using PynamoDB version 5.5.0.