// purchase product and return relevant product recommendations
  public List<Product> purchase(Customer customer, Product product) {
    purchasesCache.put(customer.id, product.id);

    ProductAssociativityGraph graph = productAssociativityGraphMap.get(product.category);
    Vertex v = Vertex.create(product.id);
    List<Vertex> associations = graph.getProductAssociations(v);

    int recommendSize = Math.min(associations.size(), maxNumRecommendations);
    return associations
        .stream()
        .map(vertex -> productCache.get(vertex.productId))
        .limit(recommendSize)
        .collect(Collectors.toList());
  }
  // batch job (possibly real-time using streaming APIs)
  public static void createProductAssociativityGraphPerCategory() {
    Set<String> customers = purchasesCache.keySet();
    customers.forEach(
        customer -> {
          List<String> customerPurchases = Lists.newArrayList(purchasesCache.get(customer));
          for (int i = 0; i < customerPurchases.size(); i++) {
            for (int j = i + 1; j < customerPurchases.size(); j++) {
              Product product1 = productCache.get(customerPurchases.get(i));
              Product product2 = productCache.get(customerPurchases.get(j));

              if (product1.category.equals(product2.category)) {
                ProductAssociativityGraph graph =
                    productAssociativityGraphMap.getOrDefault(
                        product1.category, ProductAssociativityGraph.create());

                graph.addAssociation(Vertex.create(product1.id), Vertex.create(product2.id), 1);
                productAssociativityGraphMap.putIfAbsent(product1.category, graph);
              }
            }
          }
        });
  }