Add search fields for # of changed lines.
Based off https://gerrit-review.googlesource.com/#/c/52190, but
implementing the final suggestion of indexing raw delta counts and
allowing arbitrary range queries off of those.
Also upgrade Lucene to 4.8.1 as this was released since the last
schema change (which was on 4.7.0).
Change-Id: Ia8a677e71e133f68eced4c5394df1d23efe7f12a
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 9c2a4c5..eaf0f6e 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -314,6 +314,18 @@
+
Change has been abandoned.
+[[size]]
+added:'RELATION''LINES', deleted:'RELATION''LINES', delta/size:'RELATION''LINES'::
++
+True if the number of lines added/deleted/changed satisfies the given relation
+for the given number of lines.
++
+For example, added:>50 will be true for any change which adds at least 50
+lines.
++
+Valid relations are >=, >, <=, <, or no relation, which will match if the
+number of lines is exactly equal.
+
== Argument Quoting
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
index 10220e5..e92e613 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
@@ -119,6 +119,11 @@
suggestions.add("status:merged");
suggestions.add("status:abandoned");
+ suggestions.add("added:");
+ suggestions.add("deleted:");
+ suggestions.add("delta:");
+ suggestions.add("size:");
+
suggestions.add("AND");
suggestions.add("OR");
suggestions.add("NOT");
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index c790740..db8738f 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -127,6 +127,8 @@
Version lucene44 = Version.LUCENE_44;
@SuppressWarnings("deprecation")
Version lucene46 = Version.LUCENE_46;
+ @SuppressWarnings("deprecation")
+ Version lucene47 = Version.LUCENE_47;
for (Map.Entry<Integer, Schema<ChangeData>> e
: ChangeSchemas.ALL.entrySet()) {
if (e.getKey() <= 3) {
@@ -135,8 +137,10 @@
versions.put(e.getValue(), lucene44);
} else if (e.getKey() <= 8) {
versions.put(e.getValue(), lucene46);
+ } else if (e.getKey() <= 10) {
+ versions.put(e.getValue(), lucene47);
} else {
- versions.put(e.getValue(), Version.LUCENE_47);
+ versions.put(e.getValue(), Version.LUCENE_48);
}
}
LUCENE_VERSIONS = versions.build();
@@ -497,7 +501,7 @@
FieldType<?> type = values.getField().getType();
Store store = store(values.getField());
- if (type == FieldType.INTEGER) {
+ if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) {
for (Object value : values.getValues()) {
doc.add(new IntField(name, (Integer) value, store));
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
index 3221b8a..dfe3f2d 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.FieldType;
import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.index.IntegerRangePredicate;
import com.google.gerrit.server.index.RegexPredicate;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.TimestampRangePredicate;
@@ -135,6 +136,8 @@
throws QueryParseException {
if (p.getType() == FieldType.INTEGER) {
return intQuery(p);
+ } else if (p.getType() == FieldType.INTEGER_RANGE) {
+ return intRangeQuery(p);
} else if (p.getType() == FieldType.TIMESTAMP) {
return timestampQuery(p);
} else if (p.getType() == FieldType.EXACT) {
@@ -169,6 +172,28 @@
return new TermQuery(intTerm(p.getField().getName(), value));
}
+ private Query intRangeQuery(IndexPredicate<ChangeData> p)
+ throws QueryParseException {
+ if (p instanceof IntegerRangePredicate) {
+ IntegerRangePredicate<ChangeData> r =
+ (IntegerRangePredicate<ChangeData>) p;
+ int minimum = r.getMinimumValue();
+ int maximum = r.getMaximumValue();
+ if (minimum == maximum) {
+ // Just fall back to a standard integer query.
+ return new TermQuery(intTerm(p.getField().getName(), minimum));
+ } else {
+ return NumericRangeQuery.newIntRange(
+ r.getField().getName(),
+ minimum,
+ maximum,
+ true,
+ true);
+ }
+ }
+ throw new QueryParseException("not an integer range: " + p);
+ }
+
private Query sortKeyQuery(SortKeyPredicate p) {
long min = p.getMinValue(schema);
long max = p.getMaxValue(schema);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index a5777da..41dfba5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
+import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
import com.google.gwtorm.protobuf.CodecFactory;
import com.google.gwtorm.protobuf.ProtobufCodec;
import com.google.gwtorm.server.OrmException;
@@ -428,6 +429,40 @@
}
};
+ /** The number of inserted lines in this change. */
+ public static final FieldDef<ChangeData, Integer> ADDED =
+ new FieldDef.Single<ChangeData, Integer>(
+ ChangeQueryBuilder.FIELD_ADDED, FieldType.INTEGER_RANGE, true) {
+ @Override
+ public Integer get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return input.changedLines().insertions;
+ }
+ };
+
+ /** The number of deleted lines in this change. */
+ public static final FieldDef<ChangeData, Integer> DELETED =
+ new FieldDef.Single<ChangeData, Integer>(
+ ChangeQueryBuilder.FIELD_DELETED, FieldType.INTEGER_RANGE, true) {
+ @Override
+ public Integer get(ChangeData input, FillArgs args)
+ throws OrmException {
+ return input.changedLines().deletions;
+ }
+ };
+
+ /** The total number of modified lines in this change. */
+ public static final FieldDef<ChangeData, Integer> DELTA =
+ new FieldDef.Single<ChangeData, Integer>(
+ ChangeQueryBuilder.FIELD_DELTA, FieldType.INTEGER_RANGE, false) {
+ @Override
+ public Integer get(ChangeData input, FillArgs args)
+ throws OrmException {
+ ChangedLines changedLines = input.changedLines();
+ return changedLines.insertions + changedLines.deletions;
+ }
+ };
+
private static <T> List<byte[]> toProtos(ProtobufCodec<T> codec, Collection<T> objs)
throws OrmException {
List<byte[]> result = Lists.newArrayListWithCapacity(objs.size());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
index 1a628d3..8bb8f0b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeSchemas.java
@@ -217,6 +217,32 @@
ChangeField.APPROVAL,
ChangeField.MERGEABLE);
+ static final Schema<ChangeData> V11 = release(
+ ChangeField.LEGACY_ID,
+ ChangeField.ID,
+ ChangeField.STATUS,
+ ChangeField.PROJECT,
+ ChangeField.PROJECTS,
+ ChangeField.REF,
+ ChangeField.TOPIC,
+ ChangeField.UPDATED,
+ ChangeField.FILE_PART,
+ ChangeField.PATH,
+ ChangeField.OWNER,
+ ChangeField.REVIEWER,
+ ChangeField.COMMIT,
+ ChangeField.TR,
+ ChangeField.LABEL,
+ ChangeField.REVIEWED,
+ ChangeField.COMMIT_MESSAGE,
+ ChangeField.COMMENT,
+ ChangeField.CHANGE,
+ ChangeField.APPROVAL,
+ ChangeField.MERGEABLE,
+ ChangeField.ADDED,
+ ChangeField.DELETED,
+ ChangeField.DELTA);
+
private static Schema<ChangeData> release(Collection<FieldDef<ChangeData, ?>> fields) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
index d4f9966..872179d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.index;
+import com.google.common.base.Preconditions;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gwtorm.server.OrmException;
@@ -47,6 +48,8 @@
extends FieldDef<I, Iterable<T>> {
Repeatable(String name, FieldType<T> type, boolean stored) {
super(name, type, stored);
+ Preconditions.checkArgument(type != FieldType.INTEGER_RANGE,
+ "Range queries against repeated fields are unsupported");
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
index a3247b9..4c40769 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldType.java
@@ -23,6 +23,10 @@
public static final FieldType<Integer> INTEGER =
new FieldType<Integer>("INTEGER");
+ /** A single-integer-valued field matched using range queries. */
+ public static final FieldType<Integer> INTEGER_RANGE =
+ new FieldType<Integer>("INTEGER_RANGE");
+
/** A single integer-valued field. */
public static final FieldType<Long> LONG =
new FieldType<Long>("LONG");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java
new file mode 100644
index 0000000..1259951
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IntegerRangePredicate.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 com.google.gerrit.server.index;
+
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.util.RangeUtil;
+import com.google.gerrit.server.util.RangeUtil.Range;
+import com.google.gwtorm.server.OrmException;
+
+public abstract class IntegerRangePredicate<T> extends IndexPredicate<T> {
+ private final Range range;
+
+ protected IntegerRangePredicate(FieldDef<T, Integer> type,
+ String value) throws QueryParseException {
+ super(type, value);
+ range = RangeUtil.getRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ if (range == null) {
+ throw new QueryParseException("Invalid range predicate: " + value);
+ }
+ }
+
+ protected abstract int getValueInt(T object) throws OrmException;
+
+ @Override
+ public boolean match(T object) throws OrmException {
+ int valueInt = getValueInt(object);
+ return valueInt >= range.min && valueInt <= range.max;
+ }
+
+ /** Return the minimum value of this predicate's range, inclusive. */
+ public int getMinimumValue() {
+ return range.min;
+ }
+
+ /** Return the maximum value of this predicate's range, inclusive. */
+ public int getMaximumValue() {
+ return range.max;
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
index 9277f6b..f611052 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/TimestampRangePredicate.java
@@ -25,6 +25,7 @@
import java.sql.Timestamp;
import java.util.Date;
+// TODO: Migrate this to IntegerRangePredicate
public abstract class TimestampRangePredicate<I> extends IndexPredicate<I> {
@SuppressWarnings({"deprecation", "unchecked"})
protected static FieldDef<ChangeData, Timestamp> updatedField(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java
new file mode 100644
index 0000000..95da72a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/AddedPredicate.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.IntegerRangePredicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gwtorm.server.OrmException;
+
+public class AddedPredicate extends IntegerRangePredicate<ChangeData> {
+ AddedPredicate(String value) throws QueryParseException {
+ super(ChangeField.ADDED, value);
+ }
+
+ @Override
+ protected int getValueInt(ChangeData changeData) throws OrmException {
+ return changeData.changedLines().insertions;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 4c595ea..ac7b9ae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -75,6 +75,7 @@
// NOTE: As new search operations are added, please keep the
// SearchSuggestOracle up to date.
+ public static final String FIELD_ADDED = "added";
public static final String FIELD_AFTER = "after";
public static final String FIELD_AGE = "age";
public static final String FIELD_BEFORE = "before";
@@ -83,6 +84,8 @@
public static final String FIELD_COMMENT = "comment";
public static final String FIELD_COMMIT = "commit";
public static final String FIELD_CONFLICTS = "conflicts";
+ public static final String FIELD_DELETED = "deleted";
+ public static final String FIELD_DELTA = "delta";
public static final String FIELD_DRAFTBY = "draftby";
public static final String FIELD_FILE = "file";
public static final String FIELD_IS = "is";
@@ -676,6 +679,30 @@
return sortkey_before(sortKey);
}
+ @Operator
+ public Predicate<ChangeData> added(String value)
+ throws QueryParseException {
+ return new AddedPredicate(value);
+ }
+
+ @Operator
+ public Predicate<ChangeData> deleted(String value)
+ throws QueryParseException {
+ return new DeletedPredicate(value);
+ }
+
+ @Operator
+ public Predicate<ChangeData> size(String value)
+ throws QueryParseException {
+ return delta(value);
+ }
+
+ @Operator
+ public Predicate<ChangeData> delta(String value)
+ throws QueryParseException {
+ return new DeltaPredicate(value);
+ }
+
@Override
protected Predicate<ChangeData> defaultField(String query) {
if (query.startsWith("refs/")) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java
new file mode 100644
index 0000000..478990d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeletedPredicate.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.IntegerRangePredicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gwtorm.server.OrmException;
+
+public class DeletedPredicate extends IntegerRangePredicate<ChangeData> {
+ DeletedPredicate(String value) throws QueryParseException {
+ super(ChangeField.DELETED, value);
+ }
+
+ @Override
+ protected int getValueInt(ChangeData changeData) throws OrmException {
+ return changeData.changedLines().deletions;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java
new file mode 100644
index 0000000..39b860a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/DeltaPredicate.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 com.google.gerrit.server.query.change;
+
+import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.IntegerRangePredicate;
+import com.google.gerrit.server.query.QueryParseException;
+import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
+import com.google.gwtorm.server.OrmException;
+
+public class DeltaPredicate extends IntegerRangePredicate<ChangeData> {
+ DeltaPredicate(String value) throws QueryParseException {
+ super(ChangeField.DELTA, value);
+ }
+
+ @Override
+ protected int getValueInt(ChangeData changeData) throws OrmException {
+ ChangedLines changedLines = changeData.changedLines();
+ return changedLines.insertions + changedLines.deletions;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
index 60f7ffa..9b5aae3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -24,12 +24,12 @@
import com.google.gerrit.server.query.OrPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.util.LabelVote;
+import com.google.gerrit.server.util.RangeUtil;
+import com.google.gerrit.server.util.RangeUtil.Range;
import com.google.inject.Provider;
import java.util.List;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class LabelPredicate extends OrPredicate<ChangeData> {
private static final int MAX_LABEL_VALUE = 4;
@@ -102,43 +102,28 @@
// Try next format.
}
+ Range range;
if (parsed == null) {
- Matcher m = Pattern.compile("(>|>=|=|<|<=)([+-]?\\d+)$").matcher(v);
- if (m.find()) {
- parsed = new Parsed(v.substring(0, m.start()), m.group(1),
- value(m.group(2)));
- } else {
- parsed = new Parsed(v, "=", 1);
+ range = RangeUtil.getRange(v, -MAX_LABEL_VALUE, MAX_LABEL_VALUE);
+ if (range == null) {
+ range = new Range(v, 1, 1);
}
+ } else {
+ range = RangeUtil.getRange(
+ parsed.label,
+ parsed.test,
+ parsed.expVal,
+ -MAX_LABEL_VALUE,
+ MAX_LABEL_VALUE);
}
+ String prefix = range.prefix;
+ int min = range.min;
+ int max = range.max;
- int min, max;
- switch (parsed.test) {
- case "=":
- default:
- min = max = parsed.expVal;
- break;
- case ">":
- min = parsed.expVal + 1;
- max = MAX_LABEL_VALUE;
- break;
- case ">=":
- min = parsed.expVal;
- max = MAX_LABEL_VALUE;
- break;
- case "<":
- min = -MAX_LABEL_VALUE;
- max = parsed.expVal - 1;
- break;
- case "<=":
- min = -MAX_LABEL_VALUE;
- max = parsed.expVal;
- break;
- }
List<Predicate<ChangeData>> r =
Lists.newArrayListWithCapacity(max - min + 1);
for (int i = min; i <= max; i++) {
- r.add(onePredicate(args, parsed.label, i));
+ r.add(onePredicate(args, prefix, i));
}
return r;
}
@@ -152,13 +137,6 @@
}
}
- private static int value(String value) {
- if (value.startsWith("+")) {
- value = value.substring(1);
- }
- return Integer.parseInt(value);
- }
-
private static Predicate<ChangeData> noLabelQuery(Args args, String label) {
List<Predicate<ChangeData>> r =
Lists.newArrayListWithCapacity(2 * MAX_LABEL_VALUE);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java
new file mode 100644
index 0000000..5c5e2f9
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/RangeUtil.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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
+//
+// http://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 com.google.gerrit.server.util;
+
+import com.google.common.primitives.Ints;
+import com.google.gerrit.common.Nullable;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class RangeUtil {
+ private static final Pattern RANGE_PATTERN =
+ Pattern.compile("(>|>=|=|<|<=|)([+-]?\\d+)$");
+
+ private RangeUtil() {}
+
+ public static class Range {
+ /** The prefix of the query, before the range component. */
+ public final String prefix;
+
+ /** The minimum value specified in the query, inclusive. */
+ public final int min;
+
+ /** The maximum value specified in the query, inclusive. */
+ public final int max;
+
+ public Range(String prefix, int min, int max) {
+ this.prefix = prefix;
+ this.min = min;
+ this.max = max;
+ }
+ }
+
+ /**
+ * Determine the range of values being requested in the given query.
+ *
+ * @param rangeQuery the raw query, e.g. "added:>12345"
+ * @param minValue the minimum possible value for the field, inclusive
+ * @param maxValue the maximum possible value for the field, inclusive
+ * @return the calculated {@link Range}, or null if the query is invalid
+ */
+ @Nullable
+ public static Range getRange(String rangeQuery, int minValue, int maxValue) {
+ Matcher m = RANGE_PATTERN.matcher(rangeQuery);
+ String prefix;
+ String test;
+ Integer queryInt;
+ if (m.find()) {
+ prefix = rangeQuery.substring(0, m.start());
+ test = m.group(1);
+ queryInt = value(m.group(2));
+ if (queryInt == null) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+
+ return getRange(prefix, test, queryInt, minValue, maxValue);
+ }
+
+ /**
+ * Determine the range of values being requested in the given query.
+ *
+ * @param prefix a prefix string which is copied into the range
+ * @param test the test operator, one of >, >=, =, <, or <=
+ * @param queryInt the integer being queried
+ * @param minValue the minimum possible value for the field, inclusive
+ * @param maxValue the maximum possible value for the field, inclusive
+ * @return the calculated {@link Range}
+ */
+ public static Range getRange(
+ String prefix, String test, int queryInt, int minValue, int maxValue) {
+ int min, max;
+ switch (test) {
+ case "=":
+ default:
+ min = max = queryInt;
+ break;
+ case ">":
+ min = Ints.saturatedCast(queryInt + 1L);
+ max = maxValue;
+ break;
+ case ">=":
+ min = queryInt;
+ max = maxValue;
+ break;
+ case "<":
+ min = minValue;
+ max = Ints.saturatedCast(queryInt - 1L);
+ break;
+ case "<=":
+ min = minValue;
+ max = queryInt;
+ break;
+ }
+
+ return new Range(prefix, min, max);
+ }
+
+ private static Integer value(String value) {
+ if (value.startsWith("+")) {
+ value = value.substring(1);
+ }
+ return Ints.tryParse(value);
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 5a534d5..fb50744 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -814,6 +814,44 @@
}
@Test
+ public void bySize() throws Exception {
+ TestRepository<InMemoryRepository> repo = createProject("repo");
+
+ // added = 3, deleted = 0, delta = 3
+ RevCommit commit1 = repo.parseBody(
+ repo.commit().add("file1", "foo\n\foo\nfoo").create());
+ // added = 0, deleted = 2, delta = 2
+ RevCommit commit2 = repo.parseBody(
+ repo.commit().parent(commit1).add("file1", "foo").create());
+
+ Change change1 = newChange(repo, commit1, null, null, null).insert();
+ Change change2 = newChange(repo, commit2, null, null, null).insert();
+
+ assertTrue(query("added:>4").isEmpty());
+ assertResultEquals(change1, queryOne("added:3"));
+ assertResultEquals(change1, queryOne("added:>2"));
+ assertResultEquals(change1, queryOne("added:>=3"));
+ assertResultEquals(change2, queryOne("added:<1"));
+ assertResultEquals(change2, queryOne("added:<=0"));
+
+ assertTrue(query("deleted:>3").isEmpty());
+ assertResultEquals(change2, queryOne("deleted:2"));
+ assertResultEquals(change2, queryOne("deleted:>1"));
+ assertResultEquals(change2, queryOne("deleted:>=2"));
+ assertResultEquals(change1, queryOne("deleted:<1"));
+ assertResultEquals(change1, queryOne("deleted:<=0"));
+
+ for (String str : Lists.newArrayList("delta", "size")) {
+ assertTrue(query(str + ":<2").isEmpty());
+ assertResultEquals(change1, queryOne(str + ":3"));
+ assertResultEquals(change1, queryOne(str + ":>2"));
+ assertResultEquals(change1, queryOne(str + ":>=3"));
+ assertResultEquals(change2, queryOne(str + ":<3"));
+ assertResultEquals(change2, queryOne(str + ":<=2"));
+ }
+ }
+
+ @Test
public void byDefault() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV7Test.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV7Test.java
index cf60297..1dcd83b 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV7Test.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV7Test.java
@@ -54,6 +54,11 @@
@Override
@Test
public void byDefault() {}
+
+ @Ignore
+ @Override
+ @Test
+ public void bySize() {}
// End tests for features not supported in V7.
@Test
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index a56b5ab..9ccc5aa 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -1,11 +1,11 @@
include_defs('//lib/maven.defs')
-VERSION = '4.7.0'
+VERSION = '4.8.1'
maven_jar(
name = 'core',
id = 'org.apache.lucene:lucene-core:' + VERSION,
- sha1 = '12d2b92d15158ac0d7b2864f537403acb4d7f69e',
+ sha1 = 'a549eef6316a2c38d4cda932be809107deeaf8a7',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@@ -16,7 +16,7 @@
maven_jar(
name = 'analyzers-common',
id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
- sha1 = '399fa6b0d750c8e5c9e4ae73e6407c8b3ed4e8c1',
+ sha1 = '6e3731524351c83cd21022a23bee5e87f0575555',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@@ -27,6 +27,6 @@
maven_jar(
name = 'query-parser',
id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
- sha1 = 'f78a804de1582c511224d214c2d9c82ce48379e7',
+ sha1 = 'f3e105d74137906fdeb2c7bc4dd68c08564778f9',
license = 'Apache2.0',
)