package com.redhat.lightblue.assoc; import java.util.List; import org.junit.Test; import org.junit.Assert; import org.skyscreamer.jsonassert.JSONAssert; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.redhat.lightblue.util.Path; import com.redhat.lightblue.util.JsonUtils; import com.redhat.lightblue.query.*; import com.redhat.lightblue.metadata.CompositeMetadata; import com.redhat.lightblue.metadata.AbstractGetMetadata; import com.redhat.lightblue.metadata.EntityMetadata; import com.redhat.lightblue.metadata.TypeResolver; import com.redhat.lightblue.metadata.PredefinedFields; 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.TestDataStoreParser; import com.redhat.lightblue.util.test.AbstractJsonNodeTest; public class RewriteQueryTest extends AbstractJsonNodeTest { private class GMD extends AbstractGetMetadata { public GMD(Projection p, QueryExpression q) { super(p, q); } @Override protected EntityMetadata retrieveMetadata(Path injectionField, String entityName, String version) { try { return getMd("composite/" + entityName + ".json"); } catch (Exception e) { throw new RuntimeException(e); } } } private EntityMetadata getMd(String fname) { try { 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, JsonNodeFactory.instance); EntityMetadata md = parser.parseEntityMetadata(node); PredefinedFields.ensurePredefinedFields(md); return md; } catch (Exception e) { throw new RuntimeException(e); } } private QueryExpression query(String s) throws Exception { return QueryExpression.fromJson(JsonUtils.json(s.replace('\'', '\"'))); } private Projection projection(String s) throws Exception { return Projection.fromJson(JsonUtils.json(s.replace('\'', '\"'))); } @Test public void testReqQuery() throws Exception { GMD gmd = new GMD(projection("{'field':'obj1.c','include':1}"), null); CompositeMetadata md = CompositeMetadata.buildCompositeMetadata(getMd("composite/A.json"), gmd); // Process request query at root AnalyzeQuery pq = new AnalyzeQuery(md, null); QueryExpression q = query("{'$and':[ { 'field':'field1','op':'=','rvalue':'x'},{'field':'obj1.c.*.field1','op':'=','rvalue':'y'}]}"); pq.iterate(q); List<QueryFieldInfo> list = pq.getFieldInfo(); // Rewrite the query at root RewriteQuery rw = new RewriteQuery(md, md); RewriteQuery.RewriteQueryResult result = rw.rewriteQuery(q, list); QueryExpression newq = result.query; List<BoundObject> bindings = result.bindings; Assert.assertEquals(0, bindings.size()); Assert.assertTrue(newq instanceof ValueComparisonExpression); JSONAssert.assertEquals("{field:field1,op:$eq,rvalue:x}", newq.toString(), false); rw = new RewriteQuery(md, md.getChildMetadata(new Path("obj1.c"))); result = rw.rewriteQuery(q, list); bindings = result.bindings; newq = result.query; Assert.assertEquals(0, bindings.size()); Assert.assertTrue(newq instanceof ValueComparisonExpression); JSONAssert.assertEquals("{field:field1,op:$eq,rvalue:y}", newq.toString(), false); } @Test public void testSimpleAssocQuery() throws Exception { GMD gmd = new GMD(projection("{'field':'obj1.c','include':1}"), null); CompositeMetadata md = CompositeMetadata.buildCompositeMetadata(getMd("composite/A.json"), gmd); AnalyzeQuery pq = new AnalyzeQuery(md, md.getResolvedReferenceOfField(new Path("obj1.c"))); QueryExpression q = query("{'field':'_id','op':'$eq','rfield':'$parent.c_ref'}"); pq.iterate(q); List<QueryFieldInfo> list = pq.getFieldInfo(); // Rewrite for C. This means, A docs are retrieved, now we'll retrieve C docs RewriteQuery rw = new RewriteQuery(md, md.getChildMetadata(new Path("obj1.c"))); RewriteQuery.RewriteQueryResult result = rw.rewriteQuery(q, list); QueryExpression newq = result.query; List<BoundObject> bindings = result.bindings; Assert.assertEquals(1, bindings.size()); Assert.assertTrue(bindings.get(0) instanceof BoundValue); Assert.assertTrue(newq instanceof ValueComparisonExpression); // Rewrite for A. This means, C docs are retrieved, and we'll retrieve A docs (reverse relationship) rw = new RewriteQuery(md, md); result = rw.rewriteQuery(q, list); bindings = result.bindings; newq = result.query; Assert.assertEquals(1, bindings.size()); Assert.assertTrue(bindings.get(0) instanceof BoundValue); Assert.assertTrue(newq instanceof ValueComparisonExpression); } @Test public void testReqQuery_forEach_arr_points_to_ref() throws Exception { GMD gmd = new GMD(projection("{'field':'obj1.c','include':1}"), null); CompositeMetadata md = CompositeMetadata.buildCompositeMetadata(getMd("composite/A.json"), gmd); AnalyzeQuery pq = new AnalyzeQuery(md, null); QueryExpression q = query("{'array' : 'obj1.c', 'elemMatch':{'field':'_id','op':'$eq','rfield':'$parent.c_ref'}}"); pq.iterate(q); List<QueryFieldInfo> list = pq.getFieldInfo(); // Rewrite for C. This means, A docs are retrieved, now we'll retrieve C docs RewriteQuery rw = new RewriteQuery(md, md.getChildMetadata(new Path("obj1.c"))); RewriteQuery.RewriteQueryResult result = rw.rewriteQuery(q, list); QueryExpression newq = result.query; List<BoundObject> bindings = result.bindings; Assert.assertEquals(1, bindings.size()); Assert.assertTrue(bindings.get(0) instanceof BoundValue); Assert.assertTrue(newq instanceof ValueComparisonExpression); // Rewrite for A. This means, C docs are retrieved, and we'll retrieve A docs (reverse relationship) rw = new RewriteQuery(md, md); result = rw.rewriteQuery(q, list); bindings = result.bindings; newq = result.query; Assert.assertEquals(1, bindings.size()); Assert.assertTrue(bindings.get(0) instanceof BoundValue); Assert.assertTrue(newq instanceof ValueComparisonExpression); } @Test public void testReqQuery_forEach_arr_points_into_ref() throws Exception { GMD gmd = new GMD(projection("{'field':'us','include':1}"), null); CompositeMetadata md = CompositeMetadata.buildCompositeMetadata(getMd("composite/L.json"), gmd); AnalyzeQuery pq = new AnalyzeQuery(md, null); QueryExpression q = query("{'array':'us.*.authentications','elemMatch':{ '$and':[ { 'field':'principal','op':'$in','values':['a']}, {'field':'providerName','op':'$eq','rvalue':'p'} ] } }"); pq.iterate(q); List<QueryFieldInfo> list = pq.getFieldInfo(); // L is the parent, U is the child // Rewrite for U. This means, L docs are retrieved, now we'll retrieve U docs // This is the trivial rewrite case RewriteQuery rw = new RewriteQuery(md, md.getChildMetadata(new Path("us"))); RewriteQuery.RewriteQueryResult result = rw.rewriteQuery(q, list); QueryExpression newq = result.query; System.out.println(newq); List<BoundObject> bindings = result.bindings; Assert.assertEquals("authentications", ((ArrayMatchExpression) newq).getArray().toString()); // Rewrite for L. That means, U docs are retrieved, and we'll retrieve L // This is the reverse case rw = new RewriteQuery(md, md); result = rw.rewriteQuery(q, list); bindings = result.bindings; newq = result.query; System.out.println(newq); Assert.assertTrue(newq instanceof RewriteQuery.TruePH); } @Test public void testAssocQuery_forEach_arr_points_to_ref() throws Exception { GMD gmd = new GMD(projection("{'field':'users','include':1}"), null); CompositeMetadata md = CompositeMetadata.buildCompositeMetadata(getMd("composite/UC.json"), gmd); AnalyzeQuery pq = new AnalyzeQuery(md, md.getResolvedReferenceOfField(new Path("users"))); QueryExpression q = query("{'$and':[ { 'field': '_id','op': '$eq','rfield': '$parent.userId' }," + "{'array': 'authentications','elemMatch': {'$and': [" + "{'field': 'providerName','op': '$eq','rvalue': 'p'}," + "{'field': 'principal','op': '$eq','rfield': '$parent.$parent.userRedHatPrincipal'}]} } ] }"); pq.iterate(q); List<QueryFieldInfo> list = pq.getFieldInfo(); // UC is the parent, U is the child // Rewrite for U. This means, UC docs are retrieved, now we'll retrieve U docs // This is the trivial rewrite case RewriteQuery rw = new RewriteQuery(md, md.getChildMetadata(new Path("users"))); RewriteQuery.RewriteQueryResult result = rw.rewriteQuery(q, list); QueryExpression newq = result.query; List<BoundObject> bindings = result.bindings; Assert.assertEquals(2, bindings.size()); Assert.assertTrue(bindings.get(0) instanceof BoundValue); // Rewrite for UC. That means, U docs are retrieved, and we'll retrieve UC // This is the reverse case rw = new RewriteQuery(md, md); result = rw.rewriteQuery(q, list); bindings = result.bindings; newq = result.query; System.out.println(newq); Assert.assertEquals(2, bindings.size()); } }