Redis SORT With Jedis
Look no further if you need to SORT your data with Jedis and Java.
Join the DZone community and get the full member experience.
Join For FreeIn this post, we will talk about the Redis SORT command.
Redis provides the SORT command that we can use to retrieve or store sorted values from a LIST, SET or ZSET.
In its simplest form, we can use the command over a KEY, like in the example below:
SORT numbers_list
This will sort the values contained in the key and return them. The command sorts the values as numbers. So, let's say we have a list with the following values:
1, 110, 5
The command above will return:
1 5 110
We can specify to sort the values using alphabetically using the ALPHA modifier. There are a number of modifiers. We will take a look at some of them in the examples below. The examples will use the Jedis API.
For our examples, let's consider that we have an idea management system. We have a list containing all the usernames in the system:
all:users [junior, francisco, ribeiro, user4]
And for every username, there will be a hash containing the user's information:
user:
user:junior
- name: "Junior User"
- num_ideas : "5"
- email:"fjunior@email.com"
user:francisco
- name: "Francisco User"
- num_ideas: "4"
- email: "francisco@email.com"
...
We can see a class that will populate Redis for our example:
package br.com.xicojunior.redistest;
import java.util.HashMap;
import java.util.Map;
import redis.clients.jedis.Jedis;
public class App
{
public static Jedis jedis = new Jedis("localhost");
public static void main( String[] args ){
String names[] = new String[]{"junior", "francisco", "ribeiro", "user4"};
for(String name: names){
jedis.lpush("all:users", name);
}
addUserHash(names[0], "Junior User", "junior@junior.com", "5");
addUserHash(names[1], "Francisco User", "francisco@francisco.com", "4");
addUserHash(names[2], "Ribeiro User", "ribeiro@ribeiro.com", "3");
addUserHash(names[3], "User 4", "user@user.com", "2");
for(String name: names){
System.out.println(jedis.hgetAll("user:".concat(name)));
}
System.out.println(jedis.lrange("all:users", 0, -1));
}
public static void addUserHash(String username, String name, String email, String numberOfIdeas){
Map<String, String> userProp = new HashMap<String, String>();
userProp.put("name",name);
userProp.put("email", email);
userProp.put("num_ideas", String.valueOf(numberOfIdeas));
jedis.hmset("user:".concat(username), userProp);
}
}
Let's take a look at the code example below:
package br.com.xicojunior.redistest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
public class SortTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
//[1]sorting the usernames
System.out.println(jedis.sort("all:users"));
//[ribeiro, francisco, junior, user4]
//[2]sorting the username alpha
//jedis sort method receives a SortingParams instance for modifiers
System.out.println(jedis.sort("all:users", new SortingParams().alpha()));
//[francisco, junior, ribeiro, user4]
}
}
In the example above, we sort the key "all:users." On the first try, it didn't seem to have sorted correctly because the default sorting considers numbers. On the second example, we use the ALPHA modifier. We can do this by using the overloaded version of the sort method. It receives an instance of the SortingParams class. In this case, we see the usernames are sorted correctly.
One nice feature of the SORT command is that we can sort the list using external values — values in other keys. In the example below, we will sort the all:users key by the number of ideas the user gave. It can be done using the "BY" modifier that receives the pattern of the keys to be used. Let's see our example below:
package br.com.xicojunior.redistest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
public class SortTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
//[1] Sorting the usernames by the number of ideas
System.out.println(jedis.sort("all:users", new SortingParams().by("user:*->num_ideas")));
//[user4, ribeiro, francisco, junior]
//[1] Sorting the usernames by the number of ideas DESC
System.out.println(jedis.sort("all:users", new SortingParams().by("user:*->num_ideas").desc()));
}
}
In this second example, we are sorting the usernames by an external value, in our case by the field "_numideas." Because we are sorting by a hash field, we used the following pattern "_user:->numideas." With this pattern ,we are saying to look for the key "_user:" where this "*" will be replaced by the value from the list. As it is a hash, we need to inform the field. We do this using the pattern "->fieldname_." If we were sorting by a string key, we could use the pattern "_numideas*_" because there was a key to store the number of ideas for each user.
In the first call, it retrieved the values — sorting them ASC. We can also tell reRediso sort it as _DESC _using the DESC modifier. With Jedis, BY and DESC are methods from SortingParams. As all methods return the instance, we can chain all of the callings, and that makes easier to read the code.
With the _SORT _command, we can also retrieve values from the external key or a field from an external hash. We can do this using the GET modifier, and we can use it many times. Let's see some examples of this modifier below:
package br.com.xicojunior.redistest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
public class SortTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
//[1] Sorting the usernames by the number of ideas and retrieving the user name
System.out.println(jedis.sort("all:users", new SortingParams().by("user:*->num_ideas").get("user:*->name")));
//[User 4, Ribeiro User, Francisco User, Junior User]
//[2] Retrieving the name and email
System.out.println(jedis.sort("all:users", new SortingParams().by("user:*->num_ideas").get("user:*->name","user:*->email")));
//[User 4, user@user.com, Ribeiro User, ribeiro@ribeiro.com, Francisco User, francisco@francisco.com, Junior User, junior@junior.com]
//[3] Retrieve the value of the key being sorted - Special pattern #
System.out.println(jedis.sort("all:users", new SortingParams().by("user:*->num_ideas").get("user:*->name","user:*->email","#")));
//[User 4, user@user.com, user4, Ribeiro User, ribeiro@ribeiro.com, ribeiro, Francisco User, francisco@francisco.com, francisco, Junior User, junior@junior.com, junior]
}
}
In the code above we can see the use of the GET modifier. In order to return a hash field, we can use a pattern similar to the one we used in the BY modifier. In the first, we simply return the name, but as we said, we can use GET many times. In the second, we retrieve the name and the email from the user. We can also retrieve the value for the key that was sorted by using a special pattern "#." The method GET receives a vararg, so we can pass all the external keys we want to retrieve the value from.
Another thing we can do is to store the result from the sorting in a key. It is useful for cases when we want to cache the sort result, we can specify a dest key for the sort command. The result will be stored as a LIST.
package br.com.xicojunior.redistest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
public class SortTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
jedis.sort("all:users","dest_key1");
System.out.println(jedis.lrange("dest_key1", 0, -1));
//[ribeiro, francisco, junior, user4]
jedis.sort("all:users", new SortingParams().alpha().desc(), "dest_key2");
System.out.println(jedis.lrange("dest_key2", 0, -1));
//[user4, ribeiro, junior, francisco]
}
}
One very useful feature of the SORT command is that we can use it only to get values from related keys. There is a modifier indicating to not sort the NOSORT:
package br.com.xicojunior.redistest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
public class SortTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost");
System.out.println(jedis.sort("all:users", new SortingParams().get("user:*->name","user:*->email").nosort()));
//[User 4, user@user.com, Ribeiro User, ribeiro@ribeiro.com, Francisco User, francisco@francisco.com, Junior User, junior@junior.com]
}
}
This piece of code basically retrieves the name and email for all users. In case we don't use the SORT command, we would need at least two commands to do the same:
LRANGE all:users 0 -1 //TO get all usernames
And then for each username, call HMGET for each one like below:
HMGET user:junior name email //TO get the name and email from a user
We can find the command documentation in the redis site.
That's it for today.
See you in the next post.
Published at DZone with permission of Francisco Ribeiro, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments