2015-05-25 3 views
0

У меня есть таблица действий, в которой говорится, какие пользователи следует, кто. (fromUser и toUser) Я создаю таблицу лидеров, чтобы узнать, кто имеет наибольший рейтинг, среди последователей.Parse: Compound Query on Followers times out

Так что я создал этот вопрос:

ParseQuery<ParseObject> queryActivityFollowing = new ParseQuery<>("Activity"); 
queryActivityFollowing.whereEqualTo("type", "follow"); 
queryActivityFollowing.whereEqualTo("fromUser", ParseUser.getCurrentUser()); 
queryActivityFollowing.setLimit(500); 

// innerQuery, only get Users posted by the users I follow 
ParseQuery<ParseUser> queryUserFollowing = ParseUser.getQuery(); 
queryUserFollowing.whereMatchesQuery("toUser", queryActivityFollowing); 

// querySelf 
ParseQuery<ParseUser> querySelf = ParseUser.getQuery(); 
querySelf.whereEqualTo("objectId", ParseUser.getCurrentUser().getObjectId()); 

List<ParseQuery<ParseUser>> queries = new ArrayList<>(); 
queries.add(queryUserFollowing); 
queries.add(querySelf); 

query = ParseQuery.or(queries); 
query.orderByDescending("rating_count"); 
query.setLimit(20); 

Но каким-то образом, это раз, и никогда не отображает результат. Есть ли что-то неэффективное с моим запросом?

Спасибо!

Edit: Описание данных: Activity класс с 3-мя колоннами, fromUser, toUser, type. fromUser и toUser являются указателями на _User класса, type является строка

в _User, у меня есть классические атрибуты, и целое число с именем rating_count, к которым критерий OrderBy (обновленный код выше).

На самом деле, я думаю, что запрос не истекает, а просто возвращает 0 результатов. Я следую за некоторыми из моих пользователей, поэтому это определенно не ожидаемый результат.

+0

Просьба дополнительно описать данные. В вопросе о соответствующих классах и атрибутах есть несколько советов, но, пожалуйста, укажите их типы (например, fromUser является указателем на __User?) И что они означают (например, fromUser следует за toUser или наоборот?). Я не вижу ссылки на атрибут «рейтинг», который может указывать на текст. – danh

+0

@ dahn: Спасибо за ваши комментарии, я обновил вопрос. Если я следую userB, то fromUser содержит указатель на меня, а toUser содержит указатель userB – Stephane

ответ

1

Это сложный вопрос, потому что запрос синтаксиса поддерживает это только минимально. Самая лучшая идея, которую я могу предложить это один:

  1. Один запрос на столе активность whereEqualTo("type", "follow") и whereEqualTo("fromUser", ParseUser.getCurrentUser())
  2. нет queryUserFollowing, нет querySelf. Это не нужно. Это также освобождает вас от Parse.Query.or().
  3. setLimit(1000) объяснит, почему ниже
  4. include("toUser")
  5. после завершения цикла по результатам, максимально для result.get("toUser").getInt("rating_count"), так как результаты будут экземплярами деятельности, и вы будете иметь охотно принесли свои родственные toUsers.

Эта схема проще, чем вы закодировали, и выполнит эту работу. Однако, возможно, главная проблема заключается в том, что он будет пропускать данные для пользователей с 1000 последователей. Дайте мне знать, если это проблема, и я могу предложить более сложный ответ. Небольшой недостаток заключается в том, что вы будете вынуждены выполнять поиск (возможно, сортировку) самостоятельно в памяти, чтобы найти максимальный рейтинг_count.

EDIT - Для> ок последователей, вы застряли с вызовом запрос несколько раз, установив skip графы записей, полученных в предыдущем запросе, собирая результаты в большом массиве.

Ваша точка зрения о передаче большого количества данных хорошо взята, и вы можете свести к минимуму использование сети, поместив все это в облачную функцию, выполняя работу в памяти в облаке и возвращая только те записи, которые нужны клиенту. (Этот подход имеет дополнительное преимущество в кодировании в javascript, который я говорю более свободно, чем java, поэтому я мог бы более предписывать код).

EDIT 2 - Выполнение этого в облачном коде имеет преимущество уменьшения сетевого трафика только для тех пользователей (скажем, 20), которые имеют максимальные рейтинги. Он не решает другие проблемы, о которых я говорил ранее. Вот как я сделал бы это в облаке ...

var _ = require('underscore'); 

Parse.Cloud.define("topFollowers", function(request, response) { 
    var user = new Parse.User({id:request.params.userId}); 
    topFollowers(user, 20).then(function(result) { 
     response.success(result); 
    }, function(error) { 
     response.error(error); 
    }); 
}); 

// return the top n users who are the top-rated followers of the passed user 
function topFollowers(user, n) { 
    var query = new Parse.Query("Activity"); 
    query.equalTo("type", "follow"); 
    query.equalTo("fromUser", user); 
    query.include("toUser"); 
    return runQuery(query).then(function(results) { 
     var allFollowers = _.map(results, function(result) { return result.get("toUser"); }); 
     var sortedFollowers = _.sortBy(allFollowers, function(user) { return user.get("rating_count"); }); 
     return _.first(sortedFollowers, n); 
    }); 
} 

// run and rerun a query using skip until all results are gathered in results array 
function runQuery(query, results) { 
    results = results || []; 
    query.skip(results.length); 
    return query.find().then(function(nextResults) { 
     results = results.concat(nextResults); 
     return (nextResults.length)? runQuery(query, results) : results; 
    }); 
} 

Примечание. Я не тестировал это, но имел аналогичный материал, работающий в производстве.

+1

Hi @Danh, Кажется, что это сработает, но я думаю, что есть еще одна неэффективность. Я должен был бы забрать 1000 пользователей через сеть (вроде бы дорого), а затем отсортировать их, чтобы извлечь верхнюю часть 20. (крайний случай, но вы видите мою точку зрения). Что было бы более сложным ответом, чтобы убедиться, что вся обработка и вычисления сделаны на стороне Парсера? – Stephane

+0

Я думаю, что могу удалить запрос self, добавить 'getCurrentUser()', когда будут получены результаты подписчиков, и посмотреть, попадает ли он между верхним 20. Все еще интересует ваш второй более сложный ответ. – Stephane

+0

. Коренная проблема заключается в том, что мы действительно хотим сортировать onUser.rating_count, но этот вид реляционной сортировки недоступен в синтаксическом анализе. Я вообще пытаюсь найти лучший подход, но я не думаю, что есть. Будет редактировать ответ, чтобы описать, что я имею в виду, более сложным решением для> 1k последователей – danh

1

Если вы хотите изменить модель данных, есть решение, которое получает то, что вам нужно, плюс некоторые преимущества стороны. Рассмотрим систему, в которой класс User относится только к отношениям между приложением и реальным человеком. Открытые лица пользователей друг к другу представлены новым классом (назовите его PublicUser или Persona).

В этом PublicUser классе, у вас есть указатель к user, которому принадлежит это, и массив указателей другим PublicUser с, кто этот один является following. Этот класс также содержит атрибут rating. Теперь запрос в OP прост:

  1. PublicUser запрос whereKey «после» равен CurrentUser
  2. заказ по рейтингу, ограничение до 20 или любой другой номер, который вы хотите ограничить

Вот и все. Другим преимуществом этой схемы является контроль доступа. Система понимает, что что-либо в PublicUser читается другим PublicUsers, и все, что касается __User, хранится между этим отдельным человеком и приложением.

+0

Мне это очень нравится - это не слишком разрушительно для моей модели данных, но это все равно потребует значительных изменений в работе моих приложений. Я также заставляю меня внедрить гораздо более совершенную систему безопасности, на которую меня всегда интересовало. Единственное предостережение, которое я вижу здесь, состоит в том, что строка может содержать только 128 Кбайт данных. Может быть, массив из 1000 последователей перейдет через это ...? кто знает. Но это очень экстремально, просто подумал упомянуть об этом как о возможном ограничении. Большое спасибо @dahn – Stephane

+1

Если есть вероятность, что число последователей превысит 1k, тогда мы переместим массив указателей на отношение. – danh