diff --git a/app/src/org/commcare/models/database/user/models/AndroidCaseIndexTable.java b/app/src/org/commcare/models/database/user/models/AndroidCaseIndexTable.java index f813334b57..15c4988e81 100755 --- a/app/src/org/commcare/models/database/user/models/AndroidCaseIndexTable.java +++ b/app/src/org/commcare/models/database/user/models/AndroidCaseIndexTable.java @@ -9,6 +9,7 @@ import org.commcare.android.database.user.models.ACase; import org.commcare.cases.model.Case; import org.commcare.cases.model.CaseIndex; +import org.commcare.cases.query.queryset.DualTableMultiMatchModelQuerySet; import org.commcare.cases.query.queryset.DualTableSingleMatchModelQuerySet; import org.commcare.modern.database.TableBuilder; import org.commcare.models.database.DbUtil; @@ -322,6 +323,50 @@ public DualTableSingleMatchModelQuerySet bulkReadIndexToCaseIdMatch(String index return set; } + @Override + public DualTableMultiMatchModelQuerySet bulkReadCaseIdToIndexMatch(String indexName, Collection cuedCases) { + DualTableMultiMatchModelQuerySet set = new DualTableMultiMatchModelQuerySet(); + String caseIdIndex = TableBuilder.scrubName(Case.INDEX_CASE_ID); + List> whereParamList = TableBuilder.sqlList(cuedCases, "CAST(? as INT)"); + + for (Pair querySet : whereParamList) { + + String query =String.format( + "SELECT %s,%s " + + "FROM %s " + + "INNER JOIN %s " + + "ON %s = %s " + + "WHERE %s = '%s' " + + "AND " + + "%s IN %s", + + ACase.STORAGE_KEY + "." + DatabaseHelper.ID_COL, COL_CASE_RECORD_ID, + ACase.STORAGE_KEY, + TABLE_NAME, + COL_INDEX_TARGET, caseIdIndex, + COL_INDEX_NAME, indexName, + ACase.STORAGE_KEY + "." + DatabaseHelper.ID_COL, querySet.first); + + android.database.Cursor c = db.rawQuery(query, querySet.second); + + try { + if (c.getCount() == 0) { + return set; + } else { + c.moveToFirst(); + while (!c.isAfterLast()) { + int caseId = c.getInt(c.getColumnIndex(DatabaseHelper.ID_COL)); + int targetCase = c.getInt(c.getColumnIndexOrThrow(COL_CASE_RECORD_ID)); + set.loadResult(caseId, targetCase); + c.moveToNext(); + } + } + } finally { + c.close(); + } + } + return set; + } public static String getArgumentBasedVariableSet(int number) { diff --git a/app/unit-tests/resources/inputs/case_parent_child_index_bulk.xml b/app/unit-tests/resources/inputs/case_parent_child_index_bulk.xml new file mode 100644 index 0000000000..4fc2bd876b --- /dev/null +++ b/app/unit-tests/resources/inputs/case_parent_child_index_bulk.xml @@ -0,0 +1,2311 @@ + + + + unit_test_parent + parent_0 + test_user_id + + + true + + + + + + unit_test_child + child_0_one + test_user_id + + + true + + + parent_0 + + + + + unit_test_child + child_0_two + test_user_id + + + true + + + parent_0 + + + + + + + unit_test_parent + parent_1 + test_user_id + + + true + + + + + + unit_test_child + child_1_one + test_user_id + + + true + + + parent_1 + + + + + unit_test_child + child_1_two + test_user_id + + + true + + + parent_1 + + + + + + + unit_test_parent + parent_2 + test_user_id + + + true + + + + + + unit_test_child + child_2_one + test_user_id + + + true + + + parent_2 + + + + + unit_test_child + child_2_two + test_user_id + + + true + + + parent_2 + + + + + + + unit_test_parent + parent_3 + test_user_id + + + true + + + + + + unit_test_child + child_3_one + test_user_id + + + true + + + parent_3 + + + + + unit_test_child + child_3_two + test_user_id + + + true + + + parent_3 + + + + + + + unit_test_parent + parent_4 + test_user_id + + + true + + + + + + unit_test_child + child_4_one + test_user_id + + + true + + + parent_4 + + + + + unit_test_child + child_4_two + test_user_id + + + true + + + parent_4 + + + + + + + unit_test_parent + parent_5 + test_user_id + + + true + + + + + + unit_test_child + child_5_one + test_user_id + + + true + + + parent_5 + + + + + unit_test_child + child_5_two + test_user_id + + + true + + + parent_5 + + + + + + + unit_test_parent + parent_6 + test_user_id + + + true + + + + + + unit_test_child + child_6_one + test_user_id + + + true + + + parent_6 + + + + + unit_test_child + child_6_two + test_user_id + + + true + + + parent_6 + + + + + + + unit_test_parent + parent_7 + test_user_id + + + true + + + + + + unit_test_child + child_7_one + test_user_id + + + true + + + parent_7 + + + + + unit_test_child + child_7_two + test_user_id + + + true + + + parent_7 + + + + + + + unit_test_parent + parent_8 + test_user_id + + + true + + + + + + unit_test_child + child_8_one + test_user_id + + + true + + + parent_8 + + + + + unit_test_child + child_8_two + test_user_id + + + true + + + parent_8 + + + + + + + unit_test_parent + parent_9 + test_user_id + + + true + + + + + + unit_test_child + child_9_one + test_user_id + + + true + + + parent_9 + + + + + unit_test_child + child_9_two + test_user_id + + + true + + + parent_9 + + + + + + + unit_test_parent + parent_10 + test_user_id + + + true + + + + + + unit_test_child + child_10_one + test_user_id + + + true + + + parent_10 + + + + + unit_test_child + child_10_two + test_user_id + + + true + + + parent_10 + + + + + + + unit_test_parent + parent_11 + test_user_id + + + true + + + + + + unit_test_child + child_11_one + test_user_id + + + true + + + parent_11 + + + + + unit_test_child + child_11_two + test_user_id + + + true + + + parent_11 + + + + + + + unit_test_parent + parent_12 + test_user_id + + + true + + + + + + unit_test_child + child_12_one + test_user_id + + + true + + + parent_12 + + + + + unit_test_child + child_12_two + test_user_id + + + true + + + parent_12 + + + + + + + unit_test_parent + parent_13 + test_user_id + + + true + + + + + + unit_test_child + child_13_one + test_user_id + + + true + + + parent_13 + + + + + unit_test_child + child_13_two + test_user_id + + + true + + + parent_13 + + + + + + + unit_test_parent + parent_14 + test_user_id + + + true + + + + + + unit_test_child + child_14_one + test_user_id + + + true + + + parent_14 + + + + + unit_test_child + child_14_two + test_user_id + + + true + + + parent_14 + + + + + + + unit_test_parent + parent_15 + test_user_id + + + true + + + + + + unit_test_child + child_15_one + test_user_id + + + true + + + parent_15 + + + + + unit_test_child + child_15_two + test_user_id + + + true + + + parent_15 + + + + + + + unit_test_parent + parent_16 + test_user_id + + + true + + + + + + unit_test_child + child_16_one + test_user_id + + + true + + + parent_16 + + + + + unit_test_child + child_16_two + test_user_id + + + true + + + parent_16 + + + + + + + unit_test_parent + parent_17 + test_user_id + + + true + + + + + + unit_test_child + child_17_one + test_user_id + + + true + + + parent_17 + + + + + unit_test_child + child_17_two + test_user_id + + + true + + + parent_17 + + + + + + + unit_test_parent + parent_18 + test_user_id + + + true + + + + + + unit_test_child + child_18_one + test_user_id + + + true + + + parent_18 + + + + + unit_test_child + child_18_two + test_user_id + + + true + + + parent_18 + + + + + + + unit_test_parent + parent_19 + test_user_id + + + true + + + + + + unit_test_child + child_19_one + test_user_id + + + true + + + parent_19 + + + + + unit_test_child + child_19_two + test_user_id + + + true + + + parent_19 + + + + + + + unit_test_parent + parent_20 + test_user_id + + + true + + + + + + unit_test_child + child_20_one + test_user_id + + + true + + + parent_20 + + + + + unit_test_child + child_20_two + test_user_id + + + true + + + parent_20 + + + + + + + unit_test_parent + parent_21 + test_user_id + + + true + + + + + + unit_test_child + child_21_one + test_user_id + + + true + + + parent_21 + + + + + unit_test_child + child_21_two + test_user_id + + + true + + + parent_21 + + + + + + + unit_test_parent + parent_22 + test_user_id + + + true + + + + + + unit_test_child + child_22_one + test_user_id + + + true + + + parent_22 + + + + + unit_test_child + child_22_two + test_user_id + + + true + + + parent_22 + + + + + + + unit_test_parent + parent_23 + test_user_id + + + true + + + + + + unit_test_child + child_23_one + test_user_id + + + true + + + parent_23 + + + + + unit_test_child + child_23_two + test_user_id + + + true + + + parent_23 + + + + + + + unit_test_parent + parent_24 + test_user_id + + + true + + + + + + unit_test_child + child_24_one + test_user_id + + + true + + + parent_24 + + + + + unit_test_child + child_24_two + test_user_id + + + true + + + parent_24 + + + + + + + unit_test_parent + parent_25 + test_user_id + + + true + + + + + + unit_test_child + child_25_one + test_user_id + + + true + + + parent_25 + + + + + unit_test_child + child_25_two + test_user_id + + + true + + + parent_25 + + + + + + + unit_test_parent + parent_26 + test_user_id + + + true + + + + + + unit_test_child + child_26_one + test_user_id + + + true + + + parent_26 + + + + + unit_test_child + child_26_two + test_user_id + + + true + + + parent_26 + + + + + + + unit_test_parent + parent_27 + test_user_id + + + true + + + + + + unit_test_child + child_27_one + test_user_id + + + true + + + parent_27 + + + + + unit_test_child + child_27_two + test_user_id + + + true + + + parent_27 + + + + + + + unit_test_parent + parent_28 + test_user_id + + + true + + + + + + unit_test_child + child_28_one + test_user_id + + + true + + + parent_28 + + + + + unit_test_child + child_28_two + test_user_id + + + true + + + parent_28 + + + + + + + unit_test_parent + parent_29 + test_user_id + + + true + + + + + + unit_test_child + child_29_one + test_user_id + + + true + + + parent_29 + + + + + unit_test_child + child_29_two + test_user_id + + + true + + + parent_29 + + + + + + + unit_test_parent + parent_30 + test_user_id + + + true + + + + + + unit_test_child + child_30_one + test_user_id + + + true + + + parent_30 + + + + + unit_test_child + child_30_two + test_user_id + + + true + + + parent_30 + + + + + + + unit_test_parent + parent_31 + test_user_id + + + true + + + + + + unit_test_child + child_31_one + test_user_id + + + true + + + parent_31 + + + + + unit_test_child + child_31_two + test_user_id + + + true + + + parent_31 + + + + + + + unit_test_parent + parent_32 + test_user_id + + + true + + + + + + unit_test_child + child_32_one + test_user_id + + + true + + + parent_32 + + + + + unit_test_child + child_32_two + test_user_id + + + true + + + parent_32 + + + + + + + unit_test_parent + parent_33 + test_user_id + + + true + + + + + + unit_test_child + child_33_one + test_user_id + + + true + + + parent_33 + + + + + unit_test_child + child_33_two + test_user_id + + + true + + + parent_33 + + + + + + + unit_test_parent + parent_34 + test_user_id + + + true + + + + + + unit_test_child + child_34_one + test_user_id + + + true + + + parent_34 + + + + + unit_test_child + child_34_two + test_user_id + + + true + + + parent_34 + + + + + + + unit_test_parent + parent_35 + test_user_id + + + true + + + + + + unit_test_child + child_35_one + test_user_id + + + true + + + parent_35 + + + + + unit_test_child + child_35_two + test_user_id + + + true + + + parent_35 + + + + + + + unit_test_parent + parent_36 + test_user_id + + + true + + + + + + unit_test_child + child_36_one + test_user_id + + + true + + + parent_36 + + + + + unit_test_child + child_36_two + test_user_id + + + true + + + parent_36 + + + + + + + unit_test_parent + parent_37 + test_user_id + + + true + + + + + + unit_test_child + child_37_one + test_user_id + + + true + + + parent_37 + + + + + unit_test_child + child_37_two + test_user_id + + + true + + + parent_37 + + + + + + + unit_test_parent + parent_38 + test_user_id + + + true + + + + + + unit_test_child + child_38_one + test_user_id + + + true + + + parent_38 + + + + + unit_test_child + child_38_two + test_user_id + + + true + + + parent_38 + + + + + + + unit_test_parent + parent_39 + test_user_id + + + true + + + + + + unit_test_child + child_39_one + test_user_id + + + true + + + parent_39 + + + + + unit_test_child + child_39_two + test_user_id + + + true + + + parent_39 + + + + + + + unit_test_parent + parent_40 + test_user_id + + + true + + + + + + unit_test_child + child_40_one + test_user_id + + + true + + + parent_40 + + + + + unit_test_child + child_40_two + test_user_id + + + true + + + parent_40 + + + + + + + unit_test_parent + parent_41 + test_user_id + + + true + + + + + + unit_test_child + child_41_one + test_user_id + + + true + + + parent_41 + + + + + unit_test_child + child_41_two + test_user_id + + + true + + + parent_41 + + + + + + + unit_test_parent + parent_42 + test_user_id + + + true + + + + + + unit_test_child + child_42_one + test_user_id + + + true + + + parent_42 + + + + + unit_test_child + child_42_two + test_user_id + + + true + + + parent_42 + + + + + + + unit_test_parent + parent_43 + test_user_id + + + true + + + + + + unit_test_child + child_43_one + test_user_id + + + true + + + parent_43 + + + + + unit_test_child + child_43_two + test_user_id + + + true + + + parent_43 + + + + + + + unit_test_parent + parent_44 + test_user_id + + + true + + + + + + unit_test_child + child_44_one + test_user_id + + + true + + + parent_44 + + + + + unit_test_child + child_44_two + test_user_id + + + true + + + parent_44 + + + + + + + unit_test_parent + parent_45 + test_user_id + + + true + + + + + + unit_test_child + child_45_one + test_user_id + + + true + + + parent_45 + + + + + unit_test_child + child_45_two + test_user_id + + + true + + + parent_45 + + + + + + + unit_test_parent + parent_46 + test_user_id + + + true + + + + + + unit_test_child + child_46_one + test_user_id + + + true + + + parent_46 + + + + + unit_test_child + child_46_two + test_user_id + + + true + + + parent_46 + + + + + + + unit_test_parent + parent_47 + test_user_id + + + true + + + + + + unit_test_child + child_47_one + test_user_id + + + true + + + parent_47 + + + + + unit_test_child + child_47_two + test_user_id + + + true + + + parent_47 + + + + + + + unit_test_parent + parent_48 + test_user_id + + + true + + + + + + unit_test_child + child_48_one + test_user_id + + + true + + + parent_48 + + + + + unit_test_child + child_48_two + test_user_id + + + true + + + parent_48 + + + + + + + unit_test_parent + parent_49 + test_user_id + + + true + + + + + + unit_test_child + child_49_one + test_user_id + + + true + + + parent_49 + + + + + unit_test_child + child_49_two + test_user_id + + + true + + + parent_49 + + + + + + + unit_test_parent + parent_50 + test_user_id + + + true + + + + + + unit_test_child + child_50_one + test_user_id + + + true + + + parent_50 + + + + + unit_test_child + child_50_two + test_user_id + + + true + + + parent_50 + + + + + + + unit_test_parent + parent_51 + test_user_id + + + true + + + + + + unit_test_child + child_51_one + test_user_id + + + true + + + parent_51 + + + + + unit_test_child + child_51_two + test_user_id + + + true + + + parent_51 + + + + + + + unit_test_parent + parent_52 + test_user_id + + + true + + + + + + unit_test_child + child_52_one + test_user_id + + + true + + + parent_52 + + + + + unit_test_child + child_52_two + test_user_id + + + true + + + parent_52 + + + + + + + unit_test_parent + parent_53 + test_user_id + + + true + + + + + + unit_test_child + child_53_one + test_user_id + + + true + + + parent_53 + + + + + unit_test_child + child_53_two + test_user_id + + + true + + + parent_53 + + + + + + + unit_test_parent + parent_54 + test_user_id + + + true + + + + + + unit_test_child + child_54_one + test_user_id + + + true + + + parent_54 + + + + + unit_test_child + child_54_two + test_user_id + + + true + + + parent_54 + + + + \ No newline at end of file diff --git a/app/unit-tests/src/org/commcare/android/tests/queries/CaseDbQueryTest.java b/app/unit-tests/src/org/commcare/android/tests/queries/CaseDbQueryTest.java index 5e4ca942b9..134094d292 100644 --- a/app/unit-tests/src/org/commcare/android/tests/queries/CaseDbQueryTest.java +++ b/app/unit-tests/src/org/commcare/android/tests/queries/CaseDbQueryTest.java @@ -10,6 +10,8 @@ import org.javarosa.core.model.instance.TreeReference; import org.javarosa.core.model.trace.EvaluationTraceReporter; import org.javarosa.core.model.trace.ReducingTraceReporter; +import org.javarosa.core.model.trace.AccumulatingReporter; +import org.javarosa.core.model.trace.TraceSerialization; import org.javarosa.core.model.utils.InstrumentationUtils; import org.javarosa.model.xform.XPathReference; import org.javarosa.xpath.XPathParseTool; @@ -25,6 +27,7 @@ import java.util.Collection; import static junit.framework.Assert.assertEquals; +import static junit.framework.TestCase.assertTrue; /** * General case query tests @@ -124,12 +127,18 @@ public void testModelQueryLookupDerivations() { @Test public void testModelSelfReference() { - TestUtils.processResourceTransaction("/inputs/case_test_model_query_lookups.xml"); + TestUtils.processResourceTransaction("/inputs/case_parent_child_index_bulk.xml"); EvaluationContext ec = TestUtils.getEvaluationContextWithoutSession(); + ReducingTraceReporter traceReporter = new ReducingTraceReporter(false); + ec.setDebugModeOn(traceReporter); + evaluate("join(',',instance('casedb')/casedb/case[@case_type='unit_test_child'][@status='open'][true() and " + "count(instance('casedb')/casedb/case[index/parent = instance('casedb')/casedb/case[@case_id=current()/@case_id]/index/parent][false = 'true']) > 0]/@case_id)", "", ec); + String trace = InstrumentationUtils.collectAndClearTraces( + traceReporter, "Child Lookups", TraceSerialization.TraceInfoType.FULL_PROFILE); + assertTrue("Nested index query should trigger a reverse index optimization", trace.contains("reverse index")); } @Test @@ -154,7 +163,7 @@ public void testModelQueryTransformFallback() throws XPathSyntaxException { "string(instance('casedb')/casedb/case[@case_id = instance('casedb')/casedb/case[@case_id=current()/index/parent]/index/parent]/case_name) = 'Valid']/@case_id)", "child_ptwo_one_one", ec); - TreeReference unexpanded= + TreeReference unexpanded = XPathReference.getPathExpr("instance('casedb')/casedb/case[@case_type='unit_test_child_child'][@status='open']").getReference(); @@ -167,13 +176,44 @@ public void testModelQueryTransformFallback() throws XPathSyntaxException { //TODO: Set up a trace reporter which tracks events specifically, and test that this //full loop doesn't do model loads of any of the "Irrelevant" type cases XPathExpression name = XPathParseTool.parseXPath("string(instance('casedb')/casedb/case[@case_id = instance('casedb')/casedb/case[@case_id=current()/index/parent]/index/parent]/case_name)"); - for(TreeReference currentRef : references) { + for (TreeReference currentRef : references) { EvaluationContext subEc = new EvaluationContext(ec, currentRef); FunctionUtils.toString(name.eval(subEc)); } - } + @Test + public void testParentIndexTransformOptimization() throws XPathSyntaxException { + TestUtils.processResourceTransaction("/inputs/case_parent_child_index_bulk.xml"); + EvaluationContext ec = TestUtils.getEvaluationContextWithoutSession(); + + TreeReference unexpanded = + XPathReference.getPathExpr("instance('casedb')/casedb/case[@case_type='unit_test_parent'][@status='open']").getReference(); + + Collection references = ec.expandReference(unexpanded); + + QueryContext qc = ec.getCurrentQueryContext().checkForDerivativeContextAndReturn(references.size()); + qc.setHackyOriginalContextBody(new CurrentModelQuerySet(references)); + ec.setQueryContext(qc); + + AccumulatingReporter traceReporter = new AccumulatingReporter(); + ec.setDebugModeOn(traceReporter); + + //TODO: Set up a trace reporter which tracks events specifically, and test that this + //full loop doesn't do model loads of any of the "Irrelevant" type cases + XPathExpression name = XPathParseTool.parseXPath("count(instance('casedb')/casedb/case[index/parent = current()/@case_id]/case_name)"); + for (TreeReference currentRef : references) { + EvaluationContext subEc = new EvaluationContext(ec, currentRef); + assertEquals(2.0, FunctionUtils.toInt(name.eval(subEc))); + + } + String trace = InstrumentationUtils.collectAndClearTraces( + traceReporter, "Child Lookups", TraceSerialization.TraceInfoType.FULL_PROFILE); + + assertEquals("Query performed incorrect single record reads", -1, trace.indexOf("Model [casedb]: Singular Load")); + assertTrue("Query should trigger a reverse index transform", trace.indexOf("reverse index") != -1); + System.out.println(trace); + } public static void evaluate(String xpath, String expectedValue, EvaluationContext ec) { XPathExpression expr;