/* Copyright 2013 Red Hat, Inc. and/or its affiliates. This file is part of lightblue. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.redhat.lightblue.eval; import org.junit.Test; import org.junit.Assert; import org.junit.Ignore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.redhat.lightblue.util.JsonDoc; import com.redhat.lightblue.util.Path; import com.redhat.lightblue.util.JsonUtils; import com.redhat.lightblue.util.test.AbstractJsonNodeTest; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.CompositeMetadata; import com.redhat.lightblue.metadata.TypeResolver; import com.redhat.lightblue.metadata.types.DefaultTypes; import com.redhat.lightblue.metadata.parser.Extensions; import com.redhat.lightblue.metadata.parser.JSONMetadataParser; import com.redhat.lightblue.query.Projection; import com.redhat.lightblue.TestDataStoreParser; import java.io.IOException; public class ProjectionTest extends AbstractJsonNodeTest { private static final JsonNodeFactory factory = JsonNodeFactory.withExactBigDecimals(true); private static JsonNode json(String q) { try { return JsonUtils.json(q.replace('\'', '\"')); } catch (IOException e) { throw new RuntimeException(e); } } private EntityMetadata getMd(String fname) throws Exception { JsonNode node = loadJsonNode(fname); Extensions<JsonNode> extensions = new Extensions<>(); extensions.addDefaultExtensions(); extensions.registerDataStoreParser("mongo", new TestDataStoreParser<JsonNode>()); TypeResolver resolver = new DefaultTypes(); JSONMetadataParser parser = new JSONMetadataParser(extensions, resolver, factory); return parser.parseEntityMetadata(node); } private JsonDoc getDoc(String fname) throws Exception { JsonNode node = loadJsonNode(fname); return new JsonDoc(node); } private Projector projector(String str, EntityMetadata md) { Projection p = Projection.fromJson(json(str)); return Projector.getInstance(p, md); } @Test public void basicProjectionTest() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'*','include':1}"; Projector projector = projector(pr, md); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertEquals("test", newDoc.get(new Path("objectType")).asText()); Assert.assertEquals("value1", newDoc.get(new Path("field1")).asText()); Assert.assertEquals("value2", newDoc.get(new Path("field2")).asText()); Assert.assertEquals(3, newDoc.get(new Path("field3")).asInt()); Assert.assertEquals(4.0, newDoc.get(new Path("field4")).asDouble(), 0.001); Assert.assertEquals(true, newDoc.get(new Path("field5")).asBoolean()); Assert.assertNull(newDoc.get(new Path("field6"))); Assert.assertNull(newDoc.get(new Path("field6.nf1"))); Assert.assertNull(newDoc.get(new Path("field6.nf2"))); Assert.assertNull(newDoc.get(new Path("field6.nf3"))); Assert.assertNull(newDoc.get(new Path("field6.nf4"))); Assert.assertNull(newDoc.get(new Path("field6.nf5"))); Assert.assertNull(newDoc.get(new Path("field6.nf6"))); Assert.assertNull(newDoc.get(new Path("field6.nf7"))); Assert.assertNull(newDoc.get(new Path("field7"))); Assert.assertNull(newDoc.get(new Path("field7"))); } @Test public void basicProjectionTest_recursive() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'*','include':1,'recursive':1}"; Projector projector = projector(pr, md); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertEquals("test", newDoc.get(new Path("objectType")).asText()); Assert.assertEquals("value1", newDoc.get(new Path("field1")).asText()); Assert.assertEquals("value2", newDoc.get(new Path("field2")).asText()); Assert.assertEquals(3, newDoc.get(new Path("field3")).asInt()); Assert.assertEquals(4.0, newDoc.get(new Path("field4")).asDouble(), 0.001); Assert.assertEquals(true, newDoc.get(new Path("field5")).asBoolean()); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertEquals("nvalue1", newDoc.get(new Path("field6.nf1")).asText()); Assert.assertEquals("nvalue2", newDoc.get(new Path("field6.nf2")).asText()); Assert.assertEquals(4, newDoc.get(new Path("field6.nf3")).asInt()); Assert.assertEquals(false, newDoc.get(new Path("field6.nf4")).asBoolean()); Assert.assertEquals(5, newDoc.get(new Path("field6.nf5.0")).asInt()); Assert.assertEquals(JSON_NODE_FACTORY.nullNode(), newDoc.get(new Path("field6.nf5.1"))); Assert.assertEquals(15, newDoc.get(new Path("field6.nf5.2")).asInt()); Assert.assertEquals(20, newDoc.get(new Path("field6.nf5.3")).asInt()); Assert.assertEquals("one", newDoc.get(new Path("field6.nf6.0")).asText()); Assert.assertEquals("two", newDoc.get(new Path("field6.nf6.1")).asText()); Assert.assertEquals("three", newDoc.get(new Path("field6.nf6.2")).asText()); Assert.assertEquals("four", newDoc.get(new Path("field6.nf6.3")).asText()); Assert.assertEquals("nnvalue1", newDoc.get(new Path("field6.nf7.nnf1")).asText()); Assert.assertEquals(2, newDoc.get(new Path("field6.nf7.nnf2")).asInt()); Assert.assertEquals(4, newDoc.get(new Path("field7")).size()); Assert.assertEquals("elvalue0_1", newDoc.get(new Path("field7.0.elemf1")).asText()); Assert.assertEquals("elvalue1_1", newDoc.get(new Path("field7.1.elemf1")).asText()); Assert.assertEquals("elvalue2_1", newDoc.get(new Path("field7.2.elemf1")).asText()); Assert.assertEquals("elvalue3_1", newDoc.get(new Path("field7.3.elemf1")).asText()); } @Test public void basicProjectionTest_exclusion() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "[{'field':'*','include':1,'recursive':1},{'field':'field7','include':0}]"; Projector projector = projector(pr, md); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertEquals("test", newDoc.get(new Path("objectType")).asText()); Assert.assertEquals("value1", newDoc.get(new Path("field1")).asText()); Assert.assertEquals("value2", newDoc.get(new Path("field2")).asText()); Assert.assertEquals(3, newDoc.get(new Path("field3")).asInt()); Assert.assertEquals(4.0, newDoc.get(new Path("field4")).asDouble(), 0.001); Assert.assertEquals(true, newDoc.get(new Path("field5")).asBoolean()); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertEquals("nvalue1", newDoc.get(new Path("field6.nf1")).asText()); Assert.assertEquals("nvalue2", newDoc.get(new Path("field6.nf2")).asText()); Assert.assertEquals(4, newDoc.get(new Path("field6.nf3")).asInt()); Assert.assertEquals(false, newDoc.get(new Path("field6.nf4")).asBoolean()); Assert.assertEquals(5, newDoc.get(new Path("field6.nf5.0")).asInt()); Assert.assertEquals(JSON_NODE_FACTORY.nullNode(), newDoc.get(new Path("field6.nf5.1"))); Assert.assertEquals(15, newDoc.get(new Path("field6.nf5.2")).asInt()); Assert.assertEquals(20, newDoc.get(new Path("field6.nf5.3")).asInt()); Assert.assertEquals("one", newDoc.get(new Path("field6.nf6.0")).asText()); Assert.assertEquals("two", newDoc.get(new Path("field6.nf6.1")).asText()); Assert.assertEquals("three", newDoc.get(new Path("field6.nf6.2")).asText()); Assert.assertEquals("four", newDoc.get(new Path("field6.nf6.3")).asText()); Assert.assertEquals("nnvalue1", newDoc.get(new Path("field6.nf7.nnf1")).asText()); Assert.assertEquals(2, newDoc.get(new Path("field6.nf7.nnf2")).asInt()); Assert.assertNull(newDoc.get(new Path("field7"))); } @Test public void projectionListTest() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "[{'field':'field6.*','include':1},{'field':'field5'}]"; Projector projector = projector(pr, md); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("field2"))); Assert.assertNull(newDoc.get(new Path("field3"))); Assert.assertNull(newDoc.get(new Path("field4"))); Assert.assertEquals(true, newDoc.get(new Path("field5")).asBoolean()); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertEquals("nvalue1", newDoc.get(new Path("field6.nf1")).asText()); Assert.assertEquals("nvalue2", newDoc.get(new Path("field6.nf2")).asText()); Assert.assertEquals(4, newDoc.get(new Path("field6.nf3")).asInt()); Assert.assertEquals(false, newDoc.get(new Path("field6.nf4")).asBoolean()); Assert.assertNull(newDoc.get(new Path("field6.nf5"))); Assert.assertNull(newDoc.get(new Path("field6.nf6"))); Assert.assertNull(newDoc.get(new Path("field6.nf7"))); Assert.assertNull(newDoc.get(new Path("field7"))); } @Test public void arrayRangeTest() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field6.nf6','range':[1,2],'project':{'field':'*'}}"; Projector projector = projector(pr, md); System.out.println(projector.toString()); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("field2"))); Assert.assertNull(newDoc.get(new Path("field3"))); Assert.assertNull(newDoc.get(new Path("field4"))); Assert.assertNull(newDoc.get(new Path("field5"))); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertNull(newDoc.get(new Path("field6.nf1"))); Assert.assertNull(newDoc.get(new Path("field6.nf2"))); Assert.assertNull(newDoc.get(new Path("field6.nf3"))); Assert.assertNull(newDoc.get(new Path("field6.nf4"))); Assert.assertNull(newDoc.get(new Path("field6.nf5"))); Assert.assertEquals("two", newDoc.get(new Path("field6.nf6.0")).asText()); Assert.assertEquals("three", newDoc.get(new Path("field6.nf6.1")).asText()); Assert.assertEquals(2, newDoc.get(new Path("field6.nf6")).size()); Assert.assertNull(newDoc.get(new Path("field6.nf7"))); Assert.assertNull(newDoc.get(new Path("field7"))); } @Test public void arrayRangeTest_Projection() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field6.nf6','range':[1,2],'projection':{'field':'*'}}"; Projector projector = projector(pr, md); System.out.println(projector.toString()); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("field2"))); Assert.assertNull(newDoc.get(new Path("field3"))); Assert.assertNull(newDoc.get(new Path("field4"))); Assert.assertNull(newDoc.get(new Path("field5"))); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertNull(newDoc.get(new Path("field6.nf1"))); Assert.assertNull(newDoc.get(new Path("field6.nf2"))); Assert.assertNull(newDoc.get(new Path("field6.nf3"))); Assert.assertNull(newDoc.get(new Path("field6.nf4"))); Assert.assertNull(newDoc.get(new Path("field6.nf5"))); Assert.assertEquals("two", newDoc.get(new Path("field6.nf6.0")).asText()); Assert.assertEquals("three", newDoc.get(new Path("field6.nf6.1")).asText()); Assert.assertEquals(2, newDoc.get(new Path("field6.nf6")).size()); Assert.assertNull(newDoc.get(new Path("field6.nf7"))); Assert.assertNull(newDoc.get(new Path("field7"))); } @Test public void arrayRangeTestForZeroUpperBound() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field6.nf6','range':[1,0],'projection':{'field':'*'}}"; Projector projector = projector(pr, md); System.out.println(projector.toString()); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("field6"))); } @Test public void arrayRangeTestForNegativeUpperBound() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field6.nf6','range':[1,-8],'projection':{'field':'*'}}"; Projector projector = projector(pr, md); System.out.println(projector.toString()); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("field6"))); } @Test public void arrayRangeTestForZeroZeroBounds() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field6.nf6','range':[0,0],'projection':{'field':'*'}}"; Projector projector = projector(pr, md); System.out.println(projector.toString()); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNotNull(newDoc.get(new Path("field6"))); Assert.assertEquals("one", newDoc.get(new Path("field6.nf6.0")).asText()); Assert.assertEquals(1, newDoc.get(new Path("field6.nf6")).size()); } @Test public void arrayNestedQTest() throws Exception { EntityMetadata md = getMd("./testMetadata.json"); JsonDoc doc = getDoc("./sample1.json"); String pr = "{'field':'field7','match':{'field':'elemf1','op':'$eq','rvalue':'elvalue0_1'}}"; Projector projector = projector(pr, md); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("field2"))); Assert.assertNull(newDoc.get(new Path("field3"))); Assert.assertNull(newDoc.get(new Path("field4"))); Assert.assertNull(newDoc.get(new Path("field5"))); Assert.assertNull(newDoc.get(new Path("field6"))); Assert.assertNotNull(newDoc.get(new Path("field7"))); Assert.assertEquals(1, newDoc.get(new Path("field7")).size()); Assert.assertEquals("elvalue0_1", newDoc.get(new Path("field7.0.elemf1")).asText()); } private class SimpleGMD implements CompositeMetadata.GetMetadata { public EntityMetadata getMetadata(Path injectionField, String entityName, String version) { try { return getMd("composite/" + entityName + ".json"); } catch (Exception e) { throw new RuntimeException(e); } } } // This test is no longer valid. Projection evaluation don't care entity // boundaries. // Composite metadata construction interprets the projections, and // constructs // the tree excluding unprojected entities. @Test @Ignore public void recursiveInclusionsDontCrossEntityBoundaries() throws Exception { EntityMetadata md = getMd("./composite/A.json"); CompositeMetadata cmd = CompositeMetadata.buildCompositeMetadata(md, new SimpleGMD()); JsonDoc doc = getDoc("./composite/doc1.json"); String pr = "{'field':'obj1','include':1,'recursive':1}"; Projector projector = projector(pr, cmd); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("b"))); Assert.assertNull(newDoc.get(new Path("b_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1"))); Assert.assertNotNull(newDoc.get(new Path("obj1.field1"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c_ref"))); Assert.assertNull(newDoc.get(new Path("obj1.c"))); } // This test is no longer valid. Projection evaluation don't care entity // boundaries. // Composite metadata construction interprets the projections, and // constructs // the tree excluding unprojected entities. @Test @Ignore public void explicitInclusionByPatternCrossesEntityBoundaries() throws Exception { EntityMetadata md = getMd("./composite/A.json"); CompositeMetadata cmd = CompositeMetadata.buildCompositeMetadata(md, new SimpleGMD()); JsonDoc doc = getDoc("./composite/doc1.json"); String pr = "{'field':'obj1.*','include':1,'recursive':1}"; Projector projector = projector(pr, cmd); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("b"))); Assert.assertNull(newDoc.get(new Path("b_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1"))); Assert.assertNotNull(newDoc.get(new Path("obj1.field1"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c"))); Assert.assertEquals(2, newDoc.get(new Path("obj1.c")).size()); } // This test is no longer valid. Projection evaluation don't care entity // boundaries. // Composite metadata construction interprets the projections, and // constructs // the tree excluding unprojected entities. @Test @Ignore public void explicitInclusionCrossesEntityBoundaries() throws Exception { EntityMetadata md = getMd("./composite/A.json"); CompositeMetadata cmd = CompositeMetadata.buildCompositeMetadata(md, new SimpleGMD()); JsonDoc doc = getDoc("./composite/doc1.json"); String pr = "{'field':'obj1.c','include':1,'recursive':1}"; Projector projector = projector(pr, cmd); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("b"))); Assert.assertNull(newDoc.get(new Path("b_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1"))); Assert.assertNull(newDoc.get(new Path("obj1.field1"))); Assert.assertNull(newDoc.get(new Path("obj1.c_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c"))); Assert.assertEquals(2, newDoc.get(new Path("obj1.c")).size()); Assert.assertNotNull(newDoc.get(new Path("obj1.c.0._id"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c.1._id"))); Assert.assertNull(newDoc.get(new Path("obj1.c.0.obj1.d"))); Assert.assertNull(newDoc.get(new Path("obj1.c.1.obj1.d"))); } // This test is no longer valid. Projection evaluation don't care entity // boundaries. // Composite metadata construction interprets the projections, and // constructs // the tree excluding unprojected entities. @Test @Ignore public void arrayInclusionIncludesEntity() throws Exception { EntityMetadata md = getMd("./composite/A.json"); CompositeMetadata cmd = CompositeMetadata.buildCompositeMetadata(md, new SimpleGMD()); JsonDoc doc = getDoc("./composite/doc1.json"); String pr = "{'field':'obj1.c','range':[0,1],'project':{'field':'*','include':1,'recursive':1}}"; Projector projector = projector(pr, cmd); JsonDoc newDoc = projector.project(doc, factory); System.out.println(pr + ":" + newDoc.getRoot()); Assert.assertNull(newDoc.get(new Path("objectType"))); Assert.assertNull(newDoc.get(new Path("field1"))); Assert.assertNull(newDoc.get(new Path("b"))); Assert.assertNull(newDoc.get(new Path("b_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1"))); Assert.assertNull(newDoc.get(new Path("obj1.field1"))); Assert.assertNull(newDoc.get(new Path("obj1.c_ref"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c"))); Assert.assertEquals(2, newDoc.get(new Path("obj1.c")).size()); Assert.assertNotNull(newDoc.get(new Path("obj1.c.0._id"))); Assert.assertNotNull(newDoc.get(new Path("obj1.c.1._id"))); Assert.assertNull(newDoc.get(new Path("obj1.c.0.obj1.d"))); Assert.assertNull(newDoc.get(new Path("obj1.c.1.obj1.d"))); } }