From 81d13ff6f75de34ec348e51977e6d7e0926ca0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Basl=C3=A9?= Date: Mon, 29 Jun 2015 19:54:08 +0200 Subject: [PATCH] allow adhoc unmarshalling for individual n1ql results --- .../core/CouchbaseTemplateTests.java | 75 +++++++++++++++---- .../couchbase/core/CouchbaseTemplate.java | 14 +++- .../JacksonTranslationService.java | 12 ++- .../translation/TranslationService.java | 12 +++ .../JacksonTranslationServiceTests.java | 14 ++++ 5 files changed, 110 insertions(+), 17 deletions(-) diff --git a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java index 67624d4f..e2acebb6 100644 --- a/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java +++ b/src/integration/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java @@ -17,12 +17,15 @@ package org.springframework.data.couchbase.core; import static com.couchbase.client.java.query.Select.select; +import static com.couchbase.client.java.query.dsl.Expression.i; +import static com.couchbase.client.java.query.dsl.Expression.s; import static com.couchbase.client.java.query.dsl.Expression.x; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; @@ -41,7 +44,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import org.junit.runner.RunWith; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.OptimisticLockingFailureException; @@ -219,7 +221,7 @@ public class CouchbaseTemplateTests { @Test public void shouldQueryRaw() { - Query query = Query.simple(select("name").from(Expression.i(client.name())) + Query query = Query.simple(select("name").from(i(client.name())) .where(x("name").isNotMissing())); QueryResult queryResult = template.queryN1QL(query); @@ -228,15 +230,22 @@ public class CouchbaseTemplateTests { assertFalse(queryResult.allRows().isEmpty()); } - @Test(expected = NotImplementedException.class) //TODO remove when implemented + @Test public void shouldQueryWithMapping() { - Query query = Query.simple(select("name").from(Expression.i(client.name())) - .where(x("name").isNotMissing())); + FullFragment ff1 = new FullFragment("fullFragment1", 1, "fullFragment", "test1"); + FullFragment ff2 = new FullFragment("fullFragment2", 2, "fullFragment", "test2"); + template.save(Arrays.asList(ff1, ff2)); - List fragments = template.findByN1QL(query, BeerFragment.class); + Query query = Query.simple(select(i("value")) //"value" is a n1ql keyword apparently + .from(i(client.name())) + .where(x("type").eq(s("fullFragment")) + .and(x("criteria").gt(1)))); + + List fragments = template.findByN1QL(query, Fragment.class); assertNotNull(fragments); assertFalse(fragments.isEmpty()); - //TODO assert the content of the fragments, etc... + assertEquals(1, fragments.size()); + assertEquals("test2", fragments.get(0).value); } @Test @@ -580,15 +589,55 @@ public class CouchbaseTemplateTests { } } - static class BeerFragment { - private String name; + @Document + static class FullFragment { - public String getName() { - return name; + @Id + private String id; + + private long criteria; + + private String type; + + private String value; + + public FullFragment(String id, long criteria, String type, String value) { + this.id = id; + this.criteria = criteria; + this.type = type; + this.value = value; } - public void setName(String name) { - this.name = name; + public String getId() { + return id; + } + + public long getCriteria() { + return criteria; + } + + public String getType() { + return type; + } + + public String getValue() { + return value; + } + + public void setCriteria(long criteria) { + this.criteria = criteria; + } + + public void setType(String type) { + this.type = type; + } + + public void setValue(String value) { + this.value = value; } } + + static class Fragment { + public String value; + } } diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index dfbce61a..d4a99b1b 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -34,12 +34,12 @@ import com.couchbase.client.java.document.RawJsonDocument; import com.couchbase.client.java.error.CASMismatchException; import com.couchbase.client.java.query.Query; import com.couchbase.client.java.query.QueryResult; +import com.couchbase.client.java.query.QueryRow; import com.couchbase.client.java.view.ViewQuery; import com.couchbase.client.java.view.ViewResult; import com.couchbase.client.java.view.ViewRow; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -286,9 +286,17 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP @Override public List findByN1QL(Query n1ql, Class entityClass) { - //TODO find a way of mapping content to T + QueryResult queryResult = queryN1QL(n1ql); + + List allRows = queryResult.allRows(); + List result = new ArrayList(allRows.size()); + for (QueryRow row : allRows) { + String json = row.value().toString(); + T decoded = translationService.decodeFragment(json, entityClass); + result.add(decoded); + } //TODO error handling - throw new NotImplementedException(); + return result; } @Override diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java b/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java index a178f679..84968d6f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java @@ -236,7 +236,17 @@ public class JacksonTranslationService implements TranslationService, Initializi case VALUE_NULL: return null; default: - throw new MappingException("Could not decode primitve value " + token); + throw new MappingException("Could not decode primitive value " + token); + } + } + + @Override + public T decodeFragment(String source, Class target) { + try { + return objectMapper.readValue(source, target); + } + catch (IOException e) { + throw new RuntimeException("Cannot decode ad-hoc JSON", e); } } diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/translation/TranslationService.java b/src/main/java/org/springframework/data/couchbase/core/convert/translation/TranslationService.java index 00ca6271..cc12a912 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/translation/TranslationService.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/translation/TranslationService.java @@ -16,6 +16,8 @@ package org.springframework.data.couchbase.core.convert.translation; +import com.couchbase.client.java.query.QueryRow; + import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseStorable; @@ -42,4 +44,14 @@ public interface TranslationService { * @return a properly populated document to work with. */ CouchbaseStorable decode(String source, CouchbaseStorable target); + + /** + * Decodes an ad-hoc JSON object into a corresponding "case" class. + * + * @param source the JSON for the ad-hoc JSON object (from a N1QL {@link QueryRow} for instance). + * @param target the target class information. + * @param the target class. + * @return an ad-hoc instance of the decoded JSON into the corresponding "case" class. + */ + T decodeFragment(String source, Class target); } diff --git a/src/test/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationServiceTests.java b/src/test/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationServiceTests.java index dad6ed97..f566ffaf 100644 --- a/src/test/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationServiceTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationServiceTests.java @@ -17,6 +17,7 @@ package org.springframework.data.couchbase.core.convert.translation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import org.junit.Before; import org.junit.Test; @@ -35,6 +36,7 @@ public class JacksonTranslationServiceTests { @Before public void setup() { service = new JacksonTranslationService(); + ((JacksonTranslationService) service).afterPropertiesSet(); } @Test @@ -52,4 +54,16 @@ public class JacksonTranslationServiceTests { service.decode(source, target); assertEquals("русский", target.get("language")); } + + @Test + public void shouldDecodeAdHocFragment() { + String source = "{\"language\":\"french\"}"; + LanguageFragment f = service.decodeFragment(source, LanguageFragment.class); + assertNotNull(f); + assertEquals("french", f.language); + } + + private static class LanguageFragment { + public String language; + } }