WordCount Client

Appending lines into the Topic

The client API gives us access to a Grainite client which can be used to perform various actions like appending to a topic and writing data to a grain:

Grainite client = GrainiteClient.getClient(SERVER_IP, 5056);
Topic topic = client.getTopic(APP_NAME, TOPIC_NAME);
topic.append(new Event(Key.of(KEY), Value.of(PAYLOAD)));

Implement the WordCount client

As with the handler classes from the previous step, GX generated a Client class that will allow us to work with the Grainite client. In the following steps, we will complete Client.java to utilize the application logic we defined in the previous step through the Grainite client.

Step 0: Initiliazing Client.java

For this application, you will need to replace the contents of Client.java with skeleton code that has been provided for you. Copy the contents of the template file, in samples/wordcount/templates/Client.java, and paste them into your Client.java file, located in my-wordcount/src/main/java/org/sample/wordcount.

Step 1: Loading input files

Now, you will complete the load method in Client.java to parse lines from an input file and append each as an Event in line_topic.

For this application, you are using the function append() to push events into the topic synchronously, but the same can be done asynchronously using appendAsync().

Client.java
private void load(Grainite client, String inputFile) throws Exception {
  // Get topic line_topic.
  Topic topic = client.getTopic(Constants.APP_NAME, Constants.LINE_TOPIC);
  Path inputPath = Path.of(inputFile);

  Files.lines(inputPath).forEach(line -> {
    if (!line.isEmpty()) {
      topic.append(
        new Event(Key.of(line.substring(0, 2)),
          Value.ofObject(new LineEventHandler.LineEvent(inputPath.getFileName().toString(), line))));
    }
  });

  topic.close();
}

Step 2: Getting word count

Here, you will the getWordCount method in Client.java. To get the word count for a specific word, you will first need to get a reference to the grain that contains it. Then, you can use mapGet() to access the map containing the word you want and get the value stored for that word.

Client.java
private void getWordCount(Grainite client, String word) throws GrainiteException {
  Table wordTable = client.getTable(Constants.APP_NAME, Constants.WORD_STATS_TABLE);
  Grain grain = wordTable.getGrain(Key.of(word.substring(0, 1)));

  System.out.printf("%s: %d\n", word, grain.mapGet(0, Key.of(word)).asLong(0));
}

Step 3: Getting doc stats

Getting the document stats involves similar steps to those of getting a word count. The only difference is that now we are fetching data from the grain's value, and not from its map. You can use getValue() to access the grain's value.

Client.java
private void getDocumentStats(Grainite client, String docName) throws Exception {
  Table docTable = client.getTable(Constants.APP_NAME, Constants.DOC_STATS_TABLE);
  Grain docGrain = docTable.getGrain(Key.of(docName));

  DocumentStats stats = docGrain.getValue().asType(DocumentStats.class);
  System.out.printf(
      "Document %s { Sentences: %d, Words: %d }\n", docName, stats.numPeriods, stats.numWords);
}

Step 4: Get all word counts in alphabetical order

To get the words in sorted order, you will first have to fetch the grains in sorted order. You will do this by querying from the table using all the keys (which we previously defined as the first letter of the corresponding word). If the grain exists, you will use mapQuery to scan for all the words in the grain.

Client.java
private void getAllWordsStats(Grainite client) throws Exception {
    List<String> allWordsList = new ArrayList<>();

    Table wordStats =
      client.getTable(Constants.APP_NAME, Constants.WORD_STATS_TABLE);

    // iterate over the grains in words table
    for (Iterator<Grain> iter = wordStats.scan(); iter.hasNext();) {
      Grain g = iter.next();

      // scan through all words in this grain and add them to a running list
      Iterator<KeyValue> it = g.mapQuery(MapQuery.newBuilder()
                                            .setMapId(0)
                                            .setRange(Key.minKey(), true, Key.maxKey(), true)
                                            .build());
      while (it.hasNext()) {
        KeyValue kv = it.next();
        allWordsList.add(String.format("%8d: %s", kv.getValue().asLong(),
                kv.getKey().asString()));
      }
    }

    // Print the words out in sorted order
    allWordsList.stream()
            .sorted((x, y) -> x.split(":")[1].compareTo(y.split(":")[1]))
            .forEach(System.out::println);
}

If the grain exists, you will use mapGetRange to scan for all the words in the grain.

Last updated