All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.scalar.db.api.ScanBuilder Maven / Gradle / Ivy

Go to download

A universal transaction manager that achieves database-agnostic transactions and distributed transactions that span multiple databases

The newest version!
package com.scalar.db.api;

import static com.google.common.base.Preconditions.checkNotNull;

import com.scalar.db.api.OperationBuilder.All;
import com.scalar.db.api.OperationBuilder.And;
import com.scalar.db.api.OperationBuilder.Buildable;
import com.scalar.db.api.OperationBuilder.ClearBoundaries;
import com.scalar.db.api.OperationBuilder.ClearConditions;
import com.scalar.db.api.OperationBuilder.ClearNamespace;
import com.scalar.db.api.OperationBuilder.ClearOrderings;
import com.scalar.db.api.OperationBuilder.ClearProjections;
import com.scalar.db.api.OperationBuilder.ClusteringKeyFiltering;
import com.scalar.db.api.OperationBuilder.Consistency;
import com.scalar.db.api.OperationBuilder.IndexKey;
import com.scalar.db.api.OperationBuilder.Limit;
import com.scalar.db.api.OperationBuilder.Or;
import com.scalar.db.api.OperationBuilder.Ordering;
import com.scalar.db.api.OperationBuilder.PartitionKey;
import com.scalar.db.api.OperationBuilder.PartitionKeyBuilder;
import com.scalar.db.api.OperationBuilder.Projection;
import com.scalar.db.api.OperationBuilder.TableBuilder;
import com.scalar.db.api.OperationBuilder.WhereAnd;
import com.scalar.db.api.OperationBuilder.WhereOr;
import com.scalar.db.api.Selection.Conjunction;
import com.scalar.db.common.error.CoreError;
import com.scalar.db.io.Key;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

public class ScanBuilder extends SelectionBuilder {

  public static class Namespace
      implements OperationBuilder.Namespace,
          OperationBuilder.Table {

    Namespace() {}

    @Override
    public Table namespace(String namespaceName) {
      checkNotNull(namespaceName);
      return new Table(namespaceName);
    }

    @Override
    public PartitionKeyOrIndexKeyOrAll table(String tableName) {
      checkNotNull(tableName);
      return new PartitionKeyOrIndexKeyOrAll(null, tableName);
    }
  }

  public static class Table extends TableBuilder {

    private Table(String namespaceName) {
      super(namespaceName);
    }

    @Override
    public PartitionKeyOrIndexKeyOrAll table(String tableName) {
      checkNotNull(tableName);
      return new PartitionKeyOrIndexKeyOrAll(namespace, tableName);
    }
  }

  public static class PartitionKeyOrIndexKeyOrAll
      extends PartitionKeyBuilder
      implements IndexKey, All {

    private PartitionKeyOrIndexKeyOrAll(@Nullable String namespace, String table) {
      super(namespace, table);
    }

    @Override
    public BuildableScanWithPartitionKey partitionKey(Key partitionKey) {
      checkNotNull(partitionKey);
      return new BuildableScanWithPartitionKey(namespaceName, tableName, partitionKey);
    }

    @Override
    public BuildableScanWithIndex indexKey(Key indexKey) {
      checkNotNull(indexKey);
      return new BuildableScanWithIndex(namespaceName, tableName, indexKey);
    }

    @Override
    public BuildableScanAll all() {
      return new BuildableScanAll(namespaceName, tableName);
    }
  }

  public static class BuildableScan extends Buildable
      implements ClusteringKeyFiltering,
          Ordering,
          Consistency,
          Projection,
          Limit {
    final List orderings = new ArrayList<>();
    final List projections = new ArrayList<>();
    @Nullable Key startClusteringKey;
    boolean startInclusive;
    @Nullable Key endClusteringKey;
    boolean endInclusive;
    int limit = 0;
    @Nullable com.scalar.db.api.Consistency consistency;

    private BuildableScan(@Nullable String namespace, String table, Key partitionKey) {
      super(namespace, table, partitionKey);
    }

    private BuildableScan(BuildableScan buildable) {
      this(buildable.namespaceName, buildable.tableName, buildable.partitionKey);
      this.orderings.addAll(buildable.orderings);
      this.projections.addAll(buildable.projections);
      this.startClusteringKey = buildable.startClusteringKey;
      this.startInclusive = buildable.startInclusive;
      this.endClusteringKey = buildable.endClusteringKey;
      this.endInclusive = buildable.endInclusive;
      this.limit = buildable.limit;
      this.consistency = buildable.consistency;
    }

    @Override
    public BuildableScan projection(String projection) {
      checkNotNull(projection);
      projections.add(projection);
      return this;
    }

    @Override
    public BuildableScan projections(Collection projections) {
      checkNotNull(projections);
      this.projections.addAll(projections);
      return this;
    }

    @Override
    public BuildableScan projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScan limit(int limit) {
      this.limit = limit;
      return this;
    }

    @Override
    public BuildableScan ordering(Scan.Ordering ordering) {
      checkNotNull(ordering);
      orderings.add(ordering);
      return this;
    }

    @Override
    public BuildableScan orderings(Collection orderings) {
      checkNotNull(orderings);
      this.orderings.addAll(orderings);
      return this;
    }

    @Override
    public BuildableScan orderings(Scan.Ordering... orderings) {
      return orderings(Arrays.asList(orderings));
    }

    @Override
    public BuildableScan start(Key clusteringKey, boolean inclusive) {
      checkNotNull(clusteringKey);
      startClusteringKey = clusteringKey;
      startInclusive = inclusive;
      return this;
    }

    @Override
    public BuildableScan end(Key clusteringKey, boolean inclusive) {
      checkNotNull(clusteringKey);
      endClusteringKey = clusteringKey;
      endInclusive = inclusive;
      return this;
    }

    @Override
    public BuildableScan consistency(com.scalar.db.api.Consistency consistency) {
      checkNotNull(consistency);
      this.consistency = consistency;
      return this;
    }

    @Override
    public Scan build() {
      Scan scan = new Scan(partitionKey);
      scan.forNamespace(namespaceName).forTable(tableName).withLimit(limit);
      orderings.forEach(scan::withOrdering);
      if (startClusteringKey != null) {
        scan.withStart(startClusteringKey, startInclusive);
      }
      if (endClusteringKey != null) {
        scan.withEnd(endClusteringKey, endInclusive);
      }

      if (!projections.isEmpty()) {
        scan.withProjections(projections);
      }

      if (consistency != null) {
        scan.withConsistency(consistency);
      }

      return scan;
    }
  }

  public static class BuildableScanWithPartitionKey extends BuildableScan
      implements OperationBuilder.Where,
          WhereAnd,
          WhereOr {

    private BuildableScanWithPartitionKey(
        @Nullable String namespace, String table, Key partitionKey) {
      super(namespace, table, partitionKey);
    }

    @Override
    public BuildableScanWithPartitionKey start(Key clusteringKey, boolean inclusive) {
      super.start(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey end(Key clusteringKey, boolean inclusive) {
      super.end(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey start(Key clusteringKey) {
      super.start(clusteringKey);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey end(Key clusteringKey) {
      super.end(clusteringKey);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey projection(String projection) {
      super.projection(projection);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey projections(Collection projections) {
      super.projections(projections);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanWithPartitionKey ordering(Scan.Ordering ordering) {
      super.ordering(ordering);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey orderings(Collection orderings) {
      super.orderings(orderings);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey orderings(Scan.Ordering... orderings) {
      return orderings(Arrays.asList(orderings));
    }

    @Override
    public BuildableScanWithPartitionKey limit(int limit) {
      super.limit(limit);
      return this;
    }

    @Override
    public BuildableScanWithPartitionKey consistency(com.scalar.db.api.Consistency consistency) {
      super.consistency(consistency);
      return this;
    }

    @Override
    public BuildableScanWithOngoingWhere where(ConditionalExpression condition) {
      checkNotNull(condition);
      return new BuildableScanWithOngoingWhere(this, condition);
    }

    @Override
    public BuildableScanWithOngoingWhereAnd where(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      return new BuildableScanWithOngoingWhereAnd(this, orConditionSet);
    }

    @Override
    public BuildableScanWithOngoingWhereOr where(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      return new BuildableScanWithOngoingWhereOr(this, andConditionSet);
    }

    @Override
    public BuildableScanWithOngoingWhereAnd whereAnd(Set orConditionSets) {
      checkNotNull(orConditionSets);
      return new BuildableScanWithOngoingWhereAnd(this, orConditionSets);
    }

    @Override
    public BuildableScanWithOngoingWhereOr whereOr(Set andConditionSets) {
      checkNotNull(andConditionSets);
      return new BuildableScanWithOngoingWhereOr(this, andConditionSets);
    }
  }

  public static class BuildableScanWithOngoingWhere extends BuildableScanWithWhere
      implements And, Or {

    private BuildableScanWithOngoingWhere(
        BuildableScan buildable, ConditionalExpression condition) {
      super(buildable, condition);
    }

    @Override
    public BuildableScanWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return new BuildableScanWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return new BuildableScanWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return new BuildableScanWithOngoingWhereOr(this);
    }

    @Override
    public BuildableScanWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return new BuildableScanWithOngoingWhereOr(this);
    }
  }

  public static class BuildableScanWithOngoingWhereAnd extends BuildableScanWithWhere
      implements And {

    private BuildableScanWithOngoingWhereAnd(BuildableScanWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanWithOngoingWhereAnd(
        BuildableScan buildable, OrConditionSet orConditionSet) {
      super(buildable);
      where.and(orConditionSet);
    }

    private BuildableScanWithOngoingWhereAnd(
        BuildableScan buildable, Set orConditionSets) {
      super(buildable);
      where.and(orConditionSets);
    }

    @Override
    public BuildableScanWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return this;
    }

    @Override
    public BuildableScanWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return this;
    }
  }

  public static class BuildableScanWithOngoingWhereOr extends BuildableScanWithWhere
      implements Or {

    private BuildableScanWithOngoingWhereOr(BuildableScanWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanWithOngoingWhereOr(
        BuildableScan buildable, AndConditionSet andConditionSet) {
      super(buildable);
      where.or(andConditionSet);
    }

    private BuildableScanWithOngoingWhereOr(
        BuildableScan buildable, Set andConditionSets) {
      super(buildable);
      where.or(andConditionSets);
    }

    @Override
    public BuildableScanWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return this;
    }

    @Override
    public BuildableScanWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return this;
    }
  }

  public static class BuildableScanWithWhere extends BuildableScan {

    final Where where;

    private BuildableScanWithWhere(BuildableScan buildable) {
      this(buildable, null);
    }

    private BuildableScanWithWhere(
        BuildableScan buildable, @Nullable ConditionalExpression condition) {
      super(buildable);
      this.where = new Where(condition);
    }

    private BuildableScanWithWhere(BuildableScanWithOngoingWhere buildable) {
      super(buildable);
      this.where = buildable.where;
    }

    @Override
    public Scan build() {
      return (Scan) addConjunctionsTo(super.build(), where);
    }
  }

  public static class BuildableScanWithIndex
      implements Consistency,
          Projection,
          OperationBuilder.Where,
          WhereAnd,
          WhereOr,
          Limit {
    @Nullable private final String namespaceName;
    private final String tableName;
    private final Key indexKey;
    private final List projections = new ArrayList<>();
    private int limit = 0;
    @Nullable private com.scalar.db.api.Consistency consistency;

    private BuildableScanWithIndex(@Nullable String namespaceName, String tableName, Key indexKey) {
      this.namespaceName = namespaceName;
      this.tableName = tableName;
      this.indexKey = indexKey;
    }

    @Override
    public BuildableScanWithIndex projection(String projection) {
      checkNotNull(projection);
      projections.add(projection);
      return this;
    }

    @Override
    public BuildableScanWithIndex projections(Collection projections) {
      checkNotNull(projections);
      this.projections.addAll(projections);
      return this;
    }

    @Override
    public BuildableScanWithIndex projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanWithIndex limit(int limit) {
      this.limit = limit;
      return this;
    }

    @Override
    public BuildableScanWithIndex consistency(com.scalar.db.api.Consistency consistency) {
      checkNotNull(consistency);
      this.consistency = consistency;
      return this;
    }

    @Override
    public BuildableScanWithIndexOngoingWhere where(ConditionalExpression condition) {
      checkNotNull(condition);
      return new BuildableScanWithIndexOngoingWhere(this, condition);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd where(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      return new BuildableScanWithIndexOngoingWhereAnd(this, orConditionSet);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr where(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      return new BuildableScanWithIndexOngoingWhereOr(this, andConditionSet);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd whereAnd(Set orConditionSets) {
      checkNotNull(orConditionSets);
      return new BuildableScanWithIndexOngoingWhereAnd(this, orConditionSets);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr whereOr(Set andConditionSets) {
      checkNotNull(andConditionSets);
      return new BuildableScanWithIndexOngoingWhereOr(this, andConditionSets);
    }

    public Scan build() {
      Scan scan = new ScanWithIndex(indexKey);
      scan.forNamespace(namespaceName).forTable(tableName).withLimit(limit);

      if (!projections.isEmpty()) {
        scan.withProjections(projections);
      }

      if (consistency != null) {
        scan.withConsistency(consistency);
      }

      return scan;
    }
  }

  public static class BuildableScanWithIndexOngoingWhere extends BuildableScanWithIndexWhere
      implements And,
          Or {

    private BuildableScanWithIndexOngoingWhere(
        BuildableScanWithIndex buildable, ConditionalExpression condition) {
      super(buildable, condition);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return new BuildableScanWithIndexOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return new BuildableScanWithIndexOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return new BuildableScanWithIndexOngoingWhereOr(this);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return new BuildableScanWithIndexOngoingWhereOr(this);
    }
  }

  public static class BuildableScanWithIndexOngoingWhereAnd extends BuildableScanWithIndexWhere
      implements And {

    private BuildableScanWithIndexOngoingWhereAnd(BuildableScanWithIndexOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanWithIndexOngoingWhereAnd(
        BuildableScanWithIndex buildable, OrConditionSet orConditionSet) {
      super(buildable);
      where.and(orConditionSet);
    }

    private BuildableScanWithIndexOngoingWhereAnd(
        BuildableScanWithIndex buildable, Set orConditionSets) {
      super(buildable);
      where.and(orConditionSets);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return this;
    }

    @Override
    public BuildableScanWithIndexOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return this;
    }
  }

  public static class BuildableScanWithIndexOngoingWhereOr extends BuildableScanWithIndexWhere
      implements Or {

    private BuildableScanWithIndexOngoingWhereOr(BuildableScanWithIndexOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanWithIndexOngoingWhereOr(
        BuildableScanWithIndex buildable, AndConditionSet andConditionSet) {
      super(buildable);
      where.or(andConditionSet);
    }

    private BuildableScanWithIndexOngoingWhereOr(
        BuildableScanWithIndex buildable, Set andConditionSets) {
      super(buildable);
      where.or(andConditionSets);
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return this;
    }

    @Override
    public BuildableScanWithIndexOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return this;
    }
  }

  public static class BuildableScanWithIndexWhere
      implements Consistency,
          Projection,
          Limit {

    BuildableScanWithIndex buildableScanWithIndex;
    final Where where;

    private BuildableScanWithIndexWhere(BuildableScanWithIndex buildable) {
      this(buildable, null);
    }

    private BuildableScanWithIndexWhere(
        BuildableScanWithIndex buildable, @Nullable ConditionalExpression condition) {
      this.buildableScanWithIndex = buildable;
      this.where = new Where(condition);
    }

    private BuildableScanWithIndexWhere(BuildableScanWithIndexOngoingWhere buildable) {
      this.buildableScanWithIndex = buildable.buildableScanWithIndex;
      this.where = buildable.where;
    }

    @Override
    public BuildableScanWithIndexWhere projection(String projection) {
      buildableScanWithIndex = buildableScanWithIndex.projections(projection);
      return this;
    }

    @Override
    public BuildableScanWithIndexWhere projections(Collection projections) {
      buildableScanWithIndex = buildableScanWithIndex.projections(projections);
      return this;
    }

    @Override
    public BuildableScanWithIndexWhere projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanWithIndexWhere limit(int limit) {
      buildableScanWithIndex = buildableScanWithIndex.limit(limit);
      return this;
    }

    @Override
    public BuildableScanWithIndexWhere consistency(com.scalar.db.api.Consistency consistency) {
      buildableScanWithIndex = buildableScanWithIndex.consistency(consistency);
      return this;
    }

    public Scan build() {
      return (Scan) addConjunctionsTo(buildableScanWithIndex.build(), where);
    }
  }

  public static class BuildableScanAll
      implements Ordering,
          Consistency,
          Projection,
          OperationBuilder.Where,
          WhereAnd,
          WhereOr,
          Limit {
    private final String namespaceName;
    private final String tableName;
    private final List orderings = new ArrayList<>();
    private final List projections = new ArrayList<>();
    private int limit = 0;
    @Nullable private com.scalar.db.api.Consistency consistency;

    private BuildableScanAll(String namespaceName, String tableName) {
      this.namespaceName = namespaceName;
      this.tableName = tableName;
    }

    @Override
    public BuildableScanAll projection(String projection) {
      checkNotNull(projection);
      projections.add(projection);
      return this;
    }

    @Override
    public BuildableScanAll projections(Collection projections) {
      checkNotNull(projections);
      this.projections.addAll(projections);
      return this;
    }

    @Override
    public BuildableScanAll projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanAll limit(int limit) {
      this.limit = limit;
      return this;
    }

    @Override
    public BuildableScanAll ordering(Scan.Ordering ordering) {
      checkNotNull(ordering);
      orderings.add(ordering);
      return this;
    }

    @Override
    public BuildableScanAll orderings(Collection orderings) {
      checkNotNull(orderings);
      this.orderings.addAll(orderings);
      return this;
    }

    @Override
    public BuildableScanAll orderings(Scan.Ordering... orderings) {
      return orderings(Arrays.asList(orderings));
    }

    @Override
    public BuildableScanAll consistency(com.scalar.db.api.Consistency consistency) {
      checkNotNull(consistency);
      this.consistency = consistency;
      return this;
    }

    @Override
    public BuildableScanAllWithOngoingWhere where(ConditionalExpression condition) {
      checkNotNull(condition);
      return new BuildableScanAllWithOngoingWhere(this, condition);
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd where(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      return new BuildableScanAllWithOngoingWhereAnd(this, orConditionSet);
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr where(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      return new BuildableScanAllWithOngoingWhereOr(this, andConditionSet);
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd whereAnd(Set orConditionSets) {
      checkNotNull(orConditionSets);
      return new BuildableScanAllWithOngoingWhereAnd(this, orConditionSets);
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr whereOr(Set andConditionSets) {
      checkNotNull(andConditionSets);
      return new BuildableScanAllWithOngoingWhereOr(this, andConditionSets);
    }

    public Scan build() {
      Scan scan = new ScanAll();
      scan.forNamespace(namespaceName).forTable(tableName).withLimit(limit);
      orderings.forEach(scan::withOrdering);

      if (!projections.isEmpty()) {
        scan.withProjections(projections);
      }

      if (consistency != null) {
        scan.withConsistency(consistency);
      }

      return scan;
    }
  }

  public static class BuildableScanAllWithOngoingWhere extends BuildableScanAllWithWhere
      implements And, Or {

    private BuildableScanAllWithOngoingWhere(
        BuildableScanAll buildable, ConditionalExpression condition) {
      super(buildable, condition);
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return new BuildableScanAllWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return new BuildableScanAllWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return new BuildableScanAllWithOngoingWhereOr(this);
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return new BuildableScanAllWithOngoingWhereOr(this);
    }
  }

  public static class BuildableScanAllWithOngoingWhereAnd extends BuildableScanAllWithWhere
      implements And {

    private BuildableScanAllWithOngoingWhereAnd(BuildableScanAllWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanAllWithOngoingWhereAnd(
        BuildableScanAll buildable, OrConditionSet orConditionSet) {
      super(buildable);
      where.and(orConditionSet);
    }

    private BuildableScanAllWithOngoingWhereAnd(
        BuildableScanAll buildable, Set orConditionSets) {
      super(buildable);
      where.and(orConditionSets);
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return this;
    }

    @Override
    public BuildableScanAllWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return this;
    }
  }

  public static class BuildableScanAllWithOngoingWhereOr extends BuildableScanAllWithWhere
      implements Or {

    private BuildableScanAllWithOngoingWhereOr(BuildableScanAllWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanAllWithOngoingWhereOr(
        BuildableScanAll buildable, AndConditionSet andConditionSet) {
      super(buildable);
      where.or(andConditionSet);
    }

    private BuildableScanAllWithOngoingWhereOr(
        BuildableScanAll buildable, Set andConditionSets) {
      super(buildable);
      where.or(andConditionSets);
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return this;
    }

    @Override
    public BuildableScanAllWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return this;
    }
  }

  public static class BuildableScanAllWithWhere
      implements Consistency,
          Projection,
          Ordering,
          Limit {

    final BuildableScanAll buildableScanAll;
    final Where where;

    private BuildableScanAllWithWhere(BuildableScanAll buildable) {
      this(buildable, null);
    }

    private BuildableScanAllWithWhere(
        BuildableScanAll buildable, @Nullable ConditionalExpression condition) {
      this.buildableScanAll = buildable;
      this.where = new Where(condition);
    }

    private BuildableScanAllWithWhere(BuildableScanAllWithOngoingWhere buildable) {
      this.buildableScanAll = buildable.buildableScanAll;
      this.where = buildable.where;
    }

    @Override
    public BuildableScanAllWithWhere projection(String projection) {
      buildableScanAll.projection(projection);
      return this;
    }

    @Override
    public BuildableScanAllWithWhere projections(Collection projections) {
      buildableScanAll.projections(projections);
      return this;
    }

    @Override
    public BuildableScanAllWithWhere projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanAllWithWhere ordering(Scan.Ordering ordering) {
      buildableScanAll.ordering(ordering);
      return this;
    }

    @Override
    public BuildableScanAllWithWhere orderings(Collection orderings) {
      buildableScanAll.orderings(orderings);
      return this;
    }

    @Override
    public BuildableScanAllWithWhere orderings(Scan.Ordering... orderings) {
      return orderings(Arrays.asList(orderings));
    }

    @Override
    public BuildableScanAllWithWhere limit(int limit) {
      buildableScanAll.limit(limit);
      return this;
    }

    @Override
    public BuildableScanAllWithWhere consistency(com.scalar.db.api.Consistency consistency) {
      buildableScanAll.consistency(consistency);
      return this;
    }

    public Scan build() {
      return (Scan) addConjunctionsTo(buildableScanAll.build(), where);
    }
  }

  public static class BuildableScanOrScanAllFromExisting extends BuildableScan
      implements OperationBuilder.Namespace,
          OperationBuilder.Table,
          PartitionKey,
          IndexKey,
          OperationBuilder.Where,
          WhereAnd,
          WhereOr,
          ClearConditions,
          ClearProjections,
          ClearOrderings,
          ClearBoundaries,
          ClearNamespace {

    private final boolean isScanWithIndex;
    private final boolean isScanAll;
    private Key indexKey;
    final Set> conjunctions = new HashSet<>();

    BuildableScanOrScanAllFromExisting(Scan scan) {
      super(scan.forNamespace().orElse(null), scan.forTable().orElse(null), scan.getPartitionKey());
      isScanWithIndex = scan instanceof ScanWithIndex;
      if (isScanWithIndex) {
        indexKey = scan.getPartitionKey();
      }
      isScanAll = scan instanceof ScanAll;
      scan.getStartClusteringKey()
          .ifPresent(
              key -> {
                startClusteringKey = key;
                startInclusive = scan.getStartInclusive();
              });
      scan.getEndClusteringKey()
          .ifPresent(
              key -> {
                endClusteringKey = key;
                endInclusive = scan.getEndInclusive();
              });
      limit = scan.getLimit();
      orderings.addAll(scan.getOrderings());
      projections.addAll(scan.getProjections());
      consistency = scan.getConsistency();
      conjunctions.addAll(
          scan.getConjunctions().stream()
              .map(Conjunction::getConditions)
              .collect(Collectors.toSet()));
    }

    @Override
    public BuildableScanOrScanAllFromExisting namespace(String namespaceName) {
      checkNotNull(namespaceName);
      this.namespaceName = namespaceName;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting table(String tableName) {
      checkNotNull(tableName);
      this.tableName = tableName;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting partitionKey(Key partitionKey) {
      checkNotScanWithIndexOrScanAll();
      checkNotNull(partitionKey);
      this.partitionKey = partitionKey;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting indexKey(Key indexKey) {
      checkScanWithIndex();
      checkNotNull(indexKey);
      this.indexKey = indexKey;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting consistency(
        com.scalar.db.api.Consistency consistency) {
      super.consistency(consistency);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting projection(String projection) {
      super.projection(projection);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting projections(Collection projections) {
      super.projections(projections);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting projections(String... projections) {
      super.projections(projections);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearProjections() {
      this.projections.clear();
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting limit(int limit) {
      super.limit(limit);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting ordering(Scan.Ordering ordering) {
      checkNotScanWithIndex();
      super.ordering(ordering);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting orderings(Collection orderings) {
      checkNotScanWithIndex();
      super.orderings(orderings);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting orderings(Scan.Ordering... orderings) {
      checkNotScanWithIndex();
      super.orderings(orderings);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting start(Key clusteringKey, boolean inclusive) {
      checkNotScanWithIndexOrScanAll();
      super.start(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting end(Key clusteringKey, boolean inclusive) {
      checkNotScanWithIndexOrScanAll();
      super.end(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting start(Key clusteringKey) {
      checkNotScanWithIndexOrScanAll();
      super.start(clusteringKey);
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting end(Key clusteringKey) {
      checkNotScanWithIndexOrScanAll();
      super.end(clusteringKey);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhere where(ConditionalExpression condition) {
      checkConditionsEmpty();
      checkNotNull(condition);
      return new BuildableScanFromExistingWithOngoingWhere(this, condition);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd where(OrConditionSet orConditionSet) {
      checkConditionsEmpty();
      checkNotNull(orConditionSet);
      return new BuildableScanFromExistingWithOngoingWhereAnd(this, orConditionSet);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr where(AndConditionSet andConditionSet) {
      checkConditionsEmpty();
      checkNotNull(andConditionSet);
      return new BuildableScanFromExistingWithOngoingWhereOr(this, andConditionSet);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd whereAnd(
        Set orConditionSets) {
      checkConditionsEmpty();
      checkNotNull(orConditionSets);
      return new BuildableScanFromExistingWithOngoingWhereAnd(this, orConditionSets);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr whereOr(
        Set andConditionSets) {
      checkConditionsEmpty();
      checkNotNull(andConditionSets);
      return new BuildableScanFromExistingWithOngoingWhereOr(this, andConditionSets);
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearStart() {
      checkNotScanWithIndexOrScanAll();
      this.startClusteringKey = null;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearEnd() {
      checkNotScanWithIndexOrScanAll();
      this.endClusteringKey = null;
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearOrderings() {
      checkNotScanWithIndex();
      this.orderings.clear();
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearConditions() {
      this.conjunctions.clear();
      return this;
    }

    @Override
    public BuildableScanOrScanAllFromExisting clearNamespace() {
      this.namespaceName = null;
      return this;
    }

    private void checkNotScanWithIndexOrScanAll() {
      if (isScanWithIndex || isScanAll) {
        throw new UnsupportedOperationException(
            CoreError
                .SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_ALL_RECORDS_OF_DATABASE_OR_SCANNING_RECORDS_OF_DATABASE_USING_INDEX
                .buildMessage());
      }
    }

    private void checkScanWithIndex() {
      if (!isScanWithIndex) {
        throw new UnsupportedOperationException(
            CoreError
                .SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX
                .buildMessage());
      }
    }

    private void checkNotScanWithIndex() {
      if (isScanWithIndex) {
        throw new UnsupportedOperationException(
            CoreError
                .SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX
                .buildMessage());
      }
    }

    private void checkConditionsEmpty() {
      if (!conjunctions.isEmpty()) {
        throw new IllegalStateException(
            CoreError.SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_NO_CONDITIONS_ARE_SPECIFIED
                .buildMessage());
      }
    }

    @Override
    public Scan build() {
      Scan scan;

      if (isScanWithIndex) {
        scan = new ScanWithIndex(indexKey);
      } else if (isScanAll) {
        scan = new ScanAll();
        orderings.forEach(scan::withOrdering);
      } else {
        scan = new Scan(partitionKey);
        orderings.forEach(scan::withOrdering);
        if (startClusteringKey != null) {
          scan.withStart(startClusteringKey, startInclusive);
        }
        if (endClusteringKey != null) {
          scan.withEnd(endClusteringKey, endInclusive);
        }
      }

      if (!conjunctions.isEmpty()) {
        scan.withConjunctions(
            conjunctions.stream().map(Conjunction::of).collect(Collectors.toSet()));
      }

      scan.forNamespace(namespaceName)
          .forTable(tableName)
          .withLimit(limit)
          .withConsistency(consistency);
      if (!projections.isEmpty()) {
        scan.withProjections(projections);
      }

      return scan;
    }
  }

  public static class BuildableScanFromExistingWithWhere
      implements OperationBuilder.Namespace,
          OperationBuilder.Table,
          PartitionKey,
          ClusteringKeyFiltering,
          IndexKey,
          Consistency,
          Projection,
          Ordering,
          Limit,
          ClearProjections,
          ClearOrderings,
          ClearNamespace {

    private final BuildableScanOrScanAllFromExisting buildableScanFromExisting;
    final Where where;

    private BuildableScanFromExistingWithWhere(BuildableScanFromExistingWithWhere buildable) {
      this.buildableScanFromExisting = buildable.buildableScanFromExisting;
      this.where = buildable.where;
    }

    private BuildableScanFromExistingWithWhere(
        BuildableScanOrScanAllFromExisting buildable, @Nullable ConditionalExpression condition) {
      this.buildableScanFromExisting = buildable;
      this.where = new Where(condition);
    }

    private BuildableScanFromExistingWithWhere(BuildableScanOrScanAllFromExisting buildable) {
      this(buildable, null);
    }

    @Override
    public BuildableScanFromExistingWithWhere namespace(String namespaceName) {
      buildableScanFromExisting.namespace(namespaceName);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere table(String tableName) {
      buildableScanFromExisting.table(tableName);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere partitionKey(Key partitionKey) {
      buildableScanFromExisting.partitionKey(partitionKey);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere start(Key clusteringKey, boolean inclusive) {
      buildableScanFromExisting.start(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere end(Key clusteringKey, boolean inclusive) {
      buildableScanFromExisting.end(clusteringKey, inclusive);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere indexKey(Key indexKey) {
      buildableScanFromExisting.indexKey(indexKey);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere projection(String projection) {
      buildableScanFromExisting.projections(projection);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere projections(Collection projections) {
      buildableScanFromExisting.projections(projections);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere projections(String... projections) {
      return projections(Arrays.asList(projections));
    }

    @Override
    public BuildableScanFromExistingWithWhere ordering(Scan.Ordering ordering) {
      buildableScanFromExisting.ordering(ordering);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere orderings(Collection orderings) {
      buildableScanFromExisting.orderings(orderings);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere orderings(Scan.Ordering... orderings) {
      return orderings(Arrays.asList(orderings));
    }

    @Override
    public BuildableScanFromExistingWithWhere limit(int limit) {
      buildableScanFromExisting.limit(limit);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere consistency(
        com.scalar.db.api.Consistency consistency) {
      buildableScanFromExisting.consistency(consistency);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere clearProjections() {
      buildableScanFromExisting.clearProjections();
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere clearOrderings() {
      buildableScanFromExisting.clearOrderings();
      return this;
    }

    @Override
    public BuildableScanFromExistingWithWhere clearNamespace() {
      buildableScanFromExisting.clearNamespace();
      return this;
    }

    public Scan build() {
      return (Scan) addConjunctionsTo(buildableScanFromExisting.build(), where);
    }
  }

  public static class BuildableScanFromExistingWithOngoingWhere
      extends BuildableScanFromExistingWithWhere
      implements And,
          Or {

    private BuildableScanFromExistingWithOngoingWhere(
        BuildableScanOrScanAllFromExisting buildable) {
      super(buildable);
    }

    private BuildableScanFromExistingWithOngoingWhere(
        BuildableScanOrScanAllFromExisting buildable, ConditionalExpression condition) {
      super(buildable, condition);
    }

    private BuildableScanFromExistingWithOngoingWhere(
        BuildableScanFromExistingWithOngoingWhere buildable) {
      super(buildable);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return new BuildableScanFromExistingWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return new BuildableScanFromExistingWithOngoingWhereAnd(this);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return new BuildableScanFromExistingWithOngoingWhereOr(this);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return new BuildableScanFromExistingWithOngoingWhereOr(this);
    }
  }

  public static class BuildableScanFromExistingWithOngoingWhereOr
      extends BuildableScanFromExistingWithOngoingWhere
      implements Or {

    private BuildableScanFromExistingWithOngoingWhereOr(
        BuildableScanFromExistingWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanFromExistingWithOngoingWhereOr(
        BuildableScanOrScanAllFromExisting buildable, AndConditionSet andConditionSet) {
      super(buildable);
      where.or(andConditionSet);
    }

    private BuildableScanFromExistingWithOngoingWhereOr(
        BuildableScanOrScanAllFromExisting buildable, Set andConditionSets) {
      super(buildable);
      where.or(andConditionSets);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr or(ConditionalExpression condition) {
      checkNotNull(condition);
      where.or(condition);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereOr or(AndConditionSet andConditionSet) {
      checkNotNull(andConditionSet);
      where.or(andConditionSet);
      return this;
    }
  }

  public static class BuildableScanFromExistingWithOngoingWhereAnd
      extends BuildableScanFromExistingWithOngoingWhere
      implements And {

    private BuildableScanFromExistingWithOngoingWhereAnd(
        BuildableScanFromExistingWithOngoingWhere buildable) {
      super(buildable);
    }

    private BuildableScanFromExistingWithOngoingWhereAnd(
        BuildableScanOrScanAllFromExisting buildable, OrConditionSet orConditionSet) {
      super(buildable);
      where.and(orConditionSet);
    }

    private BuildableScanFromExistingWithOngoingWhereAnd(
        BuildableScanOrScanAllFromExisting buildable, Set orConditionSets) {
      super(buildable);
      where.and(orConditionSets);
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd and(ConditionalExpression condition) {
      checkNotNull(condition);
      where.and(condition);
      return this;
    }

    @Override
    public BuildableScanFromExistingWithOngoingWhereAnd and(OrConditionSet orConditionSet) {
      checkNotNull(orConditionSet);
      where.and(orConditionSet);
      return this;
    }
  }
}