use of com.google.firebase.firestore.core.Bound in project firebase-android-sdk by firebase.
the class Query method boundFromDocumentSnapshot.
/**
* Create a Bound from a query given the document.
*
* <p>Note that the Bound will always include the key of the document and so only the provided
* document will compare equal to the returned position.
*
* <p>Will throw if the document does not contain all fields of the order by of the query or if
* any of the fields in the order by are an uncommitted server timestamp.
*/
private Bound boundFromDocumentSnapshot(String methodName, DocumentSnapshot snapshot, boolean inclusive) {
checkNotNull(snapshot, "Provided snapshot must not be null.");
if (!snapshot.exists()) {
throw new IllegalArgumentException("Can't use a DocumentSnapshot for a document that doesn't exist for " + methodName + "().");
}
Document document = snapshot.getDocument();
List<Value> components = new ArrayList<>();
// orders), multiple documents could match the position, yielding duplicate results.
for (OrderBy orderBy : query.getOrderBy()) {
if (orderBy.getField().equals(com.google.firebase.firestore.model.FieldPath.KEY_PATH)) {
components.add(Values.refValue(firestore.getDatabaseId(), document.getKey()));
} else {
Value value = document.getField(orderBy.getField());
if (ServerTimestamps.isServerTimestamp(value)) {
throw new IllegalArgumentException("Invalid query. You are trying to start or end a query using a document for which " + "the field '" + orderBy.getField() + "' is an uncommitted server timestamp. (Since the value of this field is " + "unknown, you cannot start/end a query with it.)");
} else if (value != null) {
components.add(value);
} else {
throw new IllegalArgumentException("Invalid query. You are trying to start or end a query using a document for which " + "the field '" + orderBy.getField() + "' (used as the orderBy) does not exist.");
}
}
}
return new Bound(components, inclusive);
}
use of com.google.firebase.firestore.core.Bound in project firebase-android-sdk by firebase.
the class Query method boundFromFields.
/**
* Converts a list of field values to Bound.
*/
private Bound boundFromFields(String methodName, Object[] values, boolean inclusive) {
// Use explicit order by's because it has to match the query the user made
List<OrderBy> explicitOrderBy = query.getExplicitOrderBy();
if (values.length > explicitOrderBy.size()) {
throw new IllegalArgumentException("Too many arguments provided to " + methodName + "(). The number of arguments must be less " + "than or equal to the number of orderBy() clauses.");
}
List<Value> components = new ArrayList<>();
for (int i = 0; i < values.length; i++) {
Object rawValue = values[i];
OrderBy orderBy = explicitOrderBy.get(i);
if (orderBy.getField().equals(com.google.firebase.firestore.model.FieldPath.KEY_PATH)) {
if (!(rawValue instanceof String)) {
throw new IllegalArgumentException("Invalid query. Expected a string for document ID in " + methodName + "(), but got " + rawValue + ".");
}
String documentId = (String) rawValue;
if (!query.isCollectionGroupQuery() && documentId.contains("/")) {
throw new IllegalArgumentException("Invalid query. When querying a collection and ordering by FieldPath.documentId(), " + "the value passed to " + methodName + "() must be a plain document ID, but '" + documentId + "' contains a slash.");
}
ResourcePath path = query.getPath().append(ResourcePath.fromString(documentId));
if (!DocumentKey.isDocumentKey(path)) {
throw new IllegalArgumentException("Invalid query. When querying a collection group and ordering by " + "FieldPath.documentId(), the value passed to " + methodName + "() must result in a valid document path, but '" + path + "' is not because it contains an odd number of segments.");
}
DocumentKey key = DocumentKey.fromPath(path);
components.add(Values.refValue(firestore.getDatabaseId(), key));
} else {
Value wrapped = firestore.getUserDataReader().parseQueryValue(rawValue);
components.add(wrapped);
}
}
return new Bound(components, inclusive);
}
use of com.google.firebase.firestore.core.Bound in project firebase-android-sdk by firebase.
the class RemoteSerializer method decodeQueryTarget.
public com.google.firebase.firestore.core.Target decodeQueryTarget(String parent, StructuredQuery query) {
ResourcePath path = decodeQueryPath(parent);
String collectionGroup = null;
int fromCount = query.getFromCount();
if (fromCount > 0) {
hardAssert(fromCount == 1, "StructuredQuery.from with more than one collection is not supported.");
CollectionSelector from = query.getFrom(0);
if (from.getAllDescendants()) {
collectionGroup = from.getCollectionId();
} else {
path = path.append(from.getCollectionId());
}
}
List<Filter> filterBy;
if (query.hasWhere()) {
filterBy = decodeFilters(query.getWhere());
} else {
filterBy = Collections.emptyList();
}
List<OrderBy> orderBy;
int orderByCount = query.getOrderByCount();
if (orderByCount > 0) {
orderBy = new ArrayList<>(orderByCount);
for (int i = 0; i < orderByCount; i++) {
orderBy.add(decodeOrderBy(query.getOrderBy(i)));
}
} else {
orderBy = Collections.emptyList();
}
long limit = com.google.firebase.firestore.core.Target.NO_LIMIT;
if (query.hasLimit()) {
limit = query.getLimit().getValue();
}
Bound startAt = null;
if (query.hasStartAt()) {
startAt = new Bound(query.getStartAt().getValuesList(), query.getStartAt().getBefore());
}
Bound endAt = null;
if (query.hasEndAt()) {
endAt = new Bound(query.getEndAt().getValuesList(), !query.getEndAt().getBefore());
}
return new com.google.firebase.firestore.core.Target(path, collectionGroup, filterBy, orderBy, limit, startAt, endAt);
}
use of com.google.firebase.firestore.core.Bound in project firebase-android-sdk by firebase.
the class BundleSerializer method decodeBundledQuery.
private BundledQuery decodeBundledQuery(JSONObject bundledQuery) throws JSONException {
JSONObject structuredQuery = bundledQuery.getJSONObject("structuredQuery");
verifyNoSelect(structuredQuery);
ResourcePath parent = decodeName(bundledQuery.getString("parent"));
JSONArray from = structuredQuery.getJSONArray("from");
verifyCollectionSelector(from);
JSONObject collectionSelector = from.getJSONObject(0);
boolean allDescendants = collectionSelector.optBoolean("allDescendants", false);
@Nullable String collectionGroup = null;
if (allDescendants) {
collectionGroup = collectionSelector.getString("collectionId");
} else {
parent = parent.append(collectionSelector.getString("collectionId"));
}
List<Filter> filters = decodeWhere(structuredQuery.optJSONObject("where"));
List<OrderBy> orderBys = decodeOrderBy(structuredQuery.optJSONArray("orderBy"));
@Nullable Bound startAt = decodeStartAtBound(structuredQuery.optJSONObject("startAt"));
@Nullable Bound endAt = decodeEndAtBound(structuredQuery.optJSONObject("endAt"));
verifyNoOffset(structuredQuery);
int limit = decodeLimit(structuredQuery);
Query.LimitType limitType = decodeLimitType(bundledQuery);
return new BundledQuery(new Target(parent, collectionGroup, filters, orderBys, limit, startAt, endAt), limitType);
}
use of com.google.firebase.firestore.core.Bound in project firebase-android-sdk by firebase.
the class SQLiteIndexManager method getDocumentsMatchingTarget.
@Override
public Set<DocumentKey> getDocumentsMatchingTarget(Target target) {
hardAssert(started, "IndexManager not started");
List<String> subQueries = new ArrayList<>();
List<Object> bindings = new ArrayList<>();
for (Target subTarget : getSubTargets(target)) {
FieldIndex fieldIndex = getFieldIndex(subTarget);
if (fieldIndex == null) {
return null;
}
@Nullable List<Value> arrayValues = subTarget.getArrayValues(fieldIndex);
@Nullable Collection<Value> notInValues = subTarget.getNotInValues(fieldIndex);
@Nullable Bound lowerBound = subTarget.getLowerBound(fieldIndex);
@Nullable Bound upperBound = subTarget.getUpperBound(fieldIndex);
if (Logger.isDebugEnabled()) {
Logger.debug(TAG, "Using index '%s' to execute '%s' (Arrays: %s, Lower bound: %s, Upper bound: %s)", fieldIndex, subTarget, arrayValues, lowerBound, upperBound);
}
Object[] lowerBoundEncoded = encodeBound(fieldIndex, subTarget, lowerBound);
String lowerBoundOp = lowerBound != null && lowerBound.isInclusive() ? ">=" : ">";
Object[] upperBoundEncoded = encodeBound(fieldIndex, subTarget, upperBound);
String upperBoundOp = upperBound != null && upperBound.isInclusive() ? "<=" : "<";
Object[] notInEncoded = encodeValues(fieldIndex, subTarget, notInValues);
Object[] subQueryAndBindings = generateQueryAndBindings(subTarget, fieldIndex.getIndexId(), arrayValues, lowerBoundEncoded, lowerBoundOp, upperBoundEncoded, upperBoundOp, notInEncoded);
subQueries.add(String.valueOf(subQueryAndBindings[0]));
bindings.addAll(Arrays.asList(subQueryAndBindings).subList(1, subQueryAndBindings.length));
}
String queryString;
if (subQueries.size() == 1) {
// If there's only one subQuery, just execute the one subQuery.
queryString = subQueries.get(0);
} else {
// Construct "SELECT * FROM (subQuery1 UNION subQuery2 UNION ...) LIMIT N"
queryString = "SELECT * FROM (" + TextUtils.join(" UNION ", subQueries) + ")";
if (target.getLimit() != -1) {
queryString = queryString + " LIMIT " + target.getLimit();
}
}
hardAssert(bindings.size() < 1000, "Cannot perform query with more than 999 bind elements");
SQLitePersistence.Query query = db.query(queryString).binding(bindings.toArray());
Set<DocumentKey> result = new HashSet<>();
query.forEach(row -> result.add(DocumentKey.fromPath(ResourcePath.fromString(row.getString(0)))));
Logger.debug(TAG, "Index scan returned %s documents", result.size());
return result;
}
Aggregations