private void doGetAction(JsonValues values, PrintWriter writer) {
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    Gson gson = new Gson();
    String log = new String();

    Filter keyFilter =
        new FilterPredicate("random", FilterOperator.GREATER_THAN_OR_EQUAL, getRandomLong());
    // TODO: fix filtering of expansions
    /*
    TreeSet<String> missingExpansions= new TreeSet<String>();
    for (Expansion expansion : values.expansions) {
    	if (expansion.present == false) {
    		Filter filter = new FilterPredicate(expansion.name,
    				FilterOperator.NOT_EQUAL,
    				true);
    		keyFilter = CompositeFilterOperator.and(keyFilter, filter);
    		missingExpansions.add(expansion.name);
    	}
    }
    writer.println(keyFilter.toString());
    */
    Query q = new Query("Record").setFilter(keyFilter).addSort("random", SortDirection.ASCENDING);

    /*
    q.addSort("random", SortDirection.ASCENDING);
    for (String missingExpansion : missingExpansions) {
    	q.addSort(missingExpansion, SortDirection.ASCENDING);
    }
    */

    PreparedQuery pq = datastore.prepare(q);
    Iterator<Entity> entityIterator = pq.asIterable().iterator();
    if (entityIterator.hasNext() == false) {
      q = new Query("Record").addSort("random", SortDirection.ASCENDING);
      pq = datastore.prepare(q);
      entityIterator = pq.asIterable().iterator();
      if (entityIterator.hasNext() == false) {
        throw new RuntimeException("no data in datastore");
      }
    }
    Entity record = entityIterator.next();
    JsonValues resultValues = entityToJsonValues(record);
    resultValues.debug = log;
    String result = gson.toJson(resultValues);

    writer.println(result);
  }
  private JsonValues entityToJsonValues(Entity entity) {
    Gson gson = new Gson();
    JsonValues values = new JsonValues();

    values.deck = gson.fromJson(entity.getKey().getName(), Deck.class);
    long total = 0;
    long count = 0;
    for (long rating = 1; rating <= 5; rating++) {
      long ratingCount = 0;
      String ratingKey = getRatingKey(rating);
      if (entity.hasProperty(ratingKey)) {
        ratingCount = (long) entity.getProperty(ratingKey);
      }
      count += ratingCount;
      total += ratingCount * rating;
    }
    values.rating = ((double) total) / count;

    return values;
  }
 @Override
 protected Object parseValue(JsonReader reader, String name) throws Exception {
   switch (JsonValues.valueOf(name)) {
     case rule:
       this.rule = StringFilter.FilterSet.Rule.valueOf(reader.nextString());
       return this.rule;
     case set:
       Gson gson = registerTypeAdapters(new GsonBuilder()).create();
       this.set = gson.fromJson(reader, List.class);
       return this.set;
     default:
       return super.parseValue(reader, name);
   }
 }
 protected Object parseValue(JsonReader reader, String name) throws Exception {
   switch (JsonValues.valueOf(name)) {
     case type:
       this.type = StringFilterMapping.getType(reader.nextString());
       if (this.type != null) {
         TypeAdapter adapter = GSON.getAdapter(this.type);
         if (adapter instanceof GeneralAdapter) {
           this.delegate = (GeneralAdapter) adapter;
         }
       }
       return this.type;
   }
   return null;
 }
 @Override
 protected Object parseValue(JsonReader reader, String name) throws Exception {
   switch (JsonValues.valueOf(name)) {
     case patterns:
       this.patterns = new ArrayList<>();
       reader.beginArray();
       while (reader.peek() != JsonToken.END_ARRAY) {
         this.patterns.add(Pattern.compile(reader.nextString()));
       }
       reader.endArray();
       return this.patterns;
     default:
       return super.parseValue(reader, name);
   }
 }