SortOperator.java

1
/*
2
 * Copyright OpenSearch Contributors
3
 * SPDX-License-Identifier: Apache-2.0
4
 */
5
6
7
package org.opensearch.sql.planner.physical;
8
9
import static org.opensearch.sql.ast.tree.Sort.NullOrder.NULL_FIRST;
10
import static org.opensearch.sql.ast.tree.Sort.SortOrder.ASC;
11
12
import java.util.Collections;
13
import java.util.Comparator;
14
import java.util.Iterator;
15
import java.util.List;
16
import java.util.PriorityQueue;
17
import lombok.Builder;
18
import lombok.EqualsAndHashCode;
19
import lombok.Getter;
20
import lombok.Singular;
21
import lombok.ToString;
22
import org.apache.commons.lang3.tuple.Pair;
23
import org.opensearch.sql.ast.tree.Sort.SortOption;
24
import org.opensearch.sql.data.model.ExprValue;
25
import org.opensearch.sql.data.utils.ExprValueOrdering;
26
import org.opensearch.sql.expression.Expression;
27
import org.opensearch.sql.planner.physical.SortOperator.Sorter.SorterBuilder;
28
29
/**
30
 * Sort Operator.The input data is sorted by the sort fields in the {@link SortOperator#sortList}.
31
 * The sort field is specified by the {@link Expression} with {@link SortOption}.
32
 * The count indicate how many sorted result should been return.
33
 */
34
@ToString
35
@EqualsAndHashCode(callSuper = false)
36
public class SortOperator extends PhysicalPlan {
37
  @Getter
38
  private final PhysicalPlan input;
39
40
  @Getter
41
  private final List<Pair<SortOption, Expression>> sortList;
42
  @EqualsAndHashCode.Exclude
43
  private final Sorter sorter;
44
  @EqualsAndHashCode.Exclude
45
  private Iterator<ExprValue> iterator;
46
47
  /**
48
   * Sort Operator Constructor.
49
   * @param input input {@link PhysicalPlan}
50
   * @param sortList list of sort sort field.
51
   *                 The sort field is specified by the {@link Expression} with {@link SortOption}
52
   */
53
  public SortOperator(
54
      PhysicalPlan input, List<Pair<SortOption, Expression>> sortList) {
55
    this.input = input;
56
    this.sortList = sortList;
57
    SorterBuilder sorterBuilder = Sorter.builder();
58
    for (Pair<SortOption, Expression> pair : sortList) {
59
      SortOption option = pair.getLeft();
60
      ExprValueOrdering ordering =
61 1 1. <init> : negated conditional → KILLED
          ASC.equals(option.getSortOrder())
62
              ? ExprValueOrdering.natural()
63
              : ExprValueOrdering.natural().reverse();
64
      ordering =
65 1 1. <init> : negated conditional → KILLED
          NULL_FIRST.equals(option.getNullOrder()) ? ordering.nullsFirst() : ordering.nullsLast();
66
      sorterBuilder.comparator(Pair.of(pair.getRight(), ordering));
67
    }
68
    this.sorter = sorterBuilder.build();
69
  }
70
71
  @Override
72
  public <R, C> R accept(PhysicalPlanNodeVisitor<R, C> visitor, C context) {
73 1 1. accept : replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::accept → KILLED
    return visitor.visitSort(this, context);
74
  }
75
76
  @Override
77
  public void open() {
78 1 1. open : removed call to org/opensearch/sql/planner/physical/PhysicalPlan::open → SURVIVED
    super.open();
79
    PriorityQueue<ExprValue> sorted = new PriorityQueue<>(1, sorter::compare);
80 1 1. open : negated conditional → KILLED
    while (input.hasNext()) {
81
      sorted.add(input.next());
82
    }
83
84
    iterator = iterator(sorted);
85
  }
86
87
  @Override
88
  public List<PhysicalPlan> getChild() {
89 1 1. getChild : replaced return value with Collections.emptyList for org/opensearch/sql/planner/physical/SortOperator::getChild → KILLED
    return Collections.singletonList(input);
90
  }
91
92
  @Override
93
  public boolean hasNext() {
94 2 1. hasNext : replaced boolean return with false for org/opensearch/sql/planner/physical/SortOperator::hasNext → KILLED
2. hasNext : replaced boolean return with true for org/opensearch/sql/planner/physical/SortOperator::hasNext → KILLED
    return iterator.hasNext();
95
  }
96
97
  @Override
98
  public ExprValue next() {
99 1 1. next : replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::next → KILLED
    return iterator.next();
100
  }
101
102
  @Builder
103
  public static class Sorter implements Comparator<ExprValue> {
104
    @Singular
105
    private final List<Pair<Expression, Comparator<ExprValue>>> comparators;
106
107
    @Override
108
    public int compare(ExprValue o1, ExprValue o2) {
109
      for (Pair<Expression, Comparator<ExprValue>> comparator : comparators) {
110
        Expression expression = comparator.getKey();
111
        int result =
112
            comparator
113
                .getValue()
114
                .compare(
115
                    expression.valueOf(o1.bindingTuples()), expression.valueOf(o2.bindingTuples()));
116 1 1. compare : negated conditional → KILLED
        if (result != 0) {
117 1 1. compare : replaced int return with 0 for org/opensearch/sql/planner/physical/SortOperator$Sorter::compare → KILLED
          return result;
118
        }
119
      }
120
      return 0;
121
    }
122
  }
123
124
  private Iterator<ExprValue> iterator(PriorityQueue<ExprValue> result) {
125 1 1. iterator : replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::iterator → KILLED
    return new Iterator<ExprValue>() {
126
      @Override
127
      public boolean hasNext() {
128 2 1. hasNext : negated conditional → KILLED
2. hasNext : replaced boolean return with true for org/opensearch/sql/planner/physical/SortOperator$1::hasNext → KILLED
        return !result.isEmpty();
129
      }
130
131
      @Override
132
      public ExprValue next() {
133 1 1. next : replaced return value with null for org/opensearch/sql/planner/physical/SortOperator$1::next → KILLED
        return result.poll();
134
      }
135
    };
136
  }
137
}

Mutations

61

1.1
Location : <init>
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
negated conditional → KILLED

65

1.1
Location : <init>
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc_with_null_value()]
negated conditional → KILLED

73

1.1
Location : accept
Killed by : org.opensearch.sql.executor.ExplainTest.[engine:junit-jupiter]/[class:org.opensearch.sql.executor.ExplainTest]/[method:can_explain_other_operators()]
replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::accept → KILLED

78

1.1
Location : open
Killed by : none
removed call to org/opensearch/sql/planner/physical/PhysicalPlan::open → SURVIVED

80

1.1
Location : open
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_without_input()]
negated conditional → KILLED

89

1.1
Location : getChild
Killed by : org.opensearch.sql.executor.ExplainTest.[engine:junit-jupiter]/[class:org.opensearch.sql.executor.ExplainTest]/[method:can_explain_other_operators()]
replaced return value with Collections.emptyList for org/opensearch/sql/planner/physical/SortOperator::getChild → KILLED

94

1.1
Location : hasNext
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
replaced boolean return with false for org/opensearch/sql/planner/physical/SortOperator::hasNext → KILLED

2.2
Location : hasNext
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_without_input()]
replaced boolean return with true for org/opensearch/sql/planner/physical/SortOperator::hasNext → KILLED

99

1.1
Location : next
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::next → KILLED

116

1.1
Location : compare
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
negated conditional → KILLED

117

1.1
Location : compare
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
replaced int return with 0 for org/opensearch/sql/planner/physical/SortOperator$Sorter::compare → KILLED

125

1.1
Location : iterator
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_without_input()]
replaced return value with null for org/opensearch/sql/planner/physical/SortOperator::iterator → KILLED

128

1.1
Location : hasNext
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_without_input()]
negated conditional → KILLED

2.2
Location : hasNext
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_without_input()]
replaced boolean return with true for org/opensearch/sql/planner/physical/SortOperator$1::hasNext → KILLED

133

1.1
Location : next
Killed by : org.opensearch.sql.planner.physical.SortOperatorTest.[engine:junit-jupiter]/[class:org.opensearch.sql.planner.physical.SortOperatorTest]/[method:sort_one_field_asc()]
replaced return value with null for org/opensearch/sql/planner/physical/SortOperator$1::next → KILLED

Active mutators

Tests examined


Report generated by PIT 1.9.0