2015年6月21日 星期日

[擒猿筆記篇] Android 利用 Parse Cloud Server (二) ParseQuery

上一篇 [擒猿筆記篇] Android 利用 Parse Cloud Server (一) ParseObject 介紹了 ParseObject 的用法,但是目前一次只能上傳或抓取一筆資料,那如果要同時處理多筆資料要怎麼辦?此時就得用到 ParseQuery 了。

基本用法

以下作法可以從雲端資料庫中取回所有 ParseObject 的 List
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.findInBackground(new FindCallback<ParseObject>() {
    public void done(List<ParseObject> scoreList, ParseException e) {
        if (e == null) {
            Log.d("score", "Retrieved " + scoreList.size() + " scores");
        } else {
            Log.d("score", "Error: " + e.getMessage());
        }
    }
});

條件篩選

可以在 findInBackground 之前對 ParseQuery 物件加上篩選條件,如:
query.whereEqualTo("playerName", "Dan Stemkoski");
也可以限制回傳的結果筆數,如:
query.setLimit(10); // limit to at most 10 results
若只需要一筆資料,可以使用 getFirst() 或 getFirstBackground(),如:
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerEmail", "dstemkoski@example.com");
query.getFirstInBackground(new GetCallback<ParseObject>() {
  public void done(ParseObject object, ParseException e) {
    if (object == null) {
      Log.d("score", "The getFirst request failed.");
    } else {
      Log.d("score", "Retrieved the object.");
    }
  }
});
也可以用 setSkip() 來忽略查詢結果的前幾筆資料:
query.setSkip(10); // skip the first 10 results
也可以設定依據某個欄位的值來做排序,如:
// Sorts the results in ascending order by the score field
query.orderByAscending("score");

// Sorts the results in descending order by the score field
query.orderByDescending("score");
承上,還可以增加複合欄位排序篩選,如:
// Sorts the results in ascending order by the score field
// if the previous sort keys are equal.
query.addAscendingOrder("playerAge");

// Sorts the results in descending order by the score field
// if the previous sort keys are equal.
query.addDescendingOrder("playerAge");
也可以有邏輯條件判斷式,如:
// Restricts to wins < 50
query.whereLessThan("wins", 50);

// Restricts to wins <= 50
query.whereLessThanOrEqualTo("wins", 50);

// Restricts to wins > 50
query.whereGreaterThan("wins", 50);

// Restricts to wins >= 50
query.whereGreaterThanOrEqualTo("wins", 50);
如果要篩選出符合或不符合多組關鍵字的資料,可以採取以下寫法:
String[] names = {"Jonathan Walsh", "Dario Wunsch", "Shawn Simon"};

query.whereContainedIn("playerName", Arrays.asList(names));

query.whereNotContainedIn("playerName", Arrays.asList(names));
如果要篩選有無特定欄位的資料,可以採取以下寫法:
// Finds objects that have the score set
query.whereExists("score");

// Finds objects that don't have the score set
query.whereDoesNotExist("score");
跨 Table 做複合篩選也是行地,以下寫法可以對從第一個 Table 取得的資料再做一次篩選:
ParseQuery<ParseQuery> teamQuery = ParseQuery.getQuery("Team");
teamQuery.whereGreaterThan("winPct", 0.5);
ParseQuery<ParseQuery> userQuery = ParseQuery.getQuery("_User");
userQuery.whereMatchesKeyInQuery("hometown", "city", teamQuery);
userQuery.findInBackground(new FindCallback<ParseUser>() {
  void done(List<ParseUser> results, ParseException e) {
    // results has the list of users with a hometown team with a winning record
  }
});
承上,也可以取得與上例相反的資料:
ParseQuery<ParseQuery> losingUserQuery = ParseQuery.getQuery("_User");
losingUserQuery.whereDoesNotMatchKeyInQuery("hometown", "city", teamQuery);
losingUserQuery.findInBackground(new FindCallback<ParseUser>() {
  void done(List<ParseUser> results, ParseException e) {
    // results has the list of users with a hometown team with a losing record
  }
});
此外,也可以只篩選特定欄位的資料,如以下寫法:
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.selectKeys(Arrays.asList("playerName", "score"));;
List<ParseObject> results = query.find();
其餘欄位可以用以下方法,需要時再抓取:
ParseObject object = results.get(0);
object.fetchIfNeededInBackground(new GetCallback<ParseObject>() {
  public void done(ParseObject object, ParseException e) {
    // all fields of the object will now be available here.
  }
});

篩選  Array Values

假設雲端資料庫上有一個欄位叫做 "arrayKey" 儲存的值為 Array Values,可透過以下方式來取得 arrayKey 中含有特定數值的資料:
// Find objects where the array in arrayKey contains the number 2.
query.whereEqualTo("arrayKey", 2);
進階用法如下,可一次篩選出含有多筆條件的資料:
// Find objects where the array in arrayKey contains all of the numbers 2, 3, and 4.
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(2);
numbers.add(3);
numbers.add(4);
query.whereContainsAll("arrayKey", numbers);

篩選 String Values

透過以下方式可篩選出指定欄位中開頭有特定字串的資料:
// Finds barbecue sauces that start with 'Big Daddy's'.
ParseQuery<ParseObject> query = ParseQuery.getQuery("BarbecueSauce");
query.whereStartsWith("name", "Big Daddy's");

關聯式篩選

以下寫法可以將另一組 ParseObject 當成篩選條件,篩選出跟這個 ParseObject 有關的:
// Assume ParseObject myPost was previously created.
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.whereEqualTo("post", myPost);

query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> commentList, ParseException e) {
    // commentList now has the comments for myPost
  }
});
以下寫法可以從前一次篩選出的結果中做關聯式篩選的動作:
ParseQuery<ParseObject> innerQuery = ParseQuery.getQuery("Post");
innerQuery.whereExists("image");

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.whereMatchesQuery("post", innerQuery);

query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> commentList, ParseException e) {
    // comments now contains the comments for posts with images.
  }
});
承上,以下寫法可以取得與上相反的資料:
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.whereDoesNotMatchQuery("post", innerQuery);

query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> commentList, ParseException e) {
    // comments now contains the comments for posts without images.
  }
});
此外,可以在篩選時偷渡其他 Table 的資料 XD
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");

// Retrieve the most recent ones
query.orderByDescending("createdAt");

// Only retrieve the last ten
query.setLimit(10);

// Include the post data with each comment
query.include("post");

query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> commentList, ParseException e) {
    // commentList now contains the last ten comments, and the "post"
    // field has been populated. For example:
    for (ParseObject comment : commentList) {
      // This does not require a network access.
      ParseObject post = comment.getParseObject("post");
      Log.d("post", "retrieved a related post");
    }
  }
});

從 Local DataStore 篩選資料

若有開啟 Local DataStore 的話,也可以用以下方式從 Local DataStore 篩選資料出來:
query.fromLocalDatastore();
query.findInBackground(new FindCallback<ParseObject>() {
  public void done(final List<ParseObject> scoreList, ParseException e) {
    if (e == null) {
      // Results were successfully found from the local datastore.
    } else {
      // There was an error.
    }
  }
});
篩選方式跟先前一模一樣,篩選結果會包含所有被 pin 過的 ParseObject。


將篩選結果做快取

可以將篩選的結果做快取,儲存在 Lcoal Datastore,如此一來可以加快存取速度,如下寫法:
final String TOP_SCORES_LABEL = "topScores";

// Query for the latest objects from Parse.
query.findInBackground(new FindCallback<ParseObject>() {
  public void done(final List<ParseObject> scoreList, ParseException e) {
    if (e != null) {
      // There was an error or the network wasn't available.
      return;
    }

    // Release any objects previously pinned for this query.
    ParseObject.unpinAllInBackground(TOP_SCORES_LABEL, scoreList, new DeleteCallback() {
      public void done(ParseException e) {
        if (e != null) {
          // There was some error.
          return;
        }

        // Add the latest results for this query to the cache.
        ParseObject.pinAllInBackground(TOP_SCORES_LABEL, scoreList);
      }
    });
  }
});
如果沒有開啟 Local DataStore 也沒關係,還有以下方式可以做快取:
query.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE);
query.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> scoreList, ParseException e) {
    if (e == null) {
      // Results were successfully found, looking first on the
      // network and then on disk.
    } else {
      // The network was inaccessible and we have no cached data
      // for this query.
    }
  }
});
其中 cache policy 的類型與各代表意義如下:
  • IGNORE_CACHE:此次篩選不做快取
  • CACHE_ONLY:此次篩選只從快取中抓資料,不透過網路
  • NETWORK_ONLY: 此次篩選只從網路抓資料,不透過快取
  • CACHE_ELSE_NETWORK:此次篩選先從快取抓資料,沒有資料再透過網路
  • NETWORK_ELSE_CACHE:此次篩選先從網路抓資料,沒有資料再透過快取
  • CACHE_THEN_NETWORK:此次篩選先從快取抓資料,然後再透過網路抓一次
Parse 還提供了以下方法對快取做操作:
// Check to see if there is a cached result for the query with:
boolean isInCache = query.hasCachedResult();

// Remove any cached results for a query with:
query.clearCachedResult();

// Remove cached results for all queries with:
ParseQuery.clearAllCachedResults();

計數

以下方式可以得到此次符合篩選結果的資料筆數:
ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Sean Plott");
query.countInBackground(new CountCallback() {
  public void done(int count, ParseException e) {
    if (e == null) {
      // The count request succeeded. Log the count
      Log.d("score", "Sean has played " + count + " games");
    } else {
      // The request failed
    }
  }
});

複合式篩選

若要從多個篩選條件得到只要符合其中一個條件的結果,可以利用以下寫法:
ParseQuery<ParseObject> lotsOfWins = ParseQuery.getQuery("Player");
lotsOfWins.whereGreaterThan(150);

ParseQuery<ParseObject> fewWins = ParseQuery.getQuery("Player");
fewWins.whereLessThan(5);

List<ParseQuery<ParseObject>> queries = new ArrayList<ParseQuery<ParseObject>>();
queries.add(lotsOfWins);
queries.add(fewWins);

ParseQuery<ParseObject> mainQuery = ParseQuery.or(queries);
mainQuery.findInBackground(new FindCallback<ParseObject>() {
  public void done(List<ParseObject> results, ParseException e) {
    // results has the list of players that win a lot or haven't won much.
  }
});

幹你娘我累壞了...

1 則留言:

  1. 您好,我想请教一个问题,我想通过findinbackground 进行一个查询 把查询结果 显示在listview上,其中我需要显示的一项数据,需要进行一次计数工作,也就是用到 countinbackground 然而 这个countinbackground 总是在findinbackground 执行完后再执行,是的我的计数内容无法显示在listview上,请问有什么解决方法?

    回覆刪除