/*
* Copyright 2014 Realm Inc.
*
* 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 io.realm;
import java.util.Collections;
import java.util.Date;
import io.realm.annotations.Required;
import io.realm.internal.Collection;
import io.realm.internal.LinkView;
import io.realm.internal.PendingRow;
import io.realm.internal.RealmObjectProxy;
import io.realm.internal.Row;
import io.realm.internal.SortDescriptor;
import io.realm.internal.Table;
import io.realm.internal.TableQuery;
import io.realm.internal.fields.FieldDescriptor;
/**
* A RealmQuery encapsulates a query on a {@link io.realm.Realm} or a {@link io.realm.RealmResults} using the Builder
* pattern. The query is executed using either {@link #findAll()} or {@link #findFirst()}.
* <p>
* The input to many of the query functions take a field name as String. Note that this is not type safe. If a
* RealmObject class is refactored care has to be taken to not break any queries.
* <p>
* A {@link io.realm.Realm} is unordered, which means that there is no guarantee that querying a Realm will return the
* objects in the order they where inserted. Use {@link #findAllSorted(String)} and similar methods if a specific order
* is required.
* <p>
* A RealmQuery cannot be passed between different threads.
*
* @param <E> the class of the objects to be queried.
* @see <a href="http://en.wikipedia.org/wiki/Builder_pattern">Builder pattern</a>
* @see Realm#where(Class)
* @see RealmResults#where()
*/
public class RealmQuery<E extends RealmModel> {
private final Table table;
private final BaseRealm realm;
private final TableQuery query;
private final RealmObjectSchema schema;
private Class<E> clazz;
private String className;
private LinkView linkView;
private static final String TYPE_MISMATCH = "Field '%s': type mismatch - %s expected.";
private static final String EMPTY_VALUES = "Non-empty 'values' must be provided.";
private static final String ASYNC_QUERY_WRONG_THREAD_MESSAGE = "Async query cannot be created on current thread.";
/**
* Creates a query for objects of a given class from a {@link Realm}.
*
* @param realm the realm to query within.
* @param clazz the class to query.
* @return {@link RealmQuery} object. After building the query call one of the {@code find*} methods
* to run it.
*/
public static <E extends RealmModel> RealmQuery<E> createQuery(Realm realm, Class<E> clazz) {
return new RealmQuery<>(realm, clazz);
}
/**
* Creates a query for dynamic objects of a given type from a {@link DynamicRealm}.
*
* @param realm the realm to query within.
* @param className the type to query.
* @return {@link RealmQuery} object. After building the query call one of the {@code find*} methods
* to run it.
*/
public static <E extends RealmModel> RealmQuery<E> createDynamicQuery(DynamicRealm realm, String className) {
return new RealmQuery<>(realm, className);
}
/**
* Creates a query from an existing {@link RealmResults}.
*
* @param queryResults an existing @{link io.realm.RealmResults} to query against.
* @return {@link RealmQuery} object. After building the query call one of the {@code find*} methods
* to run it.
*/
@SuppressWarnings("unchecked")
public static <E extends RealmModel> RealmQuery<E> createQueryFromResult(RealmResults<E> queryResults) {
return (queryResults.classSpec == null)
? new RealmQuery(queryResults, queryResults.className)
: new RealmQuery<>(queryResults, queryResults.classSpec);
}
/**
* Creates a query from an existing {@link RealmList}.
*
* @param list an existing @{link io.realm.RealmList} to query against.
* @return {@link RealmQuery} object. After building the query call one of the {@code find*} methods
* to run it.
*/
@SuppressWarnings("unchecked")
public static <E extends RealmModel> RealmQuery<E> createQueryFromList(RealmList<E> list) {
return (list.clazz == null)
? new RealmQuery(list.realm, list.view, list.className)
: new RealmQuery(list.realm, list.view, list.clazz);
}
private RealmQuery(Realm realm, Class<E> clazz) {
this.realm = realm;
this.clazz = clazz;
this.schema = realm.getSchema().getSchemaForClass(clazz);
this.table = schema.getTable();
this.linkView = null;
this.query = table.where();
}
private RealmQuery(RealmResults<E> queryResults, Class<E> clazz) {
this.realm = queryResults.realm;
this.clazz = clazz;
this.schema = realm.getSchema().getSchemaForClass(clazz);
this.table = queryResults.getTable();
this.linkView = null;
this.query = queryResults.getCollection().where();
}
private RealmQuery(BaseRealm realm, LinkView linkView, Class<E> clazz) {
this.realm = realm;
this.clazz = clazz;
this.schema = realm.getSchema().getSchemaForClass(clazz);
this.table = schema.getTable();
this.linkView = linkView;
this.query = linkView.where();
}
private RealmQuery(BaseRealm realm, String className) {
this.realm = realm;
this.className = className;
this.schema = realm.getSchema().getSchemaForClass(className);
this.table = schema.getTable();
this.query = table.where();
}
private RealmQuery(RealmResults<DynamicRealmObject> queryResults, String className) {
this.realm = queryResults.realm;
this.className = className;
this.schema = realm.getSchema().getSchemaForClass(className);
this.table = schema.getTable();
this.query = queryResults.getCollection().where();
}
private RealmQuery(BaseRealm realm, LinkView linkView, String className) {
this.realm = realm;
this.className = className;
this.schema = realm.getSchema().getSchemaForClass(className);
this.table = schema.getTable();
this.linkView = linkView;
this.query = linkView.where();
}
/**
* Checks if {@link io.realm.RealmQuery} is still valid to use i.e., the {@link io.realm.Realm} instance hasn't been
* closed and any parent {@link io.realm.RealmResults} is still valid.
*
* @return {@code true} if still valid to use, {@code false} otherwise.
*/
public boolean isValid() {
if (realm == null || realm.isClosed() /* this includes thread checking */) {
return false;
}
if (linkView != null) {
return linkView.isAttached();
}
return table != null && table.isValid();
}
/**
* Tests if a field is {@code null}. Only works for nullable fields.
* <p>
* For link queries, if any part of the link path is {@code null} the whole path is considered to be {@code null}
* e.g., {@code isNull("linkField.stringField")} will be considered to be {@code null} if either {@code linkField} or
* {@code linkField.stringField} is {@code null}.
*
* @param fieldName the field name.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field is not nullable.
* @see Required for further infomation.
*/
public RealmQuery<E> isNull(String fieldName) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName);
// Checks that fieldName has the correct type is done in C++.
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
return this;
}
/**
* Tests if a field is not {@code null}. Only works for nullable fields.
*
* @param fieldName the field name.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field is not nullable.
* @see Required for further infomation.
*/
public RealmQuery<E> isNotNull(String fieldName) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName);
// Checks that fieldName has the correct type is done in C++.
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, String value) {
return this.equalTo(fieldName, value, Case.SENSITIVE);
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @param casing how to handle casing. Setting this to {@link Case#INSENSITIVE} only works for Latin-1 characters.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, String value, Case casing) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value, casing);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, String value, Case casing) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Byte value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Byte value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, byte[] value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.BINARY);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Short value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Short value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Integer value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Integer value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Long value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Long value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Double value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Double value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return The query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Float value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Float value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Boolean value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Boolean value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.BOOLEAN);
if (value == null) {
this.query.isNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> equalTo(String fieldName, Date value) {
realm.checkIfValid();
return equalToWithoutThreadValidation(fieldName, value);
}
private RealmQuery<E> equalToWithoutThreadValidation(String fieldName, Date value) {
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a String field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, String[] values) {
return in(fieldName, values, Case.SENSITIVE);
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @param casing how casing is handled. {@link Case#INSENSITIVE} works only for the Latin-1 characters.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a String field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, String[] values, Case casing) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0], casing);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i], casing);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Byte field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Byte[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Short field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Short[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Integer field or {@code values} is {@code null}
* or empty.
*/
public RealmQuery<E> in(String fieldName, Integer[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Long field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Long[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Double field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Double[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Float field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Float[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Boolean field or {@code values} is {@code null}
* or empty.
*/
public RealmQuery<E> in(String fieldName, Boolean[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* In comparison. This allows you to test if objects match any value in an array of values.
*
* @param fieldName the field to compare.
* @param values array of values to compare with and it cannot be null or empty.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field isn't a Date field or {@code values} is {@code null} or
* empty.
*/
public RealmQuery<E> in(String fieldName, Date[] values) {
realm.checkIfValid();
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroupWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
orWithoutThreadValidation().equalToWithoutThreadValidation(fieldName, values[i]);
}
return endGroupWithoutThreadValidation();
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, String value) {
return this.notEqualTo(fieldName, value, Case.SENSITIVE);
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @param casing how casing is handled. {@link Case#INSENSITIVE} works only for the Latin-1 characters.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, String value, Case casing) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
if (fd.length() > 1 && !casing.getValue()) {
throw new IllegalArgumentException("Link queries cannot be case insensitive - coming soon.");
}
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Byte value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, byte[] value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.BINARY);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Short value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Integer value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Long value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Double value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Float value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Boolean value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.BOOLEAN);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.equalTo(fd.getColumnIndices(), fd.getNativeTablePointers(), !value);
}
return this;
}
/**
* Not-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> notEqualTo(String fieldName, Date value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
if (value == null) {
this.query.isNotNull(fd.getColumnIndices(), fd.getNativeTablePointers());
} else {
this.query.notEqualTo(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
}
return this;
}
/**
* Greater-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThan(String fieldName, int value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.greaterThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThan(String fieldName, long value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.greaterThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThan(String fieldName, double value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
this.query.greaterThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThan(String fieldName, float value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
this.query.greaterThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThan(String fieldName, Date value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.greaterThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThanOrEqualTo(String fieldName, int value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.greaterThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThanOrEqualTo(String fieldName, long value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.greaterThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThanOrEqualTo(String fieldName, double value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
this.query.greaterThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type
*/
public RealmQuery<E> greaterThanOrEqualTo(String fieldName, float value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
this.query.greaterThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Greater-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> greaterThanOrEqualTo(String fieldName, Date value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.greaterThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThan(String fieldName, int value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.lessThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThan(String fieldName, long value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.lessThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThan(String fieldName, double value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
this.query.lessThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThan(String fieldName, float value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
this.query.lessThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThan(String fieldName, Date value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.lessThan(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThanOrEqualTo(String fieldName, int value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.lessThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThanOrEqualTo(String fieldName, long value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.lessThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThanOrEqualTo(String fieldName, double value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
this.query.lessThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThanOrEqualTo(String fieldName, float value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
this.query.lessThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Less-than-or-equal-to comparison.
*
* @param fieldName the field to compare.
* @param value the value to compare with.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> lessThanOrEqualTo(String fieldName, Date value) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.lessThanOrEqual(fd.getColumnIndices(), fd.getNativeTablePointers(), value);
return this;
}
/**
* Between condition.
*
* @param fieldName the field to compare.
* @param from lowest value (inclusive).
* @param to highest value (inclusive).
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> between(String fieldName, int from, int to) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.between(fd.getColumnIndices(), from, to);
return this;
}
/**
* Between condition.
*
* @param fieldName the field to compare.
* @param from lowest value (inclusive).
* @param to highest value (inclusive).
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> between(String fieldName, long from, long to) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.INTEGER);
this.query.between(fd.getColumnIndices(), from, to);
return this;
}
/**
* Between condition.
*
* @param fieldName the field to compare.
* @param from lowest value (inclusive).
* @param to highest value (inclusive).
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> between(String fieldName, double from, double to) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DOUBLE);
this.query.between(fd.getColumnIndices(), from, to);
return this;
}
/**
* Between condition.
*
* @param fieldName the field to compare.
* @param from lowest value (inclusive).
* @param to highest value (inclusive).
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> between(String fieldName, float from, float to) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.FLOAT);
this.query.between(fd.getColumnIndices(), from, to);
return this;
}
/**
* Between condition.
*
* @param fieldName the field to compare.
* @param from lowest value (inclusive).
* @param to highest value (inclusive).
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> between(String fieldName, Date from, Date to) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.DATE);
this.query.between(fd.getColumnIndices(), from, to);
return this;
}
/**
* Condition that value of field contains the specified substring.
*
* @param fieldName the field to compare.
* @param value the substring.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> contains(String fieldName, String value) {
return contains(fieldName, value, Case.SENSITIVE);
}
/**
* Condition that value of field contains the specified substring.
*
* @param fieldName the field to compare.
* @param value the substring.
* @param casing how to handle casing. Setting this to {@link Case#INSENSITIVE} only works for Latin-1 characters.
* @return The query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> contains(String fieldName, String value, Case casing) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
this.query.contains(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Condition that the value of field begins with the specified string.
*
* @param fieldName the field to compare.
* @param value the string.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> beginsWith(String fieldName, String value) {
return beginsWith(fieldName, value, Case.SENSITIVE);
}
/**
* Condition that the value of field begins with the specified substring.
*
* @param fieldName the field to compare.
* @param value the substring.
* @param casing how to handle casing. Setting this to {@link Case#INSENSITIVE} only works for Latin-1 characters.
* @return the query object
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> beginsWith(String fieldName, String value, Case casing) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
this.query.beginsWith(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Condition that the value of field ends with the specified string.
*
* @param fieldName the field to compare.
* @param value the string.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> endsWith(String fieldName, String value) {
return endsWith(fieldName, value, Case.SENSITIVE);
}
/**
* Condition that the value of field ends with the specified substring.
*
* @param fieldName the field to compare.
* @param value the substring.
* @param casing how to handle casing. Setting this to {@link Case#INSENSITIVE} only works for Latin-1 characters.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> endsWith(String fieldName, String value, Case casing) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
this.query.endsWith(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Condition that the value of field matches with the specified substring, with wildcards:
* <ul>
* <li>'*' matches [0, n] unicode chars</li>
* <li>'?' matches a single unicode char.</li>
* </ul>
*
* @param fieldName the field to compare.
* @param value the wildcard string.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> like(String fieldName, String value) {
return like(fieldName, value, Case.SENSITIVE);
}
/**
* Condition that the value of field matches with the specified substring, with wildcards:
* <ul>
* <li>'*' matches [0, n] unicode chars</li>
* <li>'?' matches a single unicode char.</li>
* </ul>
*
* @param fieldName the field to compare.
* @param value the wildcard string.
* @param casing how to handle casing. Setting this to {@link Case#INSENSITIVE} only works for Latin-1 characters.
* @return the query object.
* @throws java.lang.IllegalArgumentException if one or more arguments do not match class or field type.
*/
public RealmQuery<E> like(String fieldName, String value, Case casing) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING);
this.query.like(fd.getColumnIndices(), fd.getNativeTablePointers(), value, casing);
return this;
}
/**
* Begin grouping of conditions ("left parenthesis"). A group must be closed with a call to {@code endGroup()}.
*
* @return the query object.
* @see #endGroup()
*/
public RealmQuery<E> beginGroup() {
realm.checkIfValid();
return beginGroupWithoutThreadValidation();
}
private RealmQuery<E> beginGroupWithoutThreadValidation() {
this.query.group();
return this;
}
/**
* End grouping of conditions ("right parenthesis") which was opened by a call to {@code beginGroup()}.
*
* @return the query object.
* @see #beginGroup()
*/
public RealmQuery<E> endGroup() {
realm.checkIfValid();
return endGroupWithoutThreadValidation();
}
private RealmQuery<E> endGroupWithoutThreadValidation() {
this.query.endGroup();
return this;
}
/**
* Logical-or two conditions.
*
* @return the query object.
*/
public RealmQuery<E> or() {
realm.checkIfValid();
return orWithoutThreadValidation();
}
private RealmQuery<E> orWithoutThreadValidation() {
this.query.or();
return this;
}
/**
* Negate condition.
*
* @return the query object.
*/
public RealmQuery<E> not() {
realm.checkIfValid();
this.query.not();
return this;
}
/**
* Condition that finds values that are considered "empty" i.e., an empty list, the 0-length string or byte array.
*
* @param fieldName the field to compare.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field name isn't valid or its type isn't either a RealmList,
* String or byte array.
*/
public RealmQuery<E> isEmpty(String fieldName) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING, RealmFieldType.BINARY, RealmFieldType.LIST, RealmFieldType.LINKING_OBJECTS);
this.query.isEmpty(fd.getColumnIndices(), fd.getNativeTablePointers());
return this;
}
/**
* Condition that finds values that are considered "Not-empty" i.e., a list, a string or a byte array with not-empty values.
*
* @param fieldName the field to compare.
* @return the query object.
* @throws java.lang.IllegalArgumentException if the field name isn't valid or its type isn't either a RealmList,
* String or byte array.
*/
public RealmQuery<E> isNotEmpty(String fieldName) {
realm.checkIfValid();
FieldDescriptor fd = schema.getColumnIndices(fieldName, RealmFieldType.STRING, RealmFieldType.BINARY, RealmFieldType.LIST, RealmFieldType.LINKING_OBJECTS);
this.query.isNotEmpty(fd.getColumnIndices(), fd.getNativeTablePointers());
return this;
}
/**
* Returns a distinct set of objects of a specific class. If the result is sorted, the first
* object will be returned in case of multiple occurrences, otherwise it is undefined which
* object is returned.
* <p>
* Adding {@link io.realm.annotations.Index} to the corresponding field will make this operation much faster.
*
* @param fieldName the field name.
* @return a non-null {@link RealmResults} containing the distinct objects.
* @throws IllegalArgumentException if a field is {@code null}, does not exist, is an unsupported type, or points
* to linked fields.
*/
public RealmResults<E> distinct(String fieldName) {
realm.checkIfValid();
SortDescriptor distinctDescriptor = SortDescriptor.getInstanceForDistinct(getSchemaConnector(), query.getTable(), fieldName);
return createRealmResults(query, null, distinctDescriptor, true);
}
/**
* Asynchronously returns a distinct set of objects of a specific class. If the result is
* sorted, the first object will be returned in case of multiple occurrences, otherwise it is
* undefined which object is returned.
* Adding {@link io.realm.annotations.Index} to the corresponding field will make this operation much faster.
*
* @param fieldName the field name.
* @return immediately a {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the
* query completes.
* @throws IllegalArgumentException if a field is {@code null}, does not exist, is an unsupported type, or points
* to linked fields.
*/
public RealmResults<E> distinctAsync(String fieldName) {
realm.checkIfValid();
realm.sharedRealm.capabilities.checkCanDeliverNotification(ASYNC_QUERY_WRONG_THREAD_MESSAGE);
SortDescriptor distinctDescriptor = SortDescriptor.getInstanceForDistinct(getSchemaConnector(), query.getTable(), fieldName);
return createRealmResults(query, null, distinctDescriptor, false);
}
/**
* Returns a distinct set of objects from a specific class. When multiple distinct fields are
* given, all unique combinations of values in the fields will be returned. In case of multiple
* matches, it is undefined which object is returned. Unless the result is sorted, then the
* first object will be returned.
*
* @param firstFieldName first field name to use when finding distinct objects.
* @param remainingFieldNames remaining field names when determining all unique combinations of field values.
* @return a non-null {@link RealmResults} containing the distinct objects.
* @throws IllegalArgumentException if field names is empty or {@code null}, does not exist,
* is an unsupported type, or points to a linked field.
*/
public RealmResults<E> distinct(String firstFieldName, String... remainingFieldNames) {
realm.checkIfValid();
String[] fieldNames = new String[1 + remainingFieldNames.length];
fieldNames[0] = firstFieldName;
System.arraycopy(remainingFieldNames, 0, fieldNames, 1, remainingFieldNames.length);
SortDescriptor distinctDescriptor = SortDescriptor.getInstanceForDistinct(getSchemaConnector(), table, fieldNames);
return createRealmResults(query, null, distinctDescriptor, true);
}
/**
* Calculates the sum of a given field.
*
* @param fieldName the field to sum. Only number fields are supported.
* @return the sum of fields of the matching objects. If no objects exist or they all have {@code null} as the value
* for the given field, {@code 0} will be returned. When computing the sum, objects with {@code null} values
* are ignored.
* @throws java.lang.IllegalArgumentException if the field is not a number type.
*/
public Number sum(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
switch (table.getColumnType(columnIndex)) {
case INTEGER:
return query.sumInt(columnIndex);
case FLOAT:
return query.sumFloat(columnIndex);
case DOUBLE:
return query.sumDouble(columnIndex);
default:
throw new IllegalArgumentException(String.format(TYPE_MISMATCH, fieldName, "int, float or double"));
}
}
/**
* Returns the average of a given field.
* Does not support dotted field notation.
*
* @param fieldName the field to calculate average on. Only number fields are supported.
* @return the average for the given field amongst objects in query results. This will be of type double for all
* types of number fields. If no objects exist or they all have {@code null} as the value for the given field,
* {@code 0} will be returned. When computing the average, objects with {@code null} values are ignored.
* @throws java.lang.IllegalArgumentException if the field is not a number type.
*/
public double average(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
switch (table.getColumnType(columnIndex)) {
case INTEGER:
return query.averageInt(columnIndex);
case DOUBLE:
return query.averageDouble(columnIndex);
case FLOAT:
return query.averageFloat(columnIndex);
default:
throw new IllegalArgumentException(String.format(TYPE_MISMATCH, fieldName, "int, float or double"));
}
}
/**
* Finds the minimum value of a field.
*
* @param fieldName the field to look for a minimum on. Only number fields are supported.
* @return if no objects exist or they all have {@code null} as the value for the given field, {@code null} will be
* returned. Otherwise the minimum value is returned. When determining the minimum value, objects with {@code null}
* values are ignored.
* @throws java.lang.IllegalArgumentException if the field is not a number type.
*/
public Number min(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
switch (table.getColumnType(columnIndex)) {
case INTEGER:
return this.query.minimumInt(columnIndex);
case FLOAT:
return this.query.minimumFloat(columnIndex);
case DOUBLE:
return this.query.minimumDouble(columnIndex);
default:
throw new IllegalArgumentException(String.format(TYPE_MISMATCH, fieldName, "int, float or double"));
}
}
/**
* Finds the minimum value of a field.
*
* @param fieldName the field name
* @return if no objects exist or they all have {@code null} as the value for the given date field, {@code null}
* will be returned. Otherwise the minimum date is returned. When determining the minimum date, objects with
* {@code null} values are ignored.
* @throws java.lang.UnsupportedOperationException if the query is not valid ("syntax error").
*/
public Date minimumDate(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
return this.query.minimumDate(columnIndex);
}
/**
* Finds the maximum value of a field.
*
* @param fieldName the field to look for a maximum on. Only number fields are supported.
* @return if no objects exist or they all have {@code null} as the value for the given field, {@code null} will be
* returned. Otherwise the maximum value is returned. When determining the maximum value, objects with {@code null}
* values are ignored.
* @throws java.lang.IllegalArgumentException if the field is not a number type.
*/
public Number max(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
switch (table.getColumnType(columnIndex)) {
case INTEGER:
return this.query.maximumInt(columnIndex);
case FLOAT:
return this.query.maximumFloat(columnIndex);
case DOUBLE:
return this.query.maximumDouble(columnIndex);
default:
throw new IllegalArgumentException(String.format(TYPE_MISMATCH, fieldName, "int, float or double"));
}
}
/**
* Finds the maximum value of a field.
*
* @param fieldName the field name.
* @return if no objects exist or they all have {@code null} as the value for the given date field, {@code null}
* will be returned. Otherwise the maximum date is returned. When determining the maximum date, objects with
* {@code null} values are ignored.
* @throws java.lang.UnsupportedOperationException if the query is not valid ("syntax error").
*/
public Date maximumDate(String fieldName) {
realm.checkIfValid();
long columnIndex = schema.getAndCheckFieldIndex(fieldName);
return this.query.maximumDate(columnIndex);
}
/**
* Counts the number of objects that fulfill the query conditions.
*
* @return the number of matching objects.
* @throws java.lang.UnsupportedOperationException if the query is not valid ("syntax error").
*/
public long count() {
realm.checkIfValid();
return this.query.count();
}
/**
* Finds all objects that fulfill the query conditions.
*
* @return a {@link io.realm.RealmResults} containing objects. If no objects match the condition, a list with zero
* objects is returned.
* @see io.realm.RealmResults
*/
@SuppressWarnings("unchecked")
public RealmResults<E> findAll() {
realm.checkIfValid();
return createRealmResults(query, null, null, true);
}
/**
* Finds all objects that fulfill the query conditions and sorted by specific field name.
* This method is only available from a Looper thread.
*
* @return immediately an empty {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the query completes.
* @see io.realm.RealmResults
*/
public RealmResults<E> findAllAsync() {
realm.checkIfValid();
realm.sharedRealm.capabilities.checkCanDeliverNotification(ASYNC_QUERY_WRONG_THREAD_MESSAGE);
return createRealmResults(query, null, null, false);
}
/**
* Finds all objects that fulfill the query conditions and sorted by specific field name.
* <p>
* Sorting is currently limited to character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended A',
* 'Latin Extended B' (UTF-8 range 0-591). For other character sets, sorting will have no effect.
*
* @param fieldName the field name to sort by.
* @param sortOrder how to sort the results.
* @return a {@link io.realm.RealmResults} containing objects. If no objects match the condition, a list with zero
* objects is returned.
* @throws java.lang.IllegalArgumentException if field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
@SuppressWarnings("unchecked")
public RealmResults<E> findAllSorted(String fieldName, Sort sortOrder) {
realm.checkIfValid();
SortDescriptor sortDescriptor = SortDescriptor.getInstanceForSort(getSchemaConnector(), query.getTable(), fieldName, sortOrder);
return createRealmResults(query, sortDescriptor, null, true);
}
/**
* Similar to {@link #findAllSorted(String, Sort)} but runs asynchronously on a worker thread
* (need a Realm opened from a looper thread to work).
*
* @return immediately an empty {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the query completes.
* @throws java.lang.IllegalArgumentException if field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSortedAsync(final String fieldName, final Sort sortOrder) {
realm.checkIfValid();
realm.sharedRealm.capabilities.checkCanDeliverNotification(ASYNC_QUERY_WRONG_THREAD_MESSAGE);
SortDescriptor sortDescriptor = SortDescriptor.getInstanceForSort(getSchemaConnector(), query.getTable(), fieldName, sortOrder);
return createRealmResults(query, sortDescriptor, null, false);
}
/**
* Finds all objects that fulfill the query conditions and sorted by specific field name in ascending order.
* <p>
* Sorting is currently limited to character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended A',
* 'Latin Extended B' (UTF-8 range 0-591). For other character sets, sorting will have no effect.
*
* @param fieldName the field name to sort by.
* @return a {@link io.realm.RealmResults} containing objects. If no objects match the condition, a list with zero
* objects is returned.
* @throws java.lang.IllegalArgumentException if the field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSorted(String fieldName) {
return findAllSorted(fieldName, Sort.ASCENDING);
}
/**
* Similar to {@link #findAllSorted(String)} but runs asynchronously on a worker thread.
* This method is only available from a Looper thread.
*
* @return immediately an empty {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the query completes.
* @throws java.lang.IllegalArgumentException if the field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSortedAsync(String fieldName) {
return findAllSortedAsync(fieldName, Sort.ASCENDING);
}
/**
* Finds all objects that fulfill the query conditions and sorted by specific field names.
* <p>
* Sorting is currently limited to character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended A',
* 'Latin Extended B' (UTF-8 range 0-591). For other character sets, sorting will have no effect.
*
* @param fieldNames an array of field names to sort by.
* @param sortOrders how to sort the field names.
* @return a {@link io.realm.RealmResults} containing objects. If no objects match the condition, a list with zero
* objects is returned.
* @throws java.lang.IllegalArgumentException if one of the field names does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSorted(String[] fieldNames, Sort[] sortOrders) {
realm.checkIfValid();
SortDescriptor sortDescriptor = SortDescriptor.getInstanceForSort(getSchemaConnector(), query.getTable(), fieldNames, sortOrders);
return createRealmResults(query, sortDescriptor, null, true);
}
private boolean isDynamicQuery() {
return className != null;
}
/**
* Similar to {@link #findAllSorted(String[], Sort[])} but runs asynchronously.
* from a worker thread.
* This method is only available from a Looper thread.
*
* @return immediately an empty {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the query completes.
* @throws java.lang.IllegalArgumentException if one of the field names does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
* @see io.realm.RealmResults
*/
public RealmResults<E> findAllSortedAsync(String[] fieldNames, final Sort[] sortOrders) {
realm.checkIfValid();
realm.sharedRealm.capabilities.checkCanDeliverNotification(ASYNC_QUERY_WRONG_THREAD_MESSAGE);
SortDescriptor sortDescriptor = SortDescriptor.getInstanceForSort(getSchemaConnector(), query.getTable(), fieldNames, sortOrders);
return createRealmResults(query, sortDescriptor, null, false);
}
/**
* Finds all objects that fulfill the query conditions and sorted by specific field names in ascending order.
* <p>
* Sorting is currently limited to character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended A',
* 'Latin Extended B' (UTF-8 range 0-591). For other character sets, sorting will have no effect.
*
* @param fieldName1 first field name
* @param sortOrder1 sort order for first field
* @param fieldName2 second field name
* @param sortOrder2 sort order for second field
* @return a {@link io.realm.RealmResults} containing objects. If no objects match the condition, a list with zero
* objects is returned.
* @throws java.lang.IllegalArgumentException if a field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSorted(String fieldName1, Sort sortOrder1,
String fieldName2, Sort sortOrder2) {
return findAllSorted(new String[] {fieldName1, fieldName2}, new Sort[] {sortOrder1, sortOrder2});
}
/**
* Similar to {@link #findAllSorted(String, Sort, String, Sort)} but runs asynchronously on a worker thread
* This method is only available from a Looper thread.
*
* @return immediately an empty {@link RealmResults}. Users need to register a listener
* {@link io.realm.RealmResults#addChangeListener(RealmChangeListener)} to be notified when the query completes.
* @throws java.lang.IllegalArgumentException if a field name does not exist or it belongs to a child
* {@link RealmObject} or a child {@link RealmList}.
*/
public RealmResults<E> findAllSortedAsync(String fieldName1, Sort sortOrder1,
String fieldName2, Sort sortOrder2) {
return findAllSortedAsync(new String[] {fieldName1, fieldName2}, new Sort[] {sortOrder1, sortOrder2});
}
/**
* Finds the first object that fulfills the query conditions.
*
* @return the object found or {@code null} if no object matches the query conditions.
* @see io.realm.RealmObject
*/
public E findFirst() {
realm.checkIfValid();
long tableRowIndex = getSourceRowIndexForFirstObject();
return (tableRowIndex < 0) ? null : realm.get(clazz, className, tableRowIndex);
}
/**
* Similar to {@link #findFirst()} but runs asynchronously on a worker thread. An listener should be registered to
* the returned {@link RealmObject} to get the notification when query completes. The registered listener will also
* be triggered if there are changes made to the queried {@link RealmObject}. If the {@link RealmObject} is deleted,
* the listener will be called one last time and then stop. The query will not be re-run.
*
* @return immediately an empty {@link RealmObject} with {@code isLoaded() == false}. Trying to access any field on
* the returned object before it is loaded will throw an {@code IllegalStateException}.
* @throws IllegalStateException if this is called on a non-looper thread.
*/
public E findFirstAsync() {
realm.checkIfValid();
realm.sharedRealm.capabilities.checkCanDeliverNotification(ASYNC_QUERY_WRONG_THREAD_MESSAGE);
Row row;
if (realm.isInTransaction()) {
// It is not possible to create async query inside a transaction. So immediately query the first object.
// See OS Results::prepare_async()
row = new Collection(realm.sharedRealm, query).firstUncheckedRow();
} else {
// prepares an empty reference of the RealmObject which is backed by a pending query,
// then update it once the query complete in the background.
// TODO: The performance by the pending query will be a little bit worse than directly calling core's
// Query.find(). The overhead comes with core needs to add all the row indices to the vector. However this
// can be optimized by adding support of limit in OS's Results which is supported by core already.
row = new PendingRow(realm.sharedRealm, query, null, isDynamicQuery());
}
final E result;
if (isDynamicQuery()) {
//noinspection unchecked
result = (E) new DynamicRealmObject(realm, row);
} else {
result = realm.getConfiguration().getSchemaMediator().newInstance(
clazz, realm, row, realm.getSchema().getColumnInfo(clazz),
false, Collections.<String>emptyList());
}
if (row instanceof PendingRow) {
final RealmObjectProxy proxy = (RealmObjectProxy) result;
((PendingRow) row).setFrontEnd(proxy.realmGet$proxyState());
}
return result;
}
private RealmResults<E> createRealmResults(TableQuery query,
SortDescriptor sortDescriptor,
SortDescriptor distinctDescriptor,
boolean loadResults) {
RealmResults<E> results;
Collection collection = new Collection(realm.sharedRealm, query, sortDescriptor, distinctDescriptor);
if (isDynamicQuery()) {
results = new RealmResults<>(realm, collection, className);
} else {
results = new RealmResults<>(realm, collection, clazz);
}
if (loadResults) {
results.load();
}
return results;
}
private long getSourceRowIndexForFirstObject() {
return this.query.find();
}
private SchemaConnector getSchemaConnector() {
return new SchemaConnector(realm.getSchema());
}
}