I’m building a sort of airbnb/booking.com clone where people can list their properties for rent. I have built the full domain with multiple tables referencing the center entity (which is property). This property entity is as followed:
id (Partition key) | title | description | hostId | createdAt | status | propertyType
Now I have multiple propertyTypes that I want to show seperately on different pages. So I made a Global Secondary Index on ‘status’ as ‘Partition key’ and ‘propertyType’ as ‘Sort key’ as I was told using the QueryCommand with a GSI is the quickest and most efficient way of getting filtered items. So I created a QueryCommand where the ‘status’ = “ACTIVE” and ‘propertyType’ = “Villa” with a ‘LIMIT’ of 20. I then map these values to retrieve some extra data from other tables. I do all this inside a Promise.all() which should mean that they run asynchronously and generally should be fairly quick. This is however not that case as I am something waiting for 3 seconds for only 3 results to return. I feel like I’m doing something wrong and the AWS DynamoDb documentation doesn’t really help me much further as I’ve built these queries off of their documentation.
In my service layer I call this function:
async getPropertiesByType(type) {
const propertyIdentifiers = await this.propertyRepository.getPropertiesByType(type);
return await Promise.all(
propertyIdentifiers.map(async (property) => {
const [baseProperties, generalDetails, pricing, images] = await Promise.all([
this.getBasePropertyInfo(property),
this.getGeneralDetails(property),
this.getPricing(property),
this.getImages(property)
]);
return {
property: baseProperties,
propertyGeneralDetails: generalDetails,
propertyPricing: pricing,
propertyImages: images
};
})
);
}
Which first calls this function:
async getPropertiesByType(type) {
const params = new QueryCommand({
"TableName": await this.systemManager.getSystemManagerParameter("/dynamoDb/property-table"),
"IndexName": "status-propertyType-index",
"KeyConditionExpression": "#status = :status AND #propertyType = :propertyType",
"ExpressionAttributeNames": {
'#status': 'status',
'#propertyType': 'propertyType'
},
"ExpressionAttributeValues": {
":status": {
"S": "ACTIVE"
},
":propertyType": {
"S": type
},
},
"Limit": 20
});
const result = await this.dynamoDbClient.send(params);
if (result.Count < 1) {
throw new NotFoundException(`No active property found for type ${type}.`)
} else {
const items = result.Items;
return items.map(item => { return item.id.S});
}
}
Followed by these functions:
async getBasePropertyInfo(property) {
return await this.propertyRepository.getPropertyById(property);
}
async getGeneralDetails(property) {
return await this.propertyGeneralDetailRepository.getPropertyGeneralDetailsByPropertyId(property);
}
async getPricing(property) {
return await this.propertyPricingRepository.getPricingById(property);
}
async getImages(property) {
return await this.propertyImageRepository.getImagesByPropertyId(property);
}
Which call:
async getPropertyById(id) {
const params = new GetItemCommand({
"TableName": await this.systemManager.getSystemManagerParameter("/dynamoDb/property-table"),
"Key": { "id": { "S": id } }
});
const result = await this.dynamoDbClient.send(params);
return result.Item ? PropertyBaseInfoMapping.mapDatabaseEntryToPropertyBaseInfo(result.Item) : null;
}
async getPropertyGeneralDetailsByPropertyId(id) {
const params = new QueryCommand({
"TableName": await this.systemManager.getSystemManagerParameter("/dynamoDb/property-general-detail-table"),
"IndexName": "property_id-index",
"KeyConditionExpression": "#property_id = :property_id",
"ExpressionAttributeNames": {
"#property_id": "property_id"
},
"ExpressionAttributeValues": {
":property_id": {
"S": id
}
}
})
const result = await this.dynamoDbClient.send(params);
return result.Items ? result.Items.map(item => GeneralDetailMapping.mapDatabaseEntryToGeneralDetail(item)) : null;
}
async getPricingById(id) {
const params = new GetItemCommand({
"TableName": await this.systemManager.getSystemManagerParameter("/dynamoDb/property-pricing-table"),
"Key": {
"property_id": {
"S": id
}
}
})
const result = await this.dynamoDbClient.send(params);
return result.Item ? PricingMapping.mapDatabaseEntryToPricing(result.Item) : null;
}
async getImagesByPropertyId(id) {
const params = new QueryCommand({
"TableName": await this.systemManager.getSystemManagerParameter("/dynamoDb/property-images-table"),
"IndexName": "property_id-index",
"KeyConditionExpression": "#property_id = :id",
"ExpressionAttributeNames": {
"#property_id": "property_id"
},
"ExpressionAttributeValues": {
":id": {
"S": id
}
}
})
const result = await this.dynamoDbClient.send(params);
return result.Items ? result.Items.map(item => ImageMapping.mapDatabaseEntryToImage(item)) : null;
}