3

I'm having trouble dealing with what I thought would be a simple problem. Basically, I need a java.util.Map<String, String>, where ids end up being the map keys, and someField of my document ends up in the values.

I'm really really stuck on this, which greatly surprises me. I've tried writing a separate view:

@View(map="function(d) { if (d.someField) { emit(d.someField, null); } }", name = "someField")

and then use the following Java:

public Map<String, String> getSomeFields() throws JsonParseException, JsonMappingException, IOException {
    ViewQuery q = new ViewQuery().designDocId("_design/" + entity.getSimpleName()).viewName("someField");
    String result = StreamUtils.inputStreamAsString(db.queryForStream(q), false);
    TypeReference<Map<String, String>> mapTypeRef = new TypeReference<Map<String,String>>() {};
    // mapper is a Jackson ObjectMapper
    return mapper.readValue(result, mapTypeRef);
} 

This is already really ugly, but it also doesn't actually work, as it seems the JSON results that queryForStream returns includes random other stuff, rather than just the result of the query. This causes the readValue call to throw an IOException.

I've also tried using reduce to generate a single object containing all these values, but the result of that is that Couch complains the reduce doesn't reduce enough...

Gijs
  • 5,201
  • 1
  • 27
  • 42

2 Answers2

3

I would do something like this:

ViewQuery query = ...

Map<String, String> map = new HashMap<String, String>();
for (ViewResult.Row row : db.queryView(query)) {
    map.put(row.getId(), row.getKey());
}

return map;
Henrik Barratt Due
  • 5,614
  • 1
  • 21
  • 22
  • This looks to be the simplest solution, thanks! (Note that this won't work as is, AFAICT you need to do a `.getRows()` on the `.queryView` call result before you have the row result set) – Gijs Nov 14 '11 at 15:00
2

You will need to pre parse the output from CouchDB as there is no way to avoid returning all of that metadata with the query.

Firstly, your view needs to emit the right data (the object id, and its value).

@View(map="function(d) { if (d.someField) { emit(d.id, d.someField); } }", name = "someField")

The form of the reply is a JSON object String => Object. I would start by mapping the entire reply to this, then selecting the object with the key "rows" which is a JSON Array. Each element in this array is another JSON Object with keys "id", "key", "value". You will then need to map each of these objects to a key value pair in your output.

public Map<String, String> getSomeFields() 
        throws JsonParseException, JsonMappingException, IOException {

    ViewQuery q = 
        new ViewQuery().designDocId("_design/" + 
        entity.getSimpleName()).viewName("someField");

    String queryRresult = 
        StreamUtils.inputStreamAsString(db.queryForStream(q), false);

    TypeReference<Map<String, Object>> mapTypeRef = 
        new TypeReference<Map<String,Object>>() {};

    TypeReference<List<Map<String,String>>> rowsTypeRef = 
        new TypeReference<List<Map<String,String>>>() {};

    // Map of the top level results which includes the couch meta and the
    // rows. We have to use object, because Each value is of a different 
    // type (string, ints, json objects)
    Map<String,Object> topResultMap = 
        mapper.readValue(queryRresult, mapTypeRef);

    // Once we have the top level result, cast the value for key "rows" as 
    // String, and parse it as a rows type, which is a list of maps.
    List<Map<String,String>> rows = 
        mapper.readValue((String) topResultMap.get("rows"), rowsTypeRef);

    // Finally iterator over that list pulling out the id and the value in 
    // the key and value for the results
    Map<String,String> results = new HashMap<String,String>();
    for (Map<String,String> row : rows)
        results.put(row.get("id"), row.get("value"));

    // And return them
    return results;
}

Lastly, you need to make sure you don't have a reduce part of your CouchDB view. If you do, you must pass "reduce=false" through to couch.

Emma
  • 6,112
  • 2
  • 18
  • 11