Facebook FQL Multiquery Example

While working on our latest app for the iPad, Flickpad, I have been knee deep in Facebook FQL.  FQL provides a powerful, albeit resource limited, way to access Facebook data.  FQL is loosely inspired by SQL, pretty much just implementing the most basic syntax features.  There are no joins allowed, tables must be queried on at least one of there indexed fields, IN is supported but NOT IN is not.  Facebook provides one nugget though, fql.multiquery.  Mutliquery allows you to batch up multiple queries and send them in one request.  It also allows queries to reference the results of other queries in the batch.  Documentation on multiquery is pretty limited so I figure I would put up a sample from Flickpad.   This particular multiquery is pulling all albums, comments, likes, and users for a given set of photos (users is pulling users based on the comments query).


#pragma mark - Facebook MultiQuery Methods
- (void)getMetadataForPhotos:(NSArray*)thePhotos; {
	if ([self isConnected] && !IsEmpty(thePhotos)) {
		
		NSMutableString *multiquery = [NSMutableString stringWithString:@"{"];
	
		/* Build Comments Query */
		[multiquery appendFormat:@"\"%@\":", facebookCommentType];
		
		NSString *fql = @"SELECT "
				@"object_id, fromid, time, text "
				@"FROM "
				@"comment "
				@"WHERE "
				@"object_id IN %@";
		
		NSArray *objectIds = [thePhotos valueForKeyPath:@"@distinctUnionOfObjects.objectId"];
		[multiquery appendFormat:@"\"%@\"", [NSString stringWithFormat:fql, [self fqlComplientArrayString:objectIds]]];
		
		
		/* Build Albums Query */
		[multiquery appendFormat:@",\"%@\":", facebookAlbumType];
		
		fql =	@"SELECT "
			@"aid, owner, name, size "
			@"FROM "
			@"album "
			@"WHERE "
			@"aid IN %@";
		
		NSArray *albumIds = [thePhotos valueForKeyPath:@"@distinctUnionOfObjects.albumId"];
		[multiquery appendFormat:@"\"%@\"", [NSString stringWithFormat:fql, [self fqlComplientArrayString:albumIds]]];
		

// 	!!!: Have to do Likes via a single query until Facebook fixes Like table support in (multiquery,XML)
//		/* Build Likes Query */
//		[multiquery appendFormat:@",\"%@\":", facebookLikeType];
//		
//		fql =	@"SELECT "
//			@"object_id, user_id "
//			@"FROM "
//			@"like "
//			@"WHERE "
//			@"object_id IN %@";
//		
//		[multiquery appendFormat:@"\"%@\"", [NSString stringWithFormat:fql, [self fqlComplientArrayString:objectIds]]];
		
		
		/* Build Comment User Query */
		[multiquery appendFormat:@",\"%@\":", facebookUserType];
		
		fql =	@"SELECT "
			@"uid, name, first_name, last_name, pic_square, birthday_date "
			@"FROM "
			@"user "
			@"WHERE "
			@"uid IN (SELECT fromid from #%@) "
			@"ORDER BY last_name";
		
		[multiquery appendFormat:@"\"%@\"", [NSString stringWithFormat:fql, facebookCommentType]];
		
		
		/* Close the multiquery */
		[multiquery appendString:@"}"];
		
		/* Send MultiQuery */
		NSDictionary *params = [NSDictionary dictionaryWithObject:multiquery forKey:facebookMultiQueryKey];
			
		FBRequest *request = [FBRequest requestWithDelegate:self];
		request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
							metadataForPhotosCBSS, facebookCallbackKey, 
							facebookMetadataType, facebookReturnTypeKey, 
							facebookMultiQueryMethod, facebookAPICallTypeKey, 
							thePhotos, facebookPhotoKey, nil];
		[request call:facebookMultiQueryMethod params:params];
	}
}

- (NSString*)fqlComplientArrayString:(NSArray*)objects; {
	
	NSString *result = [[objects valueForKey:@"description"] componentsJoinedByString:@","];
	
	if (!IsEmpty(result)) {
		return [NSString stringWithFormat:@"(%@)", result];
	}
	else {
		return @"";
	}
}

As you will see in the code above, querying the ‘LIKE’ table via a multiquery has an outstanding bug. It appears when you query the ‘LIKE’ table via a multiquery and with xml response format, the response contains missing and incorrectly name keys. The same query works fine via multiquery/json and via fql.query.

The only other aspect of the code that is of interest is the NSDictionary added to the userInfo attribute of the FBRequest.  These values are used when processing the response to specify a delegate callback function, the object type to create from the responses, what type of query was used, and the photos that were searched for.