Hashes
Our ultimate objective in this section is to programmatically store JavaScript objects in Redis, and we'll learn about manipulating Redis hashes using Node.js in the process. Shamu is back to provide us with a real (Sea) world context.
const shamu = {
type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
}
Set Field Values in a Redis Hash One by One
As a first approach, we'll marshal the shamu
object into a Redis hash one field at a time:
const Redis = require('ioredis');
const redis = new Redis();
async function main() {
const shamu = {
type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
};
try {
const key = 'shamu';
for (const prop in shamu) {
console.log(prop, shamu[prop]);
const result = await redis.hset(key, prop, shamu[prop]);
console.log(result);
}
}
catch (error) {
console.error(error);
}
redis.disconnect();
}
main();
Beginning in line 15, we iterate over the properties of the shamu
object and use the hset
function to update each individual field of our Redis hash. This approach works; nonetheless, there is clearly a lot of impedance mismatch between the JavaScript object and the Redis hash. Is there a better way? I'm glad you asked.
Store JavaScript Objects in Redis using HMSET
The HMSET command handles our dilemma and enables us to set a Redis hash directly from a JavaScript object:
const Redis = require('ioredis');
const redis = new Redis();
async function main() {
const shamu = {
type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
};
try {
const key = 'shamu';
const result = await redis.hmset(key, shamu);
console.log(result);
}
catch (error) {
console.error(error);
}
redis.disconnect();
}
main();
On line 14, we simply supply the shamu
JavaScript object as the second parameter of the hmset
function. This is way more elegant. Take a minute to soak this in: Redis can directly store JavaScript objects.
Warning
There are, however, some limitations to this approach as it does not work with nested JavaScript objects. Let's consider the following revised Shamu object:
const shamu = {
type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
size: { length: 30, weight: 8 },
};
The hmset
function will faithfully execute without error, but the fields of the nested size
object will be unreadable. This can be verified without code by invoking the hgetall
command from the redis-cli:
$ redis-cli hgetall shamu
1) "type"
2) "killer whale"
3) "age"
4) "5"
5) "lastFeedDate"
6) "Jan 06 2018"
7) "size"
8) "[object Object]"
While all the other Shamu properties are accessible, the size
object is not.
Is there an even better way to store JavaScript objects in Redis? Absolutely, and I'll show you that next.
Store JavaScript Objects in Redis (The Best Way)
Since strings are the primary lingua franca in the Redis world, we can stringify our shamu
JavaScript object to successfully store it in Redis:
const Redis = require('ioredis');
const redis = new Redis();
async function main() {
const shamu = {
type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
size: { length: 30, weight: 8 },
};
try {
const key = 'shamu';
const result = await redis.set(key, JSON.stringify(shamu));
// Turn around and bring back Shamu immediately to prove it works.
const shamuReturns = JSON.parse(await redis.get(key));
console.log(shamuReturns);
} catch (error) {
console.error(error);
}
redis.disconnect();
}
main();
In line 15, we use the good old-fashioned set
command to store our stringified shamu
JavaScript object.
In line 18, we employ JSON.parse
to rehydrate the shamu
object returned from Redis.
The program returns the following console output, proving that our storage and retrieval of the JavaScript object was successful:
{ type: 'killer whale',
age: 5,
lastFeedDate: 'Jan 06 2018',
size: { length: 30, weight: 8 } }
This approach provides an excellent general-purpose solution for storing both simple and more complex JavaScript objects with nested values within Redis.
Get Field Values from a Redis Hash
Back to our regularly scheduled program regarding Redis hashes, we can use the hgetall
function to retrieve all field values from the shamu
hash:
const Redis = require('ioredis');
const redis = new Redis();
function printObjectDetails(key, obj) {
console.log(`${key} object`)
console.log('-'.repeat(26));
for (const prop in obj) {
console.log(`${prop} = ${obj[prop]}`);
}
}
async function main() {
try {
const key = 'shamu';
const shamuObject = await redis.hgetall(key);
printObjectDetails(key, shamuObject);
} catch (error) {
console.error(error);
}
redis.disconnect();
}
main();
The printObjectDetails
function provides a convenient way to print the contents of the retrieved shamu
hash to the console.