Skip to content

Fix bug in count queries retrieving object data #678

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions Parse/src/main/java/com/parse/ParseRESTQueryCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@

/** package */ class ParseRESTQueryCommand extends ParseRESTCommand {

/* package */ final static String KEY_ORDER = "order";
/* package */ final static String KEY_WHERE = "where";
/* package */ final static String KEY_KEYS = "keys";
/* package */ final static String KEY_INCLUDE = "include";
/* package */ final static String KEY_LIMIT = "limit";
/* package */ final static String KEY_COUNT = "count";
/* package */ final static String KEY_SKIP = "skip";
/* package */ final static String KEY_TRACE = "trace";

public static <T extends ParseObject> ParseRESTQueryCommand findCommand(
ParseQuery.State<T> state, String sessionToken) {
String httpPath = String.format("classes/%s", state.className());
Expand All @@ -41,38 +50,41 @@ public static <T extends ParseObject> ParseRESTQueryCommand countCommand(
HashMap<String, String> parameters = new HashMap<>();
List<String> order = state.order();
if (!order.isEmpty()) {
parameters.put("order", ParseTextUtils.join(",", order));
parameters.put(KEY_ORDER, ParseTextUtils.join(",", order));
}

ParseQuery.QueryConstraints conditions = state.constraints();
if (!conditions.isEmpty()) {
JSONObject encodedConditions =
(JSONObject) encoder.encode(conditions);
parameters.put("where", encodedConditions.toString());
parameters.put(KEY_WHERE, encodedConditions.toString());
}

// This is nullable since we allow unset selectedKeys as well as no selectedKeys
Set<String> selectedKeys = state.selectedKeys();
if (selectedKeys != null) {
parameters.put("keys", ParseTextUtils.join(",", selectedKeys));
parameters.put(KEY_KEYS, ParseTextUtils.join(",", selectedKeys));
}

Set<String> includeds = state.includes();
if (!includeds.isEmpty()) {
parameters.put("include", ParseTextUtils.join(",", includeds));
parameters.put(KEY_INCLUDE, ParseTextUtils.join(",", includeds));
}

// Respect what the caller wanted for limit, even if count is true, because
// parse-server supports it. Currently with our APIs, when counting, limit will always be 0,
// but that logic is in ParseQuery class and we should not do that again here.
int limit = state.limit();
if (limit >= 0) {
parameters.put(KEY_LIMIT, Integer.toString(limit));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be moved back to original logic.
If count is true, always set limit to 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your link is a good reference because it says

With a nonzero limit, that request would return results as well as the count.

So parse-server supports counting and finding altogether. We don’t currently support this with our exposed APIs, but we might want one day in the future.
So since parse-server supports it and ParseRESTQueryCommand is a low level interface to reach the API, I think it should allow any limit.
The important thing is that any call to ParseQuery.count() will use limit = 0. And that is already true https://github.com/parse-community/Parse-SDK-Android/blob/master/Parse/src/main/java/com/parse/ParseQuery.java#L1300 :

final State<T> state = copy.setLimit(0).build();

So the logic if (count) limit = 0 is already in the count() methods. The issue was that ParseRESTQueryCommand was ignoring limit = 0 imposed by ParseQuery.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@natario1 Thanks for your explanation and I agree your opinion. Moving this logic out would be the minimum modification to fix this issue.


if (count) {
parameters.put("count", Integer.toString(1));
parameters.put(KEY_COUNT, Integer.toString(1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default limit is -1 if not set.
https://github.com/parse-community/Parse-SDK-Android/blob/master/Parse/src/main/java/com/parse/ParseQuery.java#L347
So, if there is no limit in the query (will be assigned -1), then there should be a logic that sets limit to 0?
parameters.put(KEY_LIMIT, Integer.toString(0));

Copy link
Contributor

@hermanliang hermanliang Jun 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we don't need to fetch any objects when counting, right?
So, the the limit should always be set to 0 here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. But with our exposed APIs in ParseQuery limit is already 0 for count actions https://github.com/parse-community/Parse-SDK-Android/blob/master/Parse/src/main/java/com/parse/ParseQuery.java#L1300 see other comment

} else {
int limit = state.limit();
if (limit >= 0) {
parameters.put("limit", Integer.toString(limit));
}

int skip = state.skip();
if (skip > 0) {
parameters.put("skip", Integer.toString(skip));
parameters.put(KEY_SKIP, Integer.toString(skip));
}
}

Expand All @@ -83,7 +95,7 @@ public static <T extends ParseObject> ParseRESTQueryCommand countCommand(
}

if (state.isTracingEnabled()) {
parameters.put("trace", Integer.toString(1));
parameters.put(KEY_TRACE, Integer.toString(1));
}
return parameters;
}
Expand Down
25 changes: 13 additions & 12 deletions Parse/src/test/java/com/parse/ParseRESTQueryCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,36 +56,37 @@ public void testEncodeWithNoCount() throws Exception {

Map<String, String> encoded = ParseRESTQueryCommand.encode(state, false);

assertEquals("orderKey", encoded.get("order"));
JSONObject conditionJson = new JSONObject(encoded.get("where"));
assertEquals("orderKey", encoded.get(ParseRESTQueryCommand.KEY_ORDER));
JSONObject conditionJson = new JSONObject(encoded.get(ParseRESTQueryCommand.KEY_WHERE));
JSONArray conditionWhereJsonArray = new JSONArray()
.put("inValue")
.put("inValueAgain");
assertEquals(
conditionWhereJsonArray,
conditionJson.getJSONObject("inKey").getJSONArray("$in"),
JSONCompareMode.NON_EXTENSIBLE);
assertTrue(encoded.get("keys").contains("selectedKey"));
assertTrue(encoded.get("keys").contains("selectedKeyAgain"));
assertEquals("includeKey", encoded.get("include"));
assertEquals("5", encoded.get("limit"));
assertEquals("6", encoded.get("skip"));
assertTrue(encoded.get(ParseRESTQueryCommand.KEY_KEYS).contains("selectedKey"));
assertTrue(encoded.get(ParseRESTQueryCommand.KEY_KEYS).contains("selectedKeyAgain"));
assertEquals("includeKey", encoded.get(ParseRESTQueryCommand.KEY_INCLUDE));
assertEquals("5", encoded.get(ParseRESTQueryCommand.KEY_LIMIT));
assertEquals("6", encoded.get(ParseRESTQueryCommand.KEY_SKIP));
assertEquals("extraKey", encoded.get("redirectClassNameForKey"));
assertEquals("1", encoded.get("trace"));
assertEquals("1", encoded.get(ParseRESTQueryCommand.KEY_TRACE));
}

@Test
public void testEncodeWithCount() throws Exception {
ParseQuery.State<ParseObject> state = new ParseQuery.State.Builder<>("TestObject")
.setLimit(5)
.setSkip(6)
.setLimit(3)
.build();

Map<String, String> encoded = ParseRESTQueryCommand.encode(state, true);

assertFalse(encoded.containsKey("limit"));
assertFalse(encoded.containsKey("skip"));
assertEquals("1", encoded.get("count"));
// Limit should not be stripped out from count queries
assertTrue(encoded.containsKey(ParseRESTQueryCommand.KEY_LIMIT));
assertFalse(encoded.containsKey(ParseRESTQueryCommand.KEY_SKIP));
assertEquals("1", encoded.get(ParseRESTQueryCommand.KEY_COUNT));
}

//endregion
Expand Down