Back to Blogs

Storing Timestamped Metrics in Couchbase with Spring

Laurent Doguin of Couchbase Published

Storing Timestamped Metrics in Couchbase with Spring

I have previously blogged an intro to Spring Data Couchbase. This post is a follow-up. I will show you how I use Spring social connectors to store data coming from Twitter.

Document Structure

A question we often get about Java development is, “do we have an ODM ( Object Document Mapper)?”. While there is currently no Java ODM provided in our SDK, there is one with spring-data-couchbase. So let’s talk a bit about POJO and how they are mapped as JSON to be stored in Couchbase. Here’s a practical example with the TwitterUpdate class:

You’ve probably noticed a few annotations, each of which help spring-data-couchbase map the POJO to a Couchbase document. Let’s quickly go over the basic ones.

@Document

Its purpose is to signal to spring-data-couchbase that the annotated Class will be stored as a Couchbase document. You can use the “expiry” option to set a TTL for all these stored objects. The default value is 0, which mean the object won’t expire.

@Id

Its purpose is to define which field of the document will be used as the key to store it into Couchbase. This field is mandatory and must be a 250 char max.

@Field

Its purpose it to say that the annotated field will be part of the document stored in Couchbase. The default behaviour uses the field name as the JSON object key but you can define your own by giving a custom value to the Field annotation. Look at the key field for an example.

The POJO is now ready. To store it I could use the CouchbaseClient Bean  just like I did in the previous post. But the idea here is to create a custom repository,.

Spring Data Repositories

If you’ve been doing Java enterprise code for a while or you are familiar with Spring then you are almost certainly expecting a Repository. Right now Spring Data Couchbase supports the CRUDRepository. It gives you access to basic CRUD methods (get, save, delete, update, findAll).

It’s easy to set this up. All you have to do is create an interface extending the CRUDRepository interface, then use generics to define the type of the object and the key used to stored your Data with this repository.

package org.couchbase.advocacy.metrics.twitter;
import org.springframework.data.repository.CrudRepository;
public interface TwitterUpdateRepository extends CrudRepository<TwitterUpdate, String> {}

To make sure this works as expected, and that the repository implementation will be generated automatically by the Spring Framework at runtime, make sure you add the @EnableCouchbaseReposititories on your Application class. This repository implementation will be s Spring Bean and as such will be injectable.

If you try the findAll, deleteAll or count methods, It won’t work out of the box. The reason is that they rely on a custom view on Couchbase side. All the other methods work because they rely on the key of the document. So let’s create the all view for our POJO. The convention is that a findSomething method added to a repository will rely on the something view. So right now we need to create a view called all for the TwitterUpdate design document. This is really straight forward. If you want to use the count method, don’t forget to add the count reduce by clicking on one of the built-in reduce:

The next question you might ask yourself is How do I query using something other than the key of the document? Traditionally with Couchbase you create a new View using whichever property you would like to search against as key. In my case it’s going to be ‘doc.account’.

Using the same convention as before, we can add a findByAccount method to our TwitterUpdateRepository interface. This method will take a Query object as parameter. This also means that we need the view to be called byAccount:

package org.couchbase.advocacy.metrics.twitter;

import com.couchbase.client.protocol.views.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;

public interface TwitterUpdateRepository extends CrudRepository<TwitterUpdate, String> {

    Collection<TwitterUpdate> findByAccount(Query query);

}

Now to use this method you need to define the appropriate query Object. It’s straightforward:

    Query query = new Query();
     query.setKey(“Couchbase”);
     Collection<TwitterUpdate> tUpdates = twitterUpdateRepository.findByAccount(query);

I could use the repository directly into my main method but that would be bad, as in semantically bad. Call to the ‘persistence’ part of your application usually comes from the business side of things. It belongs to a Service.

Spring Services

To create a Spring service, add a new Class and annotate it with the @Service annotation. It’s a bit like using the @Component annotation, but with an extra-semantic meaning. Again services are where you put your business logic. There are others specialised annotations like Controller or Repository. Why this separation? It can be useful to differentiate them when you’re having fun with AOP.

I can autowire the Twitter class because I have declared it as a bean in my Application class and because I have added the @ComponentScan annotation. All its properties are defined in a properties file just like they were for my Couchabse Server configuration. It comes from the spring-social-twitter project and will allow me to easily run some queries against the Twitter API. This allows me to create my Twitter Update object and store it in Couchbase via the TwitterUpdateRepository.

    @Bean
    Twitter twitter() {
        return new TwitterTemplate(twitterConsumerKey, twitterConsumerSecret,
            twitterAccessToken, twitterAccessTokenSecret);
        }

Something interesting to look at here is how the document key is built. It starts with ‘tw’ for twitter, than the name of the account I searched for and then the timestamp. This should allow me to do some nice queries in the future, once I add metrics from other twitter accounts or other social networks. I can see a nice dashboard coming up to visualize the trends on all these accounts.

I hope this helped. Next I’ll be making another blog post about a somewhat more advanced use case for Spring (Caching, Validation and scheduling).

«   Back to blogs