Get Off My Lawn
Get Off My Lawn

Reputation: 36351

Dynamodb isn't finding overlap between two date ranges

I am trying to search my database to see if a date range I am about to add overlaps with a date range that already exists in the database.

Using this question: Determine Whether Two Date Ranges Overlap

I came up with firstDay <= :end and lastDay >= :start for my FilterExpression.

def create(self, start=None, days=30):
    # Create the start/end times
    if start is None:
        start = datetime.utcnow()
    elif isinstance(start, datetime) is False:
        raise ValueError('Start time must either be "None" or a "datetime"')
    end = start + timedelta(days=days)

    # Format the start and end string "YYYYMMDD"
    start = str(start.year) + str('%02d' % start.month) + str('%02d' % start.day)
    end = str(end.year) + str('%02d' % end.month) + str('%02d' % end.day)

    # Search the database for overlap
    days = self.connection.select(
        filter='firstDay <= :end and lastDay >= :start',
        attributes={
            ':start': {'N': start},
            ':end': {'N': end}
        }
    )

    # if we get one or more days then there is overlap
    if len(days) > 0:
        raise ValueError('There looks to be a time overlap')

    # Add the item to the database
    self.connection.insert({
        "firstDay": {"N": start},
        "lastDay": {"N": end}
    })

I am then calling the function like this:

seasons = dynamodb.Seasons()
seasons.create(start=datetime.utcnow() + timedelta(days=50))

As requested, the method looks like this:

def select(self, conditions='', filter='', attributes={}, names={}, limit=1, select='ALL_ATTRIBUTES'):
    """
    Select one or more items from dynamodb
    """
    # Create the condition, it should contain the datatype hash
    conditions = self.hashKey + ' = :hash and ' + conditions if len(conditions) > 0 else self.hashKey + ' = :hash'
    attributes[':hash'] = {"S": self.hashValue}
    limit = max(1, limit)

    args = {
        'TableName': self.table,
        'Select': select,
        'ScanIndexForward': True,
        'Limit': limit,
        'KeyConditionExpression': conditions,
        'ExpressionAttributeValues': attributes
    }
    if len(names) > 0:
        args['ExpressionAttributeNames'] = names
    if len(filter) > 0:
        args['FilterExpression'] = filter

    return self.connection.query(**args)['Items']

When I run the above, it keeps inserting the above start and end date into the database because it isn't finding any overlap. Why is this happening?

The table structure looks like this (JavaScript):

{
  TableName: 'test-table',
  AttributeDefinitions: [{
    AttributeName: 'dataType',
    AttributeType: 'S'
  }, {
    AttributeName: 'created',
    AttributeType: 'S'
  }],
  KeySchema: [{
    AttributeName: 'dataType',
    KeyType: 'HASH'
  }, {
    AttributeName: 'created',
    KeyType: 'RANGE'
  }],
  ProvisionedThroughput: {
    ReadCapacityUnits: 5,
    WriteCapacityUnits: 5
  },
}

Upvotes: 3

Views: 452

Answers (1)

F_SO_K
F_SO_K

Reputation: 14889

Looks like you are setting LIMIT=1. You are probably using this to say 'just return the first match found'. In fact setting Limit to 1 means you will only evaluate the first item found in the Query (i.e. in the partition value range). You probably need to remove the limit, so that each item in the partition range is evaluated for an overlap.

Upvotes: 3

Related Questions