From 347f6d9959b963637b7602899dce9f20c88a8f7d Mon Sep 17 00:00:00 2001 From: Leonid Stryuk Date: Thu, 16 Jan 2025 14:19:44 +0100 Subject: [PATCH 1/2] XD-1189 implemented inplace operations --- .../orientdb/OStoreTransactionImpl.kt | 6 +-- .../orientdb/iterate/OEntityIterableBase.kt | 4 +- .../iterate/link/OMultipleEntitiesIterable.kt | 53 ++++++++++++++++++- .../iterate/link/OSingleEntityIterable.kt | 30 ----------- .../orientdb/query/OQueryFunctions.kt | 8 --- 5 files changed, 57 insertions(+), 44 deletions(-) delete mode 100644 entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OSingleEntityIterable.kt diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt index a980a7b1e..e88c8a877 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/OStoreTransactionImpl.kt @@ -70,7 +70,7 @@ class OStoreTransactionImpl( requireActiveTransaction() try { return session.load(id.asOId()) - } catch (e: RecordNotFoundException) { + } catch (_: RecordNotFoundException) { throw EntityRemovedInDatabaseException(id.getTypeName(), id) } @@ -259,7 +259,7 @@ class OStoreTransactionImpl( try { val vertex: Vertex = session.load(oId.asOId()) return OVertexEntity(vertex, store) - } catch (e: RecordNotFoundException) { + } catch (_: RecordNotFoundException) { throw EntityRemovedInDatabaseException(oId.getTypeName(), id) } } @@ -276,7 +276,7 @@ class OStoreTransactionImpl( override fun getSingletonIterable(entity: Entity): EntityIterable { requireActiveTransaction() - return OSingleEntityIterable(this, entity) + return OMultipleEntitiesIterable(this, listOf(entity)) } override fun find( diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/OEntityIterableBase.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/OEntityIterableBase.kt index 1ee673253..96097cb83 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/OEntityIterableBase.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/OEntityIterableBase.kt @@ -25,7 +25,7 @@ import jetbrains.exodus.entitystore.orientdb.iterate.binop.OMinusEntityIterable import jetbrains.exodus.entitystore.orientdb.iterate.binop.OUnionEntityIterable import jetbrains.exodus.entitystore.orientdb.iterate.link.OLinkIterableToEntityIterableFiltered import jetbrains.exodus.entitystore.orientdb.iterate.link.OLinkSelectEntityIterable -import jetbrains.exodus.entitystore.orientdb.iterate.link.OSingleEntityIterable +import jetbrains.exodus.entitystore.orientdb.iterate.link.OMultipleEntitiesIterable import jetbrains.exodus.entitystore.orientdb.query.* import jetbrains.exodus.entitystore.util.unsupported @@ -224,7 +224,7 @@ abstract class OEntityIterableBase(tx: OStoreTransaction) : OEntityIterable { override fun contains(entity: Entity): Boolean { val currentTx = oStore.requireActiveTransaction() - return OIntersectionEntityIterable(currentTx, this, OSingleEntityIterable(currentTx, entity)).iterator().hasNext() + return OIntersectionEntityIterable(currentTx, this, OMultipleEntitiesIterable(currentTx, listOf(entity))).iterator().hasNext() } override fun unwrap() = this diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OMultipleEntitiesIterable.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OMultipleEntitiesIterable.kt index 42135c6d9..8c2168a0f 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OMultipleEntitiesIterable.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OMultipleEntitiesIterable.kt @@ -49,10 +49,61 @@ class OMultipleEntitiesIterable(tx: OStoreTransaction, val entities: List entities.size){ + EMPTY + } else { + OMultipleEntitiesIterable(transaction as OStoreTransaction, entities.drop(number)) + } } override fun take(number: Int): EntityIterable { return OMultipleEntitiesIterable(transaction as OStoreTransaction, entities.take(number) ) } + + override fun union(right: EntityIterable): EntityIterable { + return if (right is OMultipleEntitiesIterable) { + OMultipleEntitiesIterable(transaction as OStoreTransaction, entities.union(right.entities).toList()) + } else super.union(right) + } + + override fun intersect(right: EntityIterable): EntityIterable { + return if (right is OMultipleEntitiesIterable) { + val otherEntitiesAsSet = right.entities.toSet() + val intersect = entities.filter { otherEntitiesAsSet.contains(it) } + if (intersect.isEmpty()){ + EMPTY + } else { + OMultipleEntitiesIterable(transaction as OStoreTransaction, intersect) + } + } else super.intersect(right) + } + + override fun intersectSavingOrder(right: EntityIterable): EntityIterable { + return if (right is OMultipleEntitiesIterable) { + val otherEntitiesAsSet = right.entities.toSet() + val intersect = entities.filter { otherEntitiesAsSet.contains(it) } + if (intersect.isEmpty()){ + EMPTY + } else { + OMultipleEntitiesIterable(transaction as OStoreTransaction, intersect) + } + } else super.intersectSavingOrder(right) + } + + override fun concat(right: EntityIterable): EntityIterable { + return if (right is OMultipleEntitiesIterable) { + OMultipleEntitiesIterable(transaction as OStoreTransaction, entities + right.entities) + } else super.concat(right) + } + + override fun minus(right: EntityIterable): EntityIterable { + return if (right is OMultipleEntitiesIterable) { + val minus = entities.toSet().minus(right.entities) + if (minus.isEmpty()){ + EMPTY + } else { + OMultipleEntitiesIterable(transaction as OStoreTransaction, entities + right.entities) + } + } else super.minus(right) + } } diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OSingleEntityIterable.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OSingleEntityIterable.kt deleted file mode 100644 index 9a047c077..000000000 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/iterate/link/OSingleEntityIterable.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright ${inceptionYear} - ${year} ${owner} - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jetbrains.exodus.entitystore.orientdb.iterate.link - -import jetbrains.exodus.entitystore.Entity -import jetbrains.exodus.entitystore.orientdb.OEntityId -import jetbrains.exodus.entitystore.orientdb.OStoreTransaction -import jetbrains.exodus.entitystore.orientdb.iterate.OEntityIterableBase -import jetbrains.exodus.entitystore.orientdb.query.ORecordIdSelect -import jetbrains.exodus.entitystore.orientdb.query.OSelect - -open class OSingleEntityIterable(tx: OStoreTransaction, val entity: Entity) : OEntityIterableBase(tx) { - override fun query(): OSelect { - val recordId = (entity.id as OEntityId).asOId() - return ORecordIdSelect(listOf(recordId)) - } -} diff --git a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/query/OQueryFunctions.kt b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/query/OQueryFunctions.kt index 5cdd8ae4c..4957c60ac 100644 --- a/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/query/OQueryFunctions.kt +++ b/entity-store/src/main/kotlin/jetbrains/exodus/entitystore/orientdb/query/OQueryFunctions.kt @@ -69,14 +69,6 @@ object OQueryFunctions { fun difference(left: OSelect, right: OSelect): OSelect { return when { - left is ORecordIdSelect && right is ORecordIdSelect -> { - ensureLimitIsNotUsed(left, right) - ensureSkipIsNotUsed(left, right) - - val newOrder = left.order.merge(right.order) - val ids = left.recordIds - right.recordIds.toSet() - ORecordIdSelect(ids, newOrder) - } left is OClassSelect && right is OClassSelect && isSameClassName(left, right) -> { ensureInvariants(left, right) From e0a58e26e255bb9d878642e2783ef7e974f3c6ee Mon Sep 17 00:00:00 2001 From: Leonid Stryuk Date: Thu, 16 Jan 2025 14:58:02 +0100 Subject: [PATCH 2/2] XD-1189 added to parametrized test --- .../jetbrains/exodus/query/SortEngine.kt | 3 +- .../exodus/query/OQueryEngineTest.kt | 40 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/query/src/main/kotlin/jetbrains/exodus/query/SortEngine.kt b/query/src/main/kotlin/jetbrains/exodus/query/SortEngine.kt index 749e6cbcd..0844fd43d 100644 --- a/query/src/main/kotlin/jetbrains/exodus/query/SortEngine.kt +++ b/query/src/main/kotlin/jetbrains/exodus/query/SortEngine.kt @@ -21,6 +21,7 @@ import jetbrains.exodus.entitystore.EntityIterable import jetbrains.exodus.entitystore.orientdb.OEntityIterable import jetbrains.exodus.entitystore.orientdb.OVertexEntity import jetbrains.exodus.entitystore.orientdb.iterate.OEntityIterableBase +import jetbrains.exodus.entitystore.orientdb.iterate.link.OMultipleEntitiesIterable import jetbrains.exodus.query.metadata.ModelMetaData open class SortEngine { @@ -68,7 +69,7 @@ open class SortEngine { source: Iterable, asc: Boolean ): Iterable { - if (source is OEntityIterable) { + if (source is OEntityIterable && source !is OMultipleEntitiesIterable) { val txn = queryEngine.persistentStore.andCheckCurrentTransaction return txn.sort(entityType, "${OVertexEntity.edgeClassName(linkName)}.$propName", source.unwrap(), asc) } else { diff --git a/query/src/test/kotlin/jetbrains/exodus/query/OQueryEngineTest.kt b/query/src/test/kotlin/jetbrains/exodus/query/OQueryEngineTest.kt index 0afbd65f3..b18c3f386 100644 --- a/query/src/test/kotlin/jetbrains/exodus/query/OQueryEngineTest.kt +++ b/query/src/test/kotlin/jetbrains/exodus/query/OQueryEngineTest.kt @@ -22,6 +22,7 @@ import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.EntityIterable import jetbrains.exodus.entitystore.orientdb.OStoreTransaction import jetbrains.exodus.entitystore.orientdb.iterate.OEntityIterableBase +import jetbrains.exodus.entitystore.orientdb.iterate.link.OMultipleEntitiesIterable import jetbrains.exodus.entitystore.orientdb.testutil.* import jetbrains.exodus.query.metadata.EntityMetaData import jetbrains.exodus.query.metadata.ModelMetaData @@ -53,7 +54,13 @@ class OQueryEngineTest( it.id.typeId >= 0 } InMemoryEntityIterable(filteringSequence.asIterable(), currentTx, engine) - }, "InMemory") + }, "InMemory"), + arrayOf({ engine: QueryEngine, currentTx: OStoreTransaction -> + val filteringSequence = engine.instantiateGetAll(Issues.CLASS).asSequence().filter { + it.id.typeId >= 0 + } + OMultipleEntitiesIterable(currentTx, filteringSequence.toList()) + }, "MultipleEntitiesIterable") ) } } @@ -644,6 +651,37 @@ class OQueryEngineTest( } } + @Test + fun `should query by property sorted`() { + // Given + val test = givenTestCase() + + val metadata = givenModelMetadata().withEntityMetaData(Issues.CLASS) + val engine = givenOQueryEngine(metadata) + + // When + withStoreTx { tx -> + val sortByPropertyAsc = SortByProperty( + null, // child node + "name", // link property name + true // ascending + ) + val issuesAsc = engine.query(iterableGetter(engine, tx), Issues.CLASS, sortByPropertyAsc) + + val sortByLinkPropertyDesc = SortByProperty( + null, // child node + "name", // link property name + false // descending + ) + val issuesDesc = engine.query(iterableGetter(engine, tx), Issues.CLASS, sortByLinkPropertyDesc) + + // Then + // As sorted by project name + assertOrderedNamesExactly(issuesDesc, "issue3", "issue2", "issue1") + assertOrderedNamesExactly(issuesAsc, "issue1", "issue2", "issue3") + } + } + private fun assertOrderedNamesExactly(result: Iterable, vararg names: String) { assertThat(result.map { it.getProperty("name") }).containsExactly(*names).inOrder() }