CouchDB, Node.js, Cradle — как получить данные на основе возвращаемых данных

Я работаю над системой обмена сообщениями, используя node.js + колыбель и CouchDB.

Когда пользователь получает список своих сообщений, мне нужно получить онлайн-статус пользователя, отправившего ему сообщение. Онлайн-статус хранится в пользовательском документе для каждого зарегистрированного пользователя, а информация о сообщении хранится в отдельном документе.

Это единственный способ сделать то, что мне нужно, но он крайне неэффективен

ключ privatemessages/all = имя пользователя получателя сообщения

db.view('privatemessages/all', {"key":username}, function (err, res) {
    res.forEach(function (rowA) {
        db.view('users/all', {"key":rowA.username}, function (err, res) {
            res.forEach(function (row) {
                result.push({onlinestatus:row.onlinestatus, messagedata: rowA});
            });
        });
    });

    response.end(JSON.stringify(result));
});

Может ли кто-нибудь сказать мне правильный способ сделать это?

Спасибо


person Brian    schedule 11.08.2011    source источник


Ответы (3)


Ваш код может вернуть пустой результат, потому что вы вызываете ответ в то время, когда статусы пользователей еще не могут быть получены из БД. Другая проблема заключается в том, что если я получил несколько сообщений от одного и того же пользователя, то вызов его статуса может быть дублированным. Ниже приведена функция, которая сначала извлекает сообщения из БД, избегая дублирования пользователей, а затем получает их статусы.

function getMessages(username, callback) {
    // this would be "buffer" for senders of the messages
    var users = {};
    // variable for a number of total users I have - it would be used to determine
    // the callback call because this function is doing async jobs
    var usersCount = 0;
    // helpers vars
    var i = 0, user, item;

    // get all the messages which recipient is "username"
    db.view('privatemessages/all', {"key":username}, function (errA, resA) {
        // for each of the message
        resA.forEach(function (rowA) {
            user = users[rowA.username];
            // if user doesn't exists - add him to users list with current message
            // else - add current message to existing user
            if(!user) {
                users[rowA.username] = {
                    // I guess this is the name of the sender
                    name: rowA.username,
                    // here will come his current status later
                    status: "",
                    // in this case I may only need content, so there is probably 
                    // no need to insert whole message to array
                    messages: [rowA]
                };
                usersCount++;
            } else {
                user.messages.push(rowA);
            }
        });

        // I should have all the senders with their messages
        // and now I need to get their statuses
        for(item in users) {
            // assuming that user documents have keys based on their names
            db.get(item, function(err, doc) {
                i++;
                // assign user status
                users[item].status = doc.onlineStatus;
                // when I finally fetched status of the last user, it's time to
                // execute callback and rerutn my results
                if(i === usersCount) {
                    callback(users);
                }
            });
        }
    });
}

...

getMessages(username, function(result) {
    response.end(JSON.stringify(result));
});

Хотя CouchDB — отличная база данных документов, следует быть осторожным с частыми обновлениями существующих документов, поскольку после каждого обновления создается совершенно новая версия документа (это связано с тем, что MVCC, которая используется для достижения высокой доступности и надежности данных). Следствием такого поведения является более высокое потребление дискового пространства (больше данных/обновлений, требуется больше места на диске — example), поэтому вам следует посмотреть его и соответствующим образом запустить потребление базы данных.

person yojimbo87    schedule 11.08.2011
comment
Спасибо за ответ, это многое для меня прояснило. Можно ли каким-либо образом заставить CouchDB создавать документ, который включает пользовательские данные в исходном вызове, вместо того, чтобы извлекать данные для каждого уникального пользователя? - person Brian; 11.08.2011
comment
Я думаю, что можно было бы включить некоторые пользовательские данные в представление вашего сообщения (возможно, либо с помощью сокращения, либо представления сопоставление), но я не уверен, будут ли эти данные обновлены, если исходный пользовательский документ будет изменен. Я, вероятно, предпочел бы остаться с автономными запросами для каждого уникального пользователя. Если вам нужна скорость, вы можете объединить CouchDB, например, с Redis, что является очень хорошим сочетанием скорости и надежного сохранения данных. - person yojimbo87; 12.08.2011
comment
Еще раз спасибо, я думаю, что ваш метод будет работать, он намного менее труден для базы данных, чем я думал. У меня есть еще одна проблема, похоже, что приведенный выше код завершит обратный вызов до того, как он захватит данные, это правильно? - person Brian; 12.08.2011
comment
Если вы имеете в виду код, который я разместил в этом ответе, он должен выполнить обратный вызов после загрузки данных из БД. - person yojimbo87; 12.08.2011

Я думаю, что ваша система может использовать хэш-карту в памяти, например memcached. Срок действия каждой записи о статусе пользователя истекает по истечении определенного времени. Сопоставление будет [user -> lasttimeseen]

Если хэш-карта содержит пользователя, то пользователь находится в сети. При некоторых определенных действиях обновите последний раз.

Тогда вместо того, чтобы каждый раз пинговать весь мир, просто запросите саму карту и верните результат.

person Nicolas Modrzyk    schedule 11.08.2011

Я вспомнил об этой презентации:

Базы данных не годятся для обмена сообщениями

И его цитата из Тима О'Рейли:

«В понедельник Friendfeed опросил flickr почти 3 миллиона раз для 45 000 пользователей, только 6 тысяч из которых вошли в систему. Архитектурное несоответствие».

Как указано в других ответах, обновления в CouchDB обходятся дорого, и их следует по возможности избегать, и, вероятно, нет необходимости в том, чтобы эти данные были постоянными. Кэш или система обмена сообщениями могут решить вашу проблему более элегантно и эффективно.

person MetaThis    schedule 12.08.2011