Skip to content

Commit

Permalink
feat: Query class, And & Or
Browse files Browse the repository at this point in the history
test: CompositeCondition
  • Loading branch information
sepgh committed Nov 3, 2024
1 parent 7d89112 commit 3902a0a
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
public class CompositeConditionIterator<T extends Comparable<T>> implements Iterator<T> {
private final CompositeCondition.CompositeOperator operator;
private final PriorityQueue<PeekableIterator<T>> iterators;
private final boolean ascending;
private T nextItem;

CompositeConditionIterator(CompositeCondition.CompositeOperator operator, List<Iterator<T>> iterators, Order order) {
this.operator = operator;
this.ascending = order == Order.ASC;
this.iterators = new PriorityQueue<>((a, b) -> order.equals(Order.ASC) ?
a.peek().compareTo(b.peek()) : b.peek().compareTo(a.peek()));

Expand All @@ -33,38 +35,79 @@ private void advance() {

private void advanceForAnd() {
while (!iterators.isEmpty()) {
T candidate = iterators.peek().peek();
boolean allMatch = true;
// Find the maximum value among the current head of each iterator
T maxCandidate = null;
for (PeekableIterator<T> it : iterators) {
if (it.hasNext()) {
T peeked = it.peek();
if (maxCandidate == null || (ascending ? peeked.compareTo(maxCandidate) > 0 : peeked.compareTo(maxCandidate) < 0)) {
maxCandidate = peeked;
}
}
}

if (maxCandidate == null) { // No more items in any iterator
nextItem = null;
return;
}

// Check if all iterators are at the maxCandidate
boolean allMatch = true;
for (PeekableIterator<T> it : iterators) {
while (it.hasNext() && !it.peek().equals(candidate)) {
it.next();
while (it.hasNext() && (ascending ? it.peek().compareTo(maxCandidate) < 0 : it.peek().compareTo(maxCandidate) > 0)) {
it.next(); // Advance to match the maxCandidate
}
if (!it.hasNext() || !it.peek().equals(candidate)) {
if (!it.hasNext() || !it.peek().equals(maxCandidate)) {
allMatch = false;
break;
}
}

if (allMatch) {
nextItem = candidate;
iterators.forEach(PeekableIterator::next);
nextItem = maxCandidate;
// Move all iterators to the next element
for (PeekableIterator<T> it : iterators) {
it.next();
}
return;
}
}
nextItem = null;
nextItem = null; // No more matches
}

private void advanceForOr() {
if (!iterators.isEmpty()) {
PeekableIterator<T> smallest = iterators.poll();
nextItem = smallest.next();
if (smallest.hasNext()) {
iterators.add(smallest);
if (iterators.isEmpty()) {
nextItem = null;
return;
}

// Find the smallest (or largest if descending) item among the iterators
T candidate = null;
for (PeekableIterator<T> it : iterators) {
if (it.hasNext()) {
T peeked = it.peek();
if (candidate == null || (ascending ? peeked.compareTo(candidate) < 0 : peeked.compareTo(candidate) > 0)) {
candidate = peeked;
}
}
} else {
}

// If there's no candidate (all iterators exhausted), end
if (candidate == null) {
nextItem = null;
return;
}

// Set nextItem to candidate and advance all iterators with this value
nextItem = candidate;
for (PeekableIterator<T> it : iterators) {
if (it.hasNext() && it.peek().equals(candidate)) {
it.next();
}
}

// Remove exhausted iterators
iterators.removeIf(it -> !it.hasNext());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.github.sepgh.testudo.storage.pool.FileHandler;
import com.github.sepgh.testudo.storage.pool.UnlimitedFileHandlerPool;
import com.google.common.primitives.UnsignedInteger;
import lombok.SneakyThrows;
import org.junit.jupiter.api.*;

import java.io.IOException;
Expand All @@ -31,7 +32,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;

public class SimpleQueryTestCase {
public class QueryTestCase {
private DatabaseStorageManager databaseStorageManager;
private Path dbPath;
private EngineConfig engineConfig;
Expand Down Expand Up @@ -309,4 +310,220 @@ public void simpleCondition_LowCardinality() throws IOException, ExecutionExcept
Assertions.assertEquals(UnsignedInteger.valueOf(1), executedResults.getFirst());
Assertions.assertEquals(UnsignedInteger.valueOf(2), executedResults.get(1));
}

@SneakyThrows
@Test
@Timeout(2)
public void compositeQuery_And() {
IndexStorageManagerFactory indexStorageManagerFactory = new DefaultIndexStorageManagerFactory(this.engineConfig, new JsonIndexHeaderManager.Factory());
CollectionIndexProviderFactory collectionIndexProviderFactory = new DefaultCollectionIndexProviderFactory(engineConfig, indexStorageManagerFactory, this.databaseStorageManager);

Scheme scheme = Scheme.builder()
.dbName("test")
.version(1)
.collections(
List.of(
Scheme.Collection.builder()
.id(1)
.name("test_collection")
.fields(
List.of(
Scheme.Field.builder()
.id(1)
.index(true)
.indexUnique(true)
.type("int")
.name("pk")
.build(),
Scheme.Field.builder()
.id(2)
.index(true)
.type("int")
.name("age")
.build()
)
)
.build()
)
)
.build();
Scheme.Collection collection = scheme.getCollections().getFirst();
CollectionIndexProvider collectionIndexProvider = collectionIndexProviderFactory.create(collection);


// --- ADD DATA TO THE COLLECTION --- //
UniqueTreeIndexManager<UnsignedInteger, Pointer> clusterIndexManager = (UniqueTreeIndexManager<UnsignedInteger, Pointer>) collectionIndexProvider.getClusterIndexManager();
UniqueQueryableIndex<Integer, UnsignedInteger> pkIndexManager = (UniqueQueryableIndex<Integer, UnsignedInteger>) collectionIndexProvider.getUniqueIndexManager(collection.getFields().getFirst());
DuplicateQueryableIndex<Integer, UnsignedInteger> ageIndexManager = (DuplicateQueryableIndex<Integer, UnsignedInteger>) collectionIndexProvider.getDuplicateIndexManager(collection.getFields().getLast());

byte[] data = new byte[]{
0x00, 0x00, 0x00, 0x01, // PK
0x00, 0x00, 0x00, 0x01 // AGE = 1
};
Pointer pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(1L), pointer);
pkIndexManager.addIndex(1, UnsignedInteger.valueOf(1L));
ageIndexManager.addIndex(1, UnsignedInteger.valueOf(1L));

data = new byte[]{
0x00, 0x00, 0x00, 0x02, // PK
0x00, 0x00, 0x00, 0x01 // AGE = 1
};
pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(2L), pointer);
pkIndexManager.addIndex(2, UnsignedInteger.valueOf(2L));
ageIndexManager.addIndex(1, UnsignedInteger.valueOf(2L));

data = new byte[]{
0x00, 0x00, 0x00, 0x03, // PK
0x00, 0x00, 0x00, 0x03 // AGE = 3
};
pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(3L), pointer);
pkIndexManager.addIndex(3, UnsignedInteger.valueOf(3L));
ageIndexManager.addIndex(3, UnsignedInteger.valueOf(3L));


Query query = new Query();
query.where(
new SimpleCondition<>(
collection,
"pk",
Operation.GT,
1
)
);
query.and(
new SimpleCondition<>(
collection,
"age",
Operation.GTE,
2
)
);
List<UnsignedInteger> queryResults = query.execute(collectionIndexProvider);
Assertions.assertEquals(1, queryResults.size());
Assertions.assertEquals(UnsignedInteger.valueOf(3), queryResults.getFirst());

query = new Query();
query.where(
new SimpleCondition<>(
collection,
"pk",
Operation.GT,
1
)
);
query.and(
new SimpleCondition<>(
collection,
"age",
Operation.GTE,
1
)
);
queryResults = query.execute(collectionIndexProvider);
Assertions.assertEquals(2, queryResults.size());
Assertions.assertEquals(UnsignedInteger.valueOf(2), queryResults.getFirst());
Assertions.assertEquals(UnsignedInteger.valueOf(3), queryResults.getLast());
}


@SneakyThrows
@Test
@Timeout(2)
public void compositeQuery_Or() {
IndexStorageManagerFactory indexStorageManagerFactory = new DefaultIndexStorageManagerFactory(this.engineConfig, new JsonIndexHeaderManager.Factory());
CollectionIndexProviderFactory collectionIndexProviderFactory = new DefaultCollectionIndexProviderFactory(engineConfig, indexStorageManagerFactory, this.databaseStorageManager);

Scheme scheme = Scheme.builder()
.dbName("test")
.version(1)
.collections(
List.of(
Scheme.Collection.builder()
.id(1)
.name("test_collection")
.fields(
List.of(
Scheme.Field.builder()
.id(1)
.index(true)
.indexUnique(true)
.type("int")
.name("pk")
.build(),
Scheme.Field.builder()
.id(2)
.index(true)
.type("int")
.name("age")
.build()
)
)
.build()
)
)
.build();
Scheme.Collection collection = scheme.getCollections().getFirst();
CollectionIndexProvider collectionIndexProvider = collectionIndexProviderFactory.create(collection);


// --- ADD DATA TO THE COLLECTION --- //
UniqueTreeIndexManager<UnsignedInteger, Pointer> clusterIndexManager = (UniqueTreeIndexManager<UnsignedInteger, Pointer>) collectionIndexProvider.getClusterIndexManager();
UniqueQueryableIndex<Integer, UnsignedInteger> pkIndexManager = (UniqueQueryableIndex<Integer, UnsignedInteger>) collectionIndexProvider.getUniqueIndexManager(collection.getFields().getFirst());
DuplicateQueryableIndex<Integer, UnsignedInteger> ageIndexManager = (DuplicateQueryableIndex<Integer, UnsignedInteger>) collectionIndexProvider.getDuplicateIndexManager(collection.getFields().getLast());

byte[] data = new byte[]{
0x00, 0x00, 0x00, 0x01, // PK
0x00, 0x00, 0x00, 0x01 // AGE = 1
};
Pointer pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(1L), pointer);
pkIndexManager.addIndex(1, UnsignedInteger.valueOf(1L));
ageIndexManager.addIndex(1, UnsignedInteger.valueOf(1L));

data = new byte[]{
0x00, 0x00, 0x00, 0x02, // PK
0x00, 0x00, 0x00, 0x01 // AGE = 1
};
pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(2L), pointer);
pkIndexManager.addIndex(2, UnsignedInteger.valueOf(2L));
ageIndexManager.addIndex(1, UnsignedInteger.valueOf(2L));

data = new byte[]{
0x00, 0x00, 0x00, 0x03, // PK
0x00, 0x00, 0x00, 0x03 // AGE = 3
};
pointer = this.databaseStorageManager.store(1, 1, data);
clusterIndexManager.addIndex(UnsignedInteger.valueOf(3L), pointer);
pkIndexManager.addIndex(3, UnsignedInteger.valueOf(3L));
ageIndexManager.addIndex(3, UnsignedInteger.valueOf(3L));


Query query = new Query();
query.where(
new SimpleCondition<>(
collection,
"pk",
Operation.GT,
3
)
);
query.or(
new SimpleCondition<>(
collection,
"age",
Operation.GTE,
1
)
);
List<UnsignedInteger> queryResults = query.execute(collectionIndexProvider);
Assertions.assertEquals(3, queryResults.size());
Assertions.assertEquals(UnsignedInteger.valueOf(1), queryResults.getFirst());
Assertions.assertEquals(UnsignedInteger.valueOf(2), queryResults.get(1));
Assertions.assertEquals(UnsignedInteger.valueOf(3), queryResults.getLast());

}
}

0 comments on commit 3902a0a

Please sign in to comment.