diff --git bp-core/bp-core-classes.php bp-core/bp-core-classes.php
index 8214f2f..16ac5cf 100644
--- bp-core/bp-core-classes.php
+++ bp-core/bp-core-classes.php
@@ -2,6 +2,334 @@
 // Exit if accessed directly
 if ( !defined( 'ABSPATH' ) ) exit;
 
+class BP_User_Query {
+	var $query_vars = array();
+
+	/**
+	 * List of found user ids
+	 *
+	 * @since 3.1.0
+	 * @access private
+	 * @var array
+	 */
+	var $results;
+
+	var $user_ids;
+
+	/**
+	 * Total number of found users for the current query
+	 *
+	 * @since 3.1.0
+	 * @access private
+	 * @var int
+	 */
+	var $total_users = 0;
+
+	// SQL clauses for the user ID query
+	var $uid_clauses;
+
+	/**
+	 * Constructor
+	 *
+	 * @since 1.7
+	 *
+	 * @param string|array $query The query variables
+	 */
+	function __construct( $query = null ) {
+		if ( !empty( $query ) ) {
+			$this->query_vars = wp_parse_args( $query, array(
+				'type'            => 'newest',
+				'per_page'        => 0,
+				'page'            => 1,
+				'user_id'         => 0,
+				'include'         => false,
+				'search_terms'    => false,
+				'populate_extras' => true,
+				'exclude'         => false,
+				'meta_key'        => false,
+				'meta_value'      => false,
+				'populate_extras' => true,
+				'count_total'     => 'count_query'
+			) );
+		}
+
+		// Get user ids
+		$this->prepare_user_ids_query();
+		$this->do_user_ids_query();
+
+		// If no users were found, we can stop here
+		if ( empty( $this->user_ids ) ) {
+			return;
+		}
+
+		// Fetch additional data. First, using WP_User_Query
+		$this->do_wp_user_query();
+
+		// Then, get BP-specific data
+		if ( $this->query_vars['populate_extras'] ) {
+			$this->populate_extras();
+		}
+	}
+
+	/**
+	 * Prepare the query for user_ids
+	 *
+	 * @since BuddyPress (1.7)
+	 */
+	function prepare_user_ids_query() {
+                global $wpdb, $bp;
+
+		extract( $this->query_vars );
+
+		$sql = array(
+			'select'  => '',
+			'where'   => array(),
+			'orderby' => '',
+			'order'   => '',
+			'limit'   => ''
+		);
+
+		/** 'type'  ********************************************/
+
+                // 'type' - Determines the sort order, which means it also
+                // determines where the user IDs are drawn from (the SELECT
+                // and WHERE statements)
+                if ( 'active' == $type || 'online' == $type || 'newest' == $type || 'popular' == $type ) {
+
+                        $this->uid_name = 'user_id';
+                        $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->usermeta} u";
+                        $sql['where'][] = "u.meta_key = 'last_activity'";
+			if ( 'newest' == $type ) {
+				$sql['orderby'] = "ORDER BY u.user_id";
+			} else {
+				$sql['orderby'] = "ORDER BY u.meta_value";
+			}
+
+			$sql['order']   = "DESC";
+
+                } else if ( 1==0 && 'alphabetical' == $type && ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) ) {
+			// We prefer to do alphabetical sorts against the
+			// display_name field of wp_users, because the table
+			// is smaller and better indexed. We can do so if
+			// WP-BP profile sync is enabled, or if BP xprofile is
+			// disabled altogether
+
+			// @todo BP_Core_User::get_users() does *not* limit
+			// this query to those users with last_activity set.
+			// I think this is a bug, but I've reproduced it here
+                        $this->uid_name = 'ID';
+                        $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->users} u";
+                        //$sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->users} u JOIN {$wpdb->usermeta} um ON (u.{$this->uid_name} = um.user_id)";
+			//$sql['where'][] = $wpdb->prepare( "um.meta_key = %s", bp_get_user_meta_key( 'last_activity' ) );
+			$sql['orderby'] = "ORDER BY u.display_name";
+			$sql['order']   = "ASC";
+
+                } else if ( 'alphabetical' == $type ) {
+			// When profile sync is disabled, alphabetical sorts
+			// must happen against the xprofile table
+
+			// @todo BP_Core_User::get_users() does *not* limit
+			// this query to those users with last_activity set.
+			// I think this is a bug, but I've reproduced it here
+                        $this->uid_name = 'user_id';
+                        $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$bp->profile->table_name_data} u";
+                        //$sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$bp->profile->table_name_data} u JOIN {$wpdb->usermeta} um ON (u.{$this->uid_name} = um.user_id)";
+			//$sql['where'][] = $wpdb->prepare( "um.meta_key = %s", bp_get_user_meta_key( 'last_activity' ) );
+			$sql['where'][] = "u.field_id = 1"; // @todo Is this safe to assume?
+			$sql['orderby'] = "ORDER BY u.value";
+			$sql['order']   = "ASC";
+                }
+
+		/** WHERE clauses ********************************************/
+
+                // 'include' - User ids to include in the results
+                if ( false !== $include ) {
+                        $include        = wp_parse_id_list( $include );
+                        $include_ids    = $wpdb->escape( implode( ',', (array) $include ) );
+                        $sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
+                }
+
+                // 'exclude' - User ids to exclude from the results
+                if ( false !== $exclude ) {
+                        $exclude        = wp_parse_id_list( $exclude );
+                        $exclude_ids    = $wpdb->escape( implode( ',', (array) $exclude ) );
+                        $sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})";
+                }
+
+                // 'user_id' - When a user id is passed, limit to the friends of the user
+                if ( !empty( $user_id ) && bp_is_active( 'friends' ) ) {
+                        $friend_ids = friends_get_friend_user_ids( $user_id );
+                        $friend_ids = $wpdb->escape( implode( ',', (array) $friend_ids ) );
+
+                        if ( !empty( $friend_ids ) ) {
+                                $sql['where'][] = "u.{$this->uid_name} NOT IN ({$friend_ids})";
+                        } else {
+                                // If the user has no friends, make sure the query returns null
+                                $sql['where'][] = "0 = 1";
+                        }
+                }
+
+                // 'search_terms' - To avoid global joins, do a separate query
+                if ( false !== $search_terms && bp_is_active( 'xprofile' ) ) {
+			$found_user_ids = $wpdb->get_row( $wpdb->prepare( "SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE %s", '%%' . like_escape( $search_terms ) . '%%' ), ARRAY_N );
+
+			if ( ! empty( $found_user_ids ) ) {
+				$sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
+			}
+                }
+
+                // 'meta_key', 'meta_value' - join against usermeta
+		// @todo Use WP_User_Query to get IDs for an IN clause?
+                if ( false !== $meta_key ) {
+                        $sql['select'] .= " LEFT JOIN {$wpdb->usermeta} um ON (um.user_id = u.{$this->uid_name})";
+
+                        $sql['where'][] = $wpdb->prepare( "um.meta_key = %s", $meta_key );
+
+                        // If a meta value is provided, match it
+                        if ( false !== $meta_value ) {
+                                $sql['where'][] = $wpdb->prepare( "um.meta_value = %s", $meta_value );
+                        }
+                }
+
+                // 'per_page', 'page' - handles LIMIT
+                if ( !empty( $per_page ) && !empty( $page ) ) {
+                        $sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
+                } else {
+			$sql['limit'] = '';
+		}
+
+                // Assemble the query chunks
+		$this->uid_clauses['select']  = $sql['select'];
+		$this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
+		$this->uid_clauses['orderby'] = $sql['orderby'];
+		$this->uid_clauses['order']   = $sql['order'];
+		$this->uid_clauses['limit']   = $sql['limit'];
+
+		do_action_ref_array( 'bp_pre_user_query', array( &$this ) );
+        }
+
+	/**
+	 * Execute the user id query
+	 */
+	protected function do_user_ids_query() {
+		global $wpdb;
+
+		// If counting using SQL_CALC_FOUND_ROWS, set it up here
+		if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
+			$this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] );
+		}
+
+		// Get the specific user ids
+		$this->user_ids = $wpdb->get_col( $wpdb->prepare( "{$this->uid_clauses['select']} {$this->uid_clauses['where']} {$this->uid_clauses['orderby']} {$this->uid_clauses['order']} {$this->uid_clauses['limit']}" ) );
+
+		// Get the total user count
+		if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
+			$this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) );
+		} else if ( 'count_query' == $this->query_vars['count_total'] ) {
+			$count_select = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(DISTINCT u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] );
+			$this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) );
+		}
+	}
+
+	protected function do_wp_user_query() {
+		$wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array(
+			'blog_id'     => 0, // BP does not require blog roles
+			'fields'      => array( 'ID', 'user_registered', 'user_login', 'user_nicename', 'display_name', 'user_email' ),
+			'count_total' => false, // We already have a count
+			'include'     => $this->user_ids
+		), $this ) );
+
+		// Reindex for easier matching
+		$r = array();
+		foreach ( $wp_user_query->results as $u ) {
+			$r[ $u->ID ] = $u;
+		}
+
+		// Match up to the user ids from the main query
+		foreach ( $this->user_ids as $uid ) {
+			if ( isset( $r[ $uid ] ) ) {
+				$this->results[ $uid ] = $r[ $uid ];
+
+				// The BP template functions expect an 'id'
+				// (as opposed to 'ID') property
+				$this->results[ $uid ]->id = $uid;
+			}
+		}
+	}
+
+	protected function populate_extras() {
+		global $bp, $wpdb;
+
+		if ( empty( $this->results ) ) {
+			return;
+		}
+
+		$user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) );
+
+		// Fetch the users' full names
+		if ( bp_is_active( 'xprofile' ) && 'alphabetical' != $this->query_vars['type'] ) {
+			// @todo - Why not just assume field id 1?
+			$fullname_field_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", bp_xprofile_fullname_field_name() ) );
+
+			$names = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, value as fullname FROM {$bp->profile->table_name_data} WHERE user_id IN ({$user_ids_sql})" ) );
+
+			foreach ( $names as $name ) {
+				if ( isset( $this->results[ $name->user_id ] ) ) {
+					$this->results[ $name->user_id ]->fullname = $name->fullname;
+				}
+			}
+		}
+
+		// Fetch whether or not the user is a friend
+		if ( bp_is_active( 'friends' ) ) {
+			$friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids_sql} ) ) OR (initiator_user_id IN ( {$user_ids_sql} ) AND friend_user_id = %d )", bp_loggedin_user_id(), bp_loggedin_user_id() ) );
+
+			foreach ( $friend_status as $fs ) {
+				// The "friend" is the user id in the pair who
+				// is *not* the logged in user
+				$friend_id = bp_loggedin_user_id() == $fs->initiator_user_id ? $fs->friend_user_id : $fs->initiator_user_id;
+
+				if ( isset( $this->results[ $friend_id ] ) ) {
+					$this->results[ $friend_id ]->is_friend = $fs->is_confirmed;
+				}
+			}
+		}
+
+		// Fetch usermeta data
+		// We want the three following pieces of info from usermeta:
+		// - friend count
+		// - last activity
+		// - latest update
+		$total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' );
+		$last_activity_key      = bp_get_user_meta_key( 'last_activity' );
+		$bp_latest_update_key   = bp_get_user_meta_key( 'bp_latest_update' );
+
+		$umetas = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key IN (%s,%s,%s) AND user_id IN ({$user_ids_sql})", $total_friend_count_key, $last_activity_key, $bp_latest_update_key ) );
+
+		foreach ( $umetas as $umeta ) {
+			// The members_template global expects the index key to
+			// be different from the meta_key in some cases
+			switch ( $umeta->meta_key ) {
+				case $total_friend_count_key :
+					$key = 'total_friend_count';
+					break;
+
+				case $last_activity_key :
+					$key = 'last_activity';
+					break;
+
+				case $bp_latest_update_key :
+					$key = 'latest_update';
+					break;
+			}
+
+			if ( isset( $this->results[ $umeta->user_id ] ) ) {
+				$this->results[ $umeta->user_id ]->{$key} = $umeta->meta_value;
+			}
+		}
+	}
+}
+
 /**
  * BP_Core_User class can be used by any component. It will fetch useful
  * details for any user when provided with a user_id.
@@ -201,6 +529,8 @@ class BP_Core_User {
 	function get_users( $type, $limit = 0, $page = 1, $user_id = 0, $include = false, $search_terms = false, $populate_extras = true, $exclude = false, $meta_key = false, $meta_value = false ) {
 		global $wpdb, $bp;
 
+		_deprecated_function( __METHOD__, '1.7', 'BP_User_Query' );
+
 		$sql = array();
 
 		$sql['select_main'] = "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.display_name, u.user_email";
diff --git bp-members/bp-members-functions.php bp-members/bp-members-functions.php
index 2a9e12a..a05eae2 100644
--- bp-members/bp-members-functions.php
+++ bp-members/bp-members-functions.php
@@ -71,6 +71,10 @@ add_action( 'bp_setup_globals', 'bp_core_define_slugs', 11 );
 /**
  * Return an array of users IDs based on the parameters passed.
  *
+ * Since BuddyPress 1.7, bp_core_get_users() has used BP_User_Query. If you
+ * need backward compatibility with BP_Core_User::get_users(), filter the
+ * bp_use_legacy_user_query value, returning true.
+ *
  * @package BuddyPress Core
  */
 function bp_core_get_users( $args = '' ) {
@@ -87,12 +91,40 @@ function bp_core_get_users( $args = '' ) {
 		'per_page'        => 20,       // The number of results to return per page
 		'page'            => 1,        // The page to return if limiting per page
 		'populate_extras' => true,     // Fetch the last active, where the user is a friend, total friend count, latest update
+		'count_total'     => 'count_query' // What kind of total user count to do, if any. 'count_query', 'sql_calc_found_rows', or false
 	);
 
 	$params = wp_parse_args( $args, $defaults );
 	extract( $params, EXTR_SKIP );
 
-	return apply_filters( 'bp_core_get_users', BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value ), $params );
+	// For legacy users. Note that use of BP_Core_User::get_users() is
+	// deprecated and not recommended
+	if ( false !== apply_filters( 'bp_use_legacy_user_query', false, __FUNCTION__, $params ) ) {
+		return apply_filters( 'bp_core_get_users', BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value ), $params );
+	}
+
+	$users = new BP_User_Query( array(
+		'type'            => $type,
+		'user_id'         => $user_id,
+		'exclude'         => $exclude,
+		'search_terms'    => $search_terms,
+		'meta_key'        => $meta_key,
+		'meta_value'      => $meta_value,
+		'include'         => $include,
+		'per_page'        => $per_page,
+		'page'	          => $page,
+		'populate_extras' => $populate_extras,
+		'count_total'     => $count_total
+	) );
+
+	// Reformat the BP_User_Query data
+	$retval = array(
+		'users' => array_values( $users->results ),
+		'total' => $users->total_users
+	);
+
+	return $retval;
+
 }
 
 /**
