diff --git a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt index 4fcaa9e5b0e..61a2b415286 100644 --- a/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt +++ b/collect_app/src/main/java/org/odk/collect/android/database/entities/DatabaseEntitiesRepository.kt @@ -222,6 +222,20 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep } } + override fun getByIdNot(list: String, id: String): List { + if (!listExists(list)) { + return emptyList() + } + + return queryNotEqualWithAttachedRowId( + list, + selectionColumn = EntitiesTable.COLUMN_ID, + selectionArg = id + ).foldAndClose { + mapCursorRowToEntity(it, it.getInt(ROW_ID)) + } + } + override fun getByLabel(list: String, label: String?): List { if (!listExists(list)) { return emptyList() @@ -331,6 +345,36 @@ class DatabaseEntitiesRepository(context: Context, dbPath: String) : EntitiesRep } } + private fun queryNotEqualWithAttachedRowId( + list: String, + selectionColumn: String, + selectionArg: String? + ): Cursor { + return databaseConnection.withConnection { + if (selectionArg == null) { + readableDatabase.rawQuery( + """ + SELECT *, i.$ROW_ID + FROM "$list" e, "${getRowIdTableName(list)}" i + WHERE e._id = i._id AND $selectionColumn IS NOT NULL + ORDER BY i.$ROW_ID + """.trimIndent(), + null + ) + } else { + readableDatabase.rawQuery( + """ + SELECT *, i.$ROW_ID + FROM "$list" e, "${getRowIdTableName(list)}" i + WHERE e._id = i._id AND $selectionColumn != ? + ORDER BY i.$ROW_ID + """.trimIndent(), + arrayOf(selectionArg) + ) + } + } + } + /** * Dropping and recreating this table on every change allows to maintain a sequential * "positions" for each entity that can be used as [Entity.Saved.index]. This method appears diff --git a/entities/src/main/java/org/odk/collect/entities/javarosa/filter/LocalEntitiesFilterStrategy.kt b/entities/src/main/java/org/odk/collect/entities/javarosa/filter/LocalEntitiesFilterStrategy.kt index c4f6fa1088d..831057e8cb4 100644 --- a/entities/src/main/java/org/odk/collect/entities/javarosa/filter/LocalEntitiesFilterStrategy.kt +++ b/entities/src/main/java/org/odk/collect/entities/javarosa/filter/LocalEntitiesFilterStrategy.kt @@ -39,23 +39,31 @@ class LocalEntitiesFilterStrategy(entitiesRepository: EntitiesRepository) : val candidate = CompareToNodeExpression.parse(predicate) return when (val original = candidate?.original) { is XPathEqExpr -> { - if (original.isEqual) { - val child = candidate.nodeSide.steps[0].name.name - val value = candidate.evalContextSide(sourceInstance, evaluationContext) + val child = candidate.nodeSide.steps[0].name.name + val value = candidate.evalContextSide(sourceInstance, evaluationContext) - val results = instanceAdapter.queryEq( + val results = if (original.isEqual) { + instanceAdapter.queryEq( sourceInstance.instanceId, child, value as String ) + } else { + instanceAdapter.queryNotEq( + sourceInstance.instanceId, + child, + value as String + ) + } + if (results == null) { + next.get() + } else { sourceInstance.replacePartialElements(results) results.map { it.parent = sourceInstance.root it.ref } - } else { - next.get() } } diff --git a/entities/src/main/java/org/odk/collect/entities/javarosa/intance/LocalEntitiesInstanceAdapter.kt b/entities/src/main/java/org/odk/collect/entities/javarosa/intance/LocalEntitiesInstanceAdapter.kt index 6783ed6faa6..10c1bbf5664 100644 --- a/entities/src/main/java/org/odk/collect/entities/javarosa/intance/LocalEntitiesInstanceAdapter.kt +++ b/entities/src/main/java/org/odk/collect/entities/javarosa/intance/LocalEntitiesInstanceAdapter.kt @@ -84,6 +84,19 @@ class LocalEntitiesInstanceAdapter(private val entitiesRepository: EntitiesRepos } } + fun queryNotEq(instanceId: String, child: String, value: String): List? { + return when (child) { + EntityItemElement.ID -> { + entitiesRepository.getByIdNot( + instanceId, + value + ).map { convertToElement(it) } + } + + else -> null + } + } + private fun filterAndConvertEntities( list: String, filter: (Entity.Saved) -> Boolean diff --git a/entities/src/main/java/org/odk/collect/entities/storage/EntitiesRepository.kt b/entities/src/main/java/org/odk/collect/entities/storage/EntitiesRepository.kt index 558b3502f5a..5967413002c 100644 --- a/entities/src/main/java/org/odk/collect/entities/storage/EntitiesRepository.kt +++ b/entities/src/main/java/org/odk/collect/entities/storage/EntitiesRepository.kt @@ -9,6 +9,7 @@ interface EntitiesRepository { fun addList(list: String) fun delete(id: String) fun getById(list: String, id: String): Entity.Saved? + fun getByIdNot(list: String, id: String): List fun getByLabel(list: String, label: String?): List fun getAllByProperty(list: String, property: String, value: String): List fun getByIndex(list: String, index: Int): Entity.Saved? diff --git a/entities/src/main/java/org/odk/collect/entities/storage/InMemEntitiesRepository.kt b/entities/src/main/java/org/odk/collect/entities/storage/InMemEntitiesRepository.kt index 51280906af6..a42f5fc4396 100644 --- a/entities/src/main/java/org/odk/collect/entities/storage/InMemEntitiesRepository.kt +++ b/entities/src/main/java/org/odk/collect/entities/storage/InMemEntitiesRepository.kt @@ -50,6 +50,10 @@ class InMemEntitiesRepository : EntitiesRepository { return getEntities(list).firstOrNull { it.id == id } } + override fun getByIdNot(list: String, id: String): List { + return getEntities(list).filter { it.id != id } + } + override fun getByLabel(list: String, label: String?): List { return getEntities(list).filter { it.label == label } } diff --git a/entities/src/test/java/org/odk/collect/entities/LocalEntityUseCasesTest.kt b/entities/src/test/java/org/odk/collect/entities/LocalEntityUseCasesTest.kt index e537dd10b65..e3cee5eef5f 100644 --- a/entities/src/test/java/org/odk/collect/entities/LocalEntityUseCasesTest.kt +++ b/entities/src/test/java/org/odk/collect/entities/LocalEntityUseCasesTest.kt @@ -546,6 +546,11 @@ private class MeasurableEntitiesRepository(private val wrapped: EntitiesReposito return wrapped.getById(list, id) } + override fun getByIdNot(list: String, id: String): List { + accesses += 1 + return wrapped.getByIdNot(list, id) + } + override fun getByLabel(list: String, label: String?): List { accesses += 1 return wrapped.getByLabel(list, label)