Working with the Collections Developer Preview

Collections is introduced as a Developer Preview feature in Couchbase Server 6.5. The 3.0 API SDKs all work with Collections and Scopes. As a practical demonstration, we have a collections-enabled version of the Travel Sample application.

The Developer Preview of the upcoming Collections feature in Couchbase Server is fully implemented in the 3.0 API versions of the Couchbase SDKs. When working with other server versions, the default collection is used from the SDK. Here we show how to access individual collections in the Developer Preview version of Couchbase Server 6.5, with a collections-enabled version of our Travel Sample application. User documents and flight documents are split into user and flight collections. Something that previously had to be done with key-value references to different types or categories of data.

Travel Sample app — with Collections

Travel Sample Application uses the Travel Sample data Bucket, which ships with Couchbase Server. For Couchbase Server 6.5, make sure that you have at least one node each of data; query; index; and search. For a development box, mixing more than one of these on a single node (given enough memory resources) is perfectly acceptable. If you have yet to install Couchbase Server in your development environment, start here.

Then load up the Travel Sample Bucket, using either the Web interface or the command line. You will also need to create a Search Index — Query indexes are taken care of by the Sample Bucket.

Enabling Developer Preview should only be done on a development machine; there is no upgrade path available from a DP-enabled Couchbase Server.

Preparation

Along with the Java SDK 3.0 and Couchbase Server, set up as described above, you will need git to fetch the travel sample application code:

git clone https://github.com/couchbaselabs/try-cb-java.git

Change directory into your cloned repository, and check out the Collections branch (in the case of the Java SDK, currently 6.5.0-branch).

git checkout 6.5.0-branch
  • Before building the Travel Sample Application, which in the case of the Java SDK utilises the Collections Developer Preview, you need to enable this DP feature (see warning above).

    Enable Collections Developer Preview
    /opt/couchbase/bin/couchbase-cli enable-developer-preview --enable -c http://localhost:8091 -u Administrator -p password
    Developer preview cannot be disabled once it is enabled. If you enter developer preview mode you will not be able to upgrade. DO NOT USE IN PRODUCTION.
    Are you sure [y/n]: y

The Travel Sample Bucket needs altering to be split into collections. There is a script to do this included with the Travel Sample App — run:

Create Sample Collections
sh create-collections.sh Administrator password 127.0.0.1

adjusting for any changes you have made to server URL, or admin password. You should now have the Travel Sample Data Bucket split into collections:

{"uid":"1"}{"uid":"2"}{"uid":"3"}

THE FINAL RESULT
{"uid":"3","scopes":[{"name":"userData","uid":"8","collections":[{"name":"flights","uid":"9"},{"name":"users","uid":"8"}]},{"name":"_default","uid":"0","collections":[{"name":"_default","uid":"0"}]}]}

Running the Travel Sample Application

Next, edit the storage.host field in src/main/resources/application.properties to the one for your containerised Couchbase Server (or localhost, 127.0.0.1, if appropriate), and any other local changes — such as password. From here onwards, we’ll assume the defaults.

And run with

mvn spring-boot:run

Most likely, you’ll want to open up your preferred IDE for the storage.host step, and stay there to build the app, rather than running Maven from the command line.

After the build, you should see messages from Tomcat and trycb.Application, which tells you that you’ve been successful. With your Web browser of choice, head to port 8080 of the local machine.

Using the Sample App is the same as with the non-collections version that we cover in our introductory doc, but we’re assuming you’ve come here to see Collections in use in the codebase.

Sample App Backend

The backend code shows Couchbase Java SDK in action with Query and Search, but also how to plug together all of the elements and build an application with Couchbase Server and the Java SDK — in this case using the Collections Developer Preview. Look at User.java to see some of the pieces necessary in most applications, such as the User @Service, expanded in this version to embrace user and flight collections:

@Service
public class User {

    private final TokenService jwtService;

    @Autowired
    public User(TokenService jwtService) {
        this.jwtService = jwtService;
    }

    static final String USERS_COLLECTION_NAME = "users";
    static final String FLIGHTS_COLLECTION_NAME = "flights";

Here we work on the scope.collection(USERS_COLLECTION_NAME) to insert the newly-created user document:

    public Result<Map<String, Object>> createLogin(final Scope scope, final String username, final String password,
            DurabilityLevel expiry) {
        String passHash = BCrypt.hashpw(password, BCrypt.gensalt());
        JsonObject doc = JsonObject.create()
            .put("type", "user")
            .put("name", username)
            .put("password", passHash);
        InsertOptions options = insertOptions();
        if (expiry.ordinal() > 0) {
            options.durability(expiry);
        }
        String narration = "User account created in document " + username + " in bucket " + scope.bucketName()
                + " scope " + scope.name() + " collection " + USERS_COLLECTION_NAME
                + (expiry.ordinal() > 0 ? ", with expiry of " + expiry.ordinal() + "s" : "");

        try {
            scope.collection(USERS_COLLECTION_NAME).insert(username, doc);
            return Result.of(
                    JsonObject.create().put("token", jwtService.buildToken(username)).toMap(),
                    narration);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AuthenticationServiceException("There was an error creating account");
        }
    }

Here, the flights array, retrieved from the flightsCollection and containing the flight IDs, is converted to actual objects:

        Collection flightsCollection = scope.collection(FLIGHTS_COLLECTION_NAME);
        List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < flights.size(); i++) {
            String flightId = flights.getString(i);
            GetResult res;
            try {
                res = flightsCollection.get(flightId);
            } catch (DocumentNotFoundException ex) {
                throw new RuntimeException("Unable to retrieve flight id " + flightId);
            }
            Map<String, Object> flight = res.contentAsObject().toMap();
            results.add(flight);
        }
        return results;
    }

}