Ning Developer Network

Ning Developer Admin

Querying the Ning Content Store

The XN_Query object is your gateway to the Ning Content Store. It offers a flexible mechanism for retrieving content and tags. There are four types of queries:

  • Content: for retrieving Content objects from the Content Store.
  • Content Count: for content rollups: lists of content types or contributor screen names and how many content objects with that type or contributor have been added to an app.
  • Tag: for retrieving Tag objects from the Content Store.
  • Tag_ValueCount: for tag rollups: lists of tag words and how many times they've been applied to certain content.

A query can have filters, which control what data matches the query. Tag and Content queries can also have an order, which controls the how the matching data is returned.

To create a query, pass XN_Query::create() the type of query you want to create. Example 1 shows the creation of each type of query.

$contentQuery = XN_Query::create('Content');
$tagQuery = XN_Query::create('Tag');
$tagValueCountQuery = XN_Query::create('Tag_ValueCount');

Example 1. Creating a Query

After a query has been created, add filters to it to restrict what the query matches. A filter consists of field, an operator, and a value. Example 2 adds a filter to $contentQuery that makes the query return only content whose type is IceCream (ignoring case differences) and that has a flavor developer attribute set to chocolate:.

$contentQuery->filter('type','eic','IceCream');
$contentQuery->filter('my->flavor','=','chocolate');

Example 2. Adding a Filter

To run the query and retrieve results, call execute() on the query object, as shown in Example 3

$results = $contentQuery->execute();

Example 3. Executing a Query

Note: A single query execution will return a maximum of 100 items. (This restriction exists to place a safety limit on the amount of work the Content Store has to do for a query, to ensure it's not monopolised by a single query execution.) The total count of objects that match the query criteria can be retrieved with getTotalCount().

The operators that filter() knows about are shown in Table 1.

Table 1. Content Filters
Filter Meaning
= equal to
<> not equal to
eic equal to, ignoring case
neic not equal to, ignoring case
> greater than
< less than
>= greater than or equal to
<= less than or equal to
like full-text search
likeic full-text search, ignoring case
in in a list of values

Most of these operators are pretty straightforward. The in, like, and likeic operators deserve some explanation, though.

When using the in operator, the supplied value must be an array. The filter matches objects that have a value equal to any one of the values in the array.

The like and likeic operators do a full-text search against the specified field. The search engine looks for the individual words in the supplied value (separated by space or %) and tries to find the most relevant matches. If you don't supply an order for a query that uses the like or likeic operator, you'll get results back ordered by relevance from the search engine.

Not all fields can be used with all operators. Table 2 lists the fields you can use in a Content query, what operators they support, and what kinds of values they support.

Table 2. Content Fields and Operators
Field Operator(s) Value(s)
id in, =, <> Content object or content object ID. With in, pass an array of content objects or IDs.
createdDate =, <>, <, >, <=, >= Appropriately formatted date string
updatedDate =, <>, <, >, <=, >= Appropriately formatted date string
type =, <>, like, likeic String
title =, <>, like, likeic String. With = or <> and a null value, it matches content with (or without) a null title.
description Same as title Same as title
isPrivate =, <> Boolean
owner =, <>, eic, neic, like, likeic Application object. All operators ignore case. If value is null, the current application is used.
owner->relativeUrl Same as owner String, otherwise same as owner
contributor =, <>, eic, neic Profile object. All operators ignore case. If value is null, matches whether the content contributor is (or isn't) anonymous.
contributor->screenName =, <>, eic, neic String, otherwise, same as contributor
my->developerAttribute Same as title developerAttribute is the name of any developerAttribute, otherwise same as title
tag->value =, in String or array of strings. Matches content tagged with the specified tag word(s)
tag->owner->screenName =, in String or array of strings. Matches content tagged by the specified user(s)
referencerId =, in Content object ID or array of IDs. Matches content referenced by the specified content.

The Content_Count query works a bit differently. The only filters it supports are owner and owner->relativeUrl. In addition, you must call the rollup() method on the query object with an argument of either type or contributor. This determines whether your results are rolled up by content type or content contributor. In either case, the results you get back are an associative array. The keys of the array are types (or contributor screen names). The values of the array are the number of content objects with the given type (or contributor) in the specified app. Example 4 shows a Content_Count query.

$query = XN_Query::create('Content_Count');
$query->filter('owner'); // current app
$query->rollup('type');
$types = $query->execute();
foreach ($types as $type => $count) {
echo
"There are $count object(s) with content type $type in this app.";
echo '<br/>';
}

Example 4. Content Rollup Query

Table 3 lists the fields you can use in a Tag query, what operators they support, and what kinds of values they support.

Table 3. Tag Fields and Operators
Field Operator(s) Value(s)
content =, <>, in Content object (or array of content objects)
contentId Same as content Content object ID (or array of IDs)
content->owner = Application object. If null, assumes the current app.
content->ownerName = String, otherwise same as content->owner
value =, <>, eic, neic String
createdDate =, <>, <, >, <=, >= Appropriately formatted date string
updatedDate =, <>, <, >, <=, >= Appropriately formatted date string
owner =, <>, eic, neic Profile object. All operators ignore case. Matches tags saved by the given user.
ownerName Same as owner String, otherwise same as owner
owner->screenName Same as owner String, otherwise same as owner

Table 4 lists the fields you can use in a Tag_ValueCount query, what operators they support, and what kinds of values they support.

Table 4. Tag_ValueCount Fields and Operators
Field Operator(s) Value(s)
content = Content object (or array of content objects)
contentId Same as content Content object ID (or array of IDs)
owner = Profile object.
ownerName Same as owner String, otherwise same as owner
content->owner = Application object. If null, assumes the current app.
content->ownerName = String, otherwise same as content->owner

For a filter value, "appropriately formatted date string" means a complete W3C date string including year, month, day, hour, minute, second, and timezone offset specifier, such as 1975-03-10T19:45:23-05:00. See Thinking Globally about Characters and Dates for more information on the date formats that the Content Store supports.

To have the results of your Content or Tag query come back in a particular order, you need to specify a query order with the order() method. Example 5 shows queries with orders.

$contentQuery = XN_Query::create('Content');
$contentQuery->filter('type','eic','IceCream');
$contentQuery->filter('my->flavor','=','chocolate');
$contentQuery->order('createdDate','asc');
$tagQuery = XN_Query::create('Tag');
$tagQuery->filter('content','=',$someContentObject);
$tagQuery->filter('owner->screenName','=','xntestuser');
$tagQuery->order('createdDate','desc');

Example 5.

The orders that Content queries support are:

  • createdDate
  • updatedDate
  • type
  • title
  • description
  • contributor->screenName
  • my.developerAttribute (for any developer attribute)

The orders that Tag queries support are

  • createdDate
  • updatedDate
  • value

By default, Tag_ValueCount queries are ordered by tag frequency (most frequent first.) To order Tag_ValueCount queries alphabetically by tag word, use the tag ordering with a Tag_ValueCount query.

Both Content and Tag queries also support the random() order, which returns results in random order.

By default, the ordering is descending (biggest first). The optional second argument to order() controls this. Pass asc for ascending or desc for descending.

The various XN_Query methods support chaining, so you can build queries as shown in Example 6

$results = XN_Query::create('Content')
->filter('type','eic','IceCream')
->filter('my->flavor','=','chocolate')
->execute();

Example 6: Query method chaining

Performance Tips

Connecting to the Ning Content Store involves making an HTTP connection, since the PHP API is simply a wrapper for the Ning REST APIs. This is similar to performing an SQL query through a socket against a relational database in a web page. So, just as you want to minimize separate database queries in traditional web pages, you will want to minimize separate content store queries in Ning sites. If you need to do more than 10 queries in a single page, reconsider how the page is structured and how your content is structured.

Similarly, limit the number of attributes on a particular content object. If your site logic requires storing more than 50 different attributes on a content object, reconsider your content object structure. This is particularly important if there could be a large number of content objects that each have a lot of attributes. If a site has three content objects that each have 50 attributes, that's not a big deal. But 1000 content objects that each have 50 attributes is not a good idea.

Also, structure your content objects so that it's unlikely that the object will need to be changed by two simultaneous requests. For example, storing individual blog posts in their own content objects makes sense – as each post is created or edited (usually just by one person at a time), the separate content objects are created or changed. But storing each blog post as an attribute of a single site-wide content object is a bad idea. Each time a blog post is created or edited, that object has to be updated or changed. So if two posts are saved at the same time, there will be contention problems on that object.

Built-In Content Store Query Limits

Queries against the content store return a maximum of 100 objects. Even though you may run across a few examples of it in some apps, avoid repeating a single query with different start and end limits to retrieve all (more than 100) matching elements. Pagination is critical both from a performance and usability point of view. If a feature in an app requires you to do this, rethink the feature. Sometimes just changing your approach in the code solves the problem. Other times, caching data and keeping aggregate statistics in separate content objects is a helpful way to avoid extra-large queries.

Owner-filtered queries

In almost every case, queries should be limited to just content or tags owned by your site, rather than sites across Ning. Make sure that your queries (whether on content or tags) have an owner filter:

$query->filter('owner');

This is a required parameter to almost every query you'll ever write for a feature on a Ning site.

Avoid N+1 Queries

An N+1 Query is one where you fetch a list of keys and then iterate over that list of keys to obtain another element, each time performing a new query. This results in 1 query to start and N queries that follow to obtain each of the items in the list. Since each query is actually a call to the Ning API, N+1 Queries substantially degrade your site's performance.

Almost every use case for N+1 queries can be resolved with 2 queries: get N ids in a single query and then get N objects in a second query using the in filter and passing a list of Object IDs returned by the first query.

Query Debugging

While developing new features for your Ning Social Site, you may find you need to look at the actual data that is being passed between the Ning Core and PHP in API requests. By setting the xn_debug query string variable and calling the XN_Debug::allowDebug() function, you get verbose output in the page of all API calls. This is useful to see what code in your app is actually taking time to talk to the content store and what the raw requests and responses are.

Last updated by Ning Developer Admin Oct 8.

We're Hiring

We are looking for talented and passionate individuals to join our growing team.

Visit our engineering jobs and see if Ning is right for you.

© 2008   Created by Ning Developer Admin

Badges  |  Report an Issue  |  Privacy  |  Terms of Service