Announcement Announcement Module
No announcement yet.
FQL and SpringSocialFacebook Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • FQL and SpringSocialFacebook

    I saw the task targeted at 1.1.0 release, and wondered how it was going.

    I'm about to start diving in to some serious FQL work, and wanted to be sure it going well.
    Might also need to use the multi-query functionality, which makes an even more crazy api.

    Has anybody out there made model classes to hold the data from various tables?
    In particular, for the FQL stream table.

    Thanks in advance,

  • #2
    Well, there's an initial bit of FQL work in the latest snapshots. Specifically, FqlOperations give you a method with this signature:

    <T> List<T> query(String fql, FqlResultMapper<T> mapper);
    Essentially, you pass in whatever FQL you want along with your implementation of the FqlResultMapper and it will return whatever type the result mapper returns. For example, here's some code from the FqlTemplateTest:

    List<StatusObject> results = facebook.fqlOperations().query("SELECT uid, status_id, message, time, source FROM status WHERE uid=me()", new FqlResultMapper<StatusObject>() {
    	public StatusObject mapObject(FqlResult result) {
    		StatusObject status = new StatusObject();
    		status.uid = result.getInteger("uid");
    		status.statusId = result.getString("status_id");
    		status.statusIdLong = result.getLong("status_id");
    		status.message = result.getString("message");
    		status.time = result.getTime("time");
    		status.source = result.getLong("source");
    		return status;
    It doesn't yet support multi-query (I envision that being a case where you send in a Map<String, String> of queries instead of a simple String), but single queries should work fine. AFAIK, I'm the only one who has tested this, so I welcome you to try it out and let me know what's missing or doesn't work (aside from multi-query, which I already know about).


    • #3
      OK, I'll certainly give it a peek.
      As for the mapObject logic, I'm tempted to add in more of the full jackson mapping logic. Many of the tables have complex sub-elements, which will start being more and more cumbersome to create mappers, each time.

      Also thinking of having some cheats for SELECT strings for the basic case of 'return everything', per FQL table.
      The multi-query would also need to be intelligent about partial failures.



      • #4
        Sounds good. I was wondering about nested elements in the return, but on my cursory review of the tables, I couldn't find an I initially assumed a flat structure. Could you give me an example of a table that has more complex sub-elements that would require more detailed JSON-mapping? Not doubting you...just want to see what it looks like.

        If you find opportunity for improvement, I always welcome pull requests!


        • #5
          FQL - stream table complications.

 has the data for the stream table, which is the wall / home type feed.

          comments array
          An array containing the following information about comments for a post:

          can_remove (bool): Indicates whether users can remove comments
          can_post (bool): Indicates whether users can post comments
          count (int): The total number of comments added to the post
          comment_list (array): A sample of comments made on the post, to get the full list of comments, use the Post Graph API object or query the comment FQL table
          and a few others. The documentation is somewhat thin, so your guess is as good as mine.
          I'll give you more specifics as I walk down that path.


          • #6

            Comment about FqlResult: Most of the calls to getX return null if it's not present in the object map;
            However, getTime will throw an exception.

            public Date getTime(String fieldName) {
            	try {
            		long timeInMilliseconds = Long.valueOf(String.valueOf(resultMap.get(fieldName))) * 1000;
            		return resultMap.containsKey(fieldName) ? new Date(timeInMilliseconds) : null;
            	} catch (NumberFormatException e) {
            		throw new FqlException("Field '" + fieldName +"' is not a time.", e);
            I believe Long.valueOf will be the culprit, as "null" will not parse.
            The API should be consistent in how it treat missing (as opposed for malformed) map entries.


            • #7
              Noted and thanks. Will look to correct that soon.


              • #8
                Rob: Fixed the getTime() to check for null before attempting to convert it to a date. In the master branch now.


                • #9
                  facebook's FQL documentation lies!

                  Another thing worth mentioning for anybody else making adapters for FQL - their documentation does not accurately represent the json they return.
                  They say things like
                  comments array An array containing the following information about comments for a post:
                  But it really returns an object with the elements in question as attributes.
                  Attempting to parse it with FqlResult as a list will fail.

                  Likewise, many other items claiming to return array really return a sub-object.

                  Items that claim to return an int will return a value that does not fit in a java int. E.g. source_id
                  Items claiming to return an int will also return null, which will fail to parse in FqlResult -
                  resultMap.containsKey(fieldName) will return true, but Long.valueOf(String.valueOf(resultMap.get(fieldNam e))) will fail.

                  I'm not sure if it is desirable to fill return NULL in that case, or throw exception. I'm very close to using String everywhere I can, instead of the declared FB type.


                  • #10
                    FqlResult.getList does not work for array of primitives

                    query, modified for simplicity:
                    SELECT post_id, viewer_id, tagged_ids FROM stream WHERE source_id=100002487294420 AND updated_time>1333363915
                    { "data": [ {
                          "post_id": "100002487294420_183841518401962",
                          "viewer_id": 100003283444808,
                          "tagged_ids": [ 100002886616615 ]
                        }, {
                          "post_id": "100002487294420_261607593932148",
                          "viewer_id": 100003283444808,
                          "tagged_ids": [ ]
                    The problem occurs when trying to use FqlResult.getList to handle it.
                    getList assumes the contents of the array are objects, rather than primitives like ints or strings.

                    I suppose I need to use getObject() plain?
                    Last edited by Rob Blair; Apr 2nd, 2012, 08:47 PM. Reason: add example


                    • #11
                      These are all good things that you're pointing out. I'll look into this one...but it'd be helpful if you find things like this to open a bug in JIRA at Sometimes these comments in the forums get lost in all of the forum noise; but I notice open bugs in JIRA.


                      • #12
                        Originally posted by habuma View Post
                        These are all good things that you're pointing out. I'll look into this one...but it'd be helpful if you find things like this to open a bug in JIRA at Sometimes these comments in the forums get lost in all of the forum noise; but I notice open bugs in JIRA.
                        I will do that. I do like to take my time, make sure I understand the code and intent before adding a bug. Nobody wants JIRA full of hundreds of wish-list items.


                        • #13
                          Mostly about the getList() issue, but touches on the whole set of getXYZ issues.


                          • #14
                            FqlResult and list of primitive values

                            Here is a possible modification to FqlResult, in the vein of FqlResultMapper:
                            If you need to map an array of primitives, like Integer, String, or Array, the existing FqlResultMapper will not suffice.
                            Class Cast Errors come out, since it tries to handle the data as a List of Map<String,Object>

                            Would a FqlValueMapper make sense?
                            public interface FqlValueMapper<T> {
                                T mapObject(Object objectValue);
                            with a function in FqlResult like
                            public <T> List<T> getList(String fieldName, FqlValueMapper<T> mapper) {
                                // get field as List<Object>, and iterate over each item, passing it to the FqlValueMaper
                            This would let us handle the stream table that has a list of ids in 'tagged_ids' or the 'message_tags' that is a List<List<tagObject>>

                            message_tags (class java.util.ArrayList): [[{id=100002886616615, name=Robert Blair, type=user, offset=0, length=12}]]

                            Should I make a bug, or make a pull request. (not the git expert yet, mind you.)


                            • #15
                              habuma, don't go rushing to add this stuff quite yet -
                              I've found another facet in the facebook world.
                              Some of the FQL values in json can come as a sparse array OR regular array
                              "message_tags": { "7": [ {
                              "id": 157167397630682,
                              "name": "INQ Mobile (SF)",
                              "type": "page",
                              "offset": 7,
                              "length": 15
                              message_tage[[{"offset":0, ...}]]

                              Might add a getSparseArray(fieldName, Mapper()) function that handles that.
                              It's a bit confusing, so I'll need to think if there is a better way, or one that does not require a library change.