Skip to content

Commit

Permalink
Add support for self (left / right / inner / cross) join (#292)
Browse files Browse the repository at this point in the history
* Added crossSelfJoin, innerSelfJoin, leftOuterSelfJoin, rightOuterSelfJoin + Tests + Comments + Readme

* Changes as discussed in #292
  • Loading branch information
Spike2050 authored and lukaseder committed Mar 6, 2017
1 parent a983c42 commit 50469cf
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Seq.of(1, 2, 3, 4).containsAny(2, 5);
// (tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B"))
Seq.of(1, 2).crossJoin(Seq.of("A", "B"));

// (tuple(1, 1), tuple(1, 2), tuple(2, 1), tuple(2, 2))
Seq.of(1, 2).crossSelfJoin()

// (1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, ...)
Seq.of(1, 2, 3).cycle();

Expand All @@ -62,6 +65,9 @@ Seq.of(1, 2, 3, 4).grouped(i -> i % 2);
// (tuple(1, 1), tuple(2, 2))
Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), (a, b) -> a == b);

// (tuple(1, 2), tuple(2, 1))
Seq.of(1, 2).innerSelfJoin((t, u) -> t != u)

// (1, 0, 2, 0, 3, 0, 4)
Seq.of(1, 2, 3, 4).intersperse(0);

Expand All @@ -77,6 +83,9 @@ Seq.of(1, 2, 3).join("|", "^", "$");
// (tuple(1, 1), tuple(2, 2), tuple(4, null))
Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b);

// (tuple(tuple(1, 0), NULL), tuple(tuple(2, 1), tuple(1, 0)))
Seq.of(tuple(1, 0), tuple(2, 1)).leftOuterSelfJoin((t, u) -> t.v2 == u.v1)

// (1, 2)
Seq.of(1, 2, 3, 4, 5).limitWhile(i -> i < 3);

Expand All @@ -89,6 +98,9 @@ Seq.of(new Object(), 1, "B", 2L).ofType(Number.class);
// (tuple(1, 1), tuple(2, 2), tuple(null, 3))
Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b);

// (tuple(NULL, tuple(1, 0)), tuple(tuple(1, 0), tuple(2, 1)))
Seq.of(tuple(1, 0), tuple(2, 1)).rightOuterSelfJoin((t, u) -> t.v2 == u.v1)

// tuple((1, 3), (2, 4))
Seq.of(1, 2, 3, 4).partition(i -> i % 2 != 0);

Expand Down
61 changes: 61 additions & 0 deletions src/main/java/org/jooq/lambda/Seq.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,22 @@ default <U> Seq<Tuple2<T, U>> crossJoin(Seq<? extends U> other) {
return Seq.crossJoin(this, other);
}

/**
* Cross join stream with itself into one.
* <p>
* <code><pre>
* // (tuple(1, 1), tuple(1, 2), tuple(2, 1), tuple(2, 2))
* Seq.of(1, 2).crossSelfJoin()
* </pre></code>
*/
default Seq<Tuple2<T, T>> crossSelfJoin() {

// This algorithm isn't lazy and has substantial complexity for large argument streams!
List<? extends T> list = toList();

return Seq.crossJoin(seq(list), seq(list));
}

/**
* Inner join 2 streams into one.
* <p>
Expand Down Expand Up @@ -245,6 +261,22 @@ default <U> Seq<Tuple2<T, U>> innerJoin(Seq<? extends U> other, BiPredicate<? su
.onClose(other::close);
}

/**
* Inner join stream with itself.
* <p>
* <code><pre>
* // (tuple(1, 1), tuple(2, 2))
* Seq.of(1, 2).innerSelfJoin((t, u) -> Objects.equals(t, u))
* </pre></code>
*/
default Seq<Tuple2<T, T>> innerSelfJoin(BiPredicate<? super T, ? super T> predicate) {

// This algorithm isn't lazy and has substantial complexity for large argument streams!
List<? extends T> list = toList();

return seq(list).innerJoin(seq(list), predicate);
}

/**
* Left outer join 2 streams into one.
* <p>
Expand Down Expand Up @@ -289,6 +321,22 @@ default <U> Seq<Tuple2<T, U>> leftOuterJoin(Seq<? extends U> other, BiPredicate<
.onClose(other::close);
}

/**
* Left outer join one streams into itself.
* <p>
* <code><pre>
* // (tuple(tuple(1, 0), NULL), tuple(tuple(2, 1), tuple(1, 0)))
* Seq.of(new Tuple2<Integer, Integer>(1, 0), new Tuple2<Integer, Integer>(2, 1)).leftOuterSelfJoin((t, u) -> Objects.equals(t.v2, u.v1))
* </pre></code>
*/
default Seq<Tuple2<T, T>> leftOuterSelfJoin(BiPredicate<? super T, ? super T> predicate) {

// This algorithm isn't lazy and has substantial complexity for large argument streams!
List<? extends T> list = toList();

return seq(list).leftOuterJoin(seq(list), predicate);
}

/**
* Right outer join 2 streams into one.
* <p>
Expand Down Expand Up @@ -328,6 +376,19 @@ default <U> Seq<Tuple2<T, U>> rightOuterJoin(Seq<? extends U> other, BiPredicate
.onClose(other::close);
}

/**
* Right outer join stream into itself.
* <p>
* <code><pre>
* // (tuple(NULL, tuple(1, 0)), tuple(tuple(1, 0), tuple(2, 1)))
* Seq.of(new Tuple2<Integer, Integer>(1, 0), new Tuple2<Integer, Integer>(2, 1)).rightOuterSelfJoin((t, u) -> Objects.equals(t.v2, u.v1))
* </pre></code>
*/
default Seq<Tuple2<T, T>> rightOuterSelfJoin(BiPredicate<? super T, ? super T> predicate) {
return leftOuterSelfJoin((u, t) -> predicate.test(t, u))
.map(t -> tuple(t.v2, t.v1));
}

/**
* Produce this stream, or an alternative stream from the
* <code>value</code>, in case this stream is empty.
Expand Down
76 changes: 76 additions & 0 deletions src/test/java/org/jooq/lambda/SeqTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,39 @@ public void testCrossJoin() {
).toList());
}

@Test
public void testCrossSelfJoin() {

// {A} x {B}
// ---------------------------------------------------------------------
assertEquals(asList(),
Seq.of().crossSelfJoin().toList());

assertEquals(asList(
tuple(1, 1)),
Seq.of(1).crossSelfJoin().toList());

assertEquals(asList(
tuple(1, 1),
tuple(1, 2),
tuple(2, 1),
tuple(2, 2)),
Seq.of(1, 2).crossSelfJoin().toList());

assertEquals(asList(
tuple(1, 1),
tuple(1, 2),
tuple(1, 3),
tuple(2, 1),
tuple(2, 2),
tuple(2, 3),
tuple(3, 1),
tuple(3, 2),
tuple(3, 3)),
Seq.of(1, 2, 3).crossSelfJoin().toList());

}

@Test
public void testInnerJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;
Expand Down Expand Up @@ -828,6 +861,23 @@ public void testInnerJoin() {
Seq.<Object>of(1).innerJoin(Seq.of(1, 2), TRUE).toList());
}

@Test
public void testInnerSelfJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;

assertEquals(asList(),
Seq.of().innerSelfJoin(TRUE).toList());

assertEquals(asList(
tuple(1, 1),
tuple(2, 2)),
Seq.of(1, 2).innerSelfJoin((t, u) -> t == u).toList());

assertEquals(asList(
tuple(1, 2)),
Seq.of(1, 2).innerSelfJoin((t, u) -> t * 2 == u).toList());
}

@Test
public void testLeftOuterJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;
Expand Down Expand Up @@ -860,6 +910,19 @@ public void testLeftOuterJoin() {
Seq.<Object>of(1).leftOuterJoin(Seq.of(1, 2), TRUE).toList());
}

@Test
public void testLeftOuterSelfJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;

assertEquals(asList(),
Seq.of().leftOuterSelfJoin(TRUE).toList());

assertEquals(asList(
tuple(tuple(1, 0), (Tuple2<Integer, Integer>) null),
tuple(tuple(2, 1), tuple(1,0))),
Seq.of(tuple(1, 0), tuple(2, 1)).leftOuterSelfJoin((t, u) -> t.v2 == u.v1).toList());
}

@Test
public void testRightOuterJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;
Expand Down Expand Up @@ -896,6 +959,19 @@ public void testRightOuterJoin() {
Seq.<Object>of(1).rightOuterJoin(Seq.of(1, 2), TRUE).toList());
}

@Test
public void testRightOuterSelfJoin() {
BiPredicate<Object, Object> TRUE = (t, u) -> true;

assertEquals(asList(),
Seq.of().rightOuterSelfJoin(TRUE).toList());

assertEquals(asList(
tuple((Tuple2<Integer, Integer>) null, tuple(1, 0)),
tuple(tuple(1,0), tuple(2, 1))),
Seq.of(tuple(1, 0), tuple(2, 1)).rightOuterSelfJoin((t, u) -> t.v1 == u.v2).toList());
}

@Test
public void testOnEmpty() throws X {
assertEquals(asList(1), Seq.of().onEmpty(1).toList());
Expand Down

0 comments on commit 50469cf

Please sign in to comment.