Pubnub.history() 分页

Pubnub.history() paging

我正在尝试为 pubnub.history() 实施分页。这是我目前所拥有的:

尝试 1: 这会从时间开始获取 第一个 count 条消息:

function getHistory(chnl,count, token) {
    var pagesize=10;
    console.log('Starting at ' + token + ', getting ' + count);
    pubnub.history({
        channel : chnl,
        callback : function(m){
            var msgs = m[0];
            var msgcount = msgs.length;
            var startToken = m[1];
            var endToken = m[2];
            for (var i = 0; i < msgcount; i++) {
                var msg = msgs[i];
                processMessage(chnl,msg);
            }
            if (count >= pagesize) {
                getHistory(chnl,count - pagesize,endToken);
            }
        },
        count: pagesize,
        start: token,
        reverse: true
    });
}

尝试 2: 这一次又一次地获取最新的 pagesize 消息。 token每次都一样:

function getHistory(chnl,count, token) {
    var pagesize=10;
    console.log('Starting at ' + token + ', getting ' + count);
    pubnub.history({
        channel : chnl,
        callback : function(m){
            var msgs = m[0];
            var msgcount = msgs.length;
            var startToken = m[1];
            var endToken = m[2];
            for (var i = 0; i < msgcount; i++) {
                var msg = msgs[i];
                processMessage(chnl,msg);
            }
            if (count >= pagesize) {
                getHistory(chnl,count - pagesize,startToken);
            }
        },
        count: pagesize,
        end: token,
        reverse: false
    });
}

我需要获取最后的 count 条消息,以最新的 结尾。我该如何修改我的函数来执行此操作?


更新:我的新 API v4 代码:

function getHistory(chnl,count, token) {
    var pagesize=10;
    console.log('Starting at ' + token + ', getting ' + count);
    pubnub.history({
        channel : chnl,
        callback : function(m){

        },
        count: pagesize,
        start: token,
        reverse: true
    },function(status, response) {
        var msgs = response.messages;
        var msgcount = msgs.length;
        var startToken = response.startTimeToken;
        var endToken = response.endTimeToken;
        for (var i = 0; i < msgcount; i++) {
            var msg = msgs[i];
            processMessage(msg);
        }
        if (count >= pagesize) {
            getHistory(chnl,count - pagesize,endToken);
        }
    });
}

如果我请求 1000,这就是它现在正在做的事情:

2016-10-25 13:29:59.060 pubnub.js:23 Starting at 14772721032416580, getting 620
2016-10-25 13:29:59.385 pubnub.js:23 Starting at 14772748031396800, getting 610
2016-10-25 13:29:59.678 pubnub.js:23 Starting at 14772778027380036, getting 600
2016-10-25 13:30:00.024 pubnub.js:23 Starting at 14772808029462014, getting 590
2016-10-25 13:30:00.440 pubnub.js:23 Starting at 14772838027743668, getting 580
2016-10-25 13:30:00.772 pubnub.js:23 Starting at 14772868026780088, getting 570
2016-10-25 13:30:01.116 pubnub.js:23 Starting at 14772898023567196, getting 560
2016-10-25 13:30:01.426 pubnub.js:23 Starting at 14772928025649280, getting 550
2016-10-25 13:30:01.726 pubnub.js:23 Starting at 14772958026355252, getting 540
2016-10-25 13:30:02.113 pubnub.js:23 Starting at 14773220007794584, getting 530
2016-10-25 13:30:02.457 pubnub.js:23 Starting at 14774200653664656, getting 520
2016-10-25 13:30:02.606 pubnub.js:23 Starting at 14774200653664656, getting 510
2016-10-25 13:30:02.690 pubnub.js:23 Starting at 14774200653664656, getting 500
2016-10-25 13:30:02.790 pubnub.js:23 Starting at 14774200653664656, getting 490
2016-10-25 13:30:02.922 pubnub.js:23 Starting at 14774200653664656, getting 480
2016-10-25 13:30:03.015 pubnub.js:23 Starting at 14774200653664656, getting 470

正如您在本例中看到的那样,实际上并没有 1000 条消息,因此在 13:30:02.467 它开始一遍又一遍地收到相同的消息。然后我请求了 300,它成功了,但是它得到了 first 300,而不是最近的 300。

所以还有两个问题:

  1. 如果请求的数量超过现有数量,则获取所有消息。
  2. 获取最近 x 条消息。

PubNub 存储 - 历史分页

一些设置

  • 我整理了一个递归分页函数和一组测试它的步骤。请记住,此代码尽可能简单,同时仍然对 POC 具有一定的功能,但这不是最佳实践。
  • 这是 PubNub JavaScript SDK v4,而不是最初提出问题的 v3。

首先,使用您自己的密钥初始化一个 PubNub 实例

// init PubNub
var pubnub = new PubNub({
    publishKey   : 'pub-...',
    subscribeKey : 'sub-...'
})

其次,创建一个函数来填充频道

我们想创建一个包含消息的频道,我们可以轻松理解这些消息的顺序:消息#1、消息#2 等

我们使用 setInterval 在发布的消息之间添加一些延迟。首先,我们要确保消息按照我们发布的顺序存储(请记住,它都是异步的,包括在服务器端)并且我们希望我们的消息时间标记之间有一些 时间间隔 .

function pub(channel, total) {
    var i = 1;

    var looper = setInterval(
        function() {
            pubnub.publish({
                channel: channel,
                message: "message #" + i++
            });

            if (i > total) {
                clearInterval(looper);
            }
        }, 
    400);
}

您可以创建多个包含不同数量消息的不同测试通道,但一个包含 32 条消息的通道应该可以用于简单的历史分页测试。您想要一个 非圆形 数字,这样您就可以按 5 或 10 翻页,看看不均匀的最终结果会给您带来什么。使用另一个 整数 的频道也可以看到这一点。

最后,创建历史分页功能

它有点粗糙,但它完成了工作,没有太多花哨的东西来妨碍指出正在发生的事情。它使用递归而不是最佳方式。

function getMessages(args, callback) {
    pubnub.history(
        {
            // search starting from this timetoken
            start: args.startToken,
            channel: args.channel,
            // false - search forwards through the timeline
            // true - search backwards through the timeline
            reverse: args.reverse,
            // limit number of messages per request to this value; default/max=100
            count: args.pagesize,
            // include each returned message's publish timetoken
            includeTimetoken: true,
            // prevents JS from truncating 17 digit timetokens
            stringifiedTimeToken: true
        },
        function(status, response) {
            // holds the accumulation of resulting messages across all iterations
            var results = args.results;
            // the retrieved messages from history for this iteration only
            var msgs = response.messages;
            // timetoken of the first message in response
            var firstTT = response.startTimeToken;
            // timetoken of the last message in response
            var lastTT = response.endTimeToken;
            // if no max results specified, default to 500
            args.max = !args.max ? 500 : args.max;

            if (msgs != undefined && msgs.length > 0) {
                // display each of the returned messages in browser console
                for (var i in msgs) {
                    msg = msgs[i];
                    console.log(msg.entry, msg.timetoken);
                }

                // first iteration, results is undefined, so initialize with first history results
                if (!results) results = msgs;
                // subsequent iterations, results has previous iterartions' results, so concat
                // but concat to end of results if reverse true, otherwise prepend to begining of results
                else args.reverse ? results = results.concat(msgs) : results = msgs.concat(results);
            }

            // show the total messages returned out of the max requested
            console.log('total    : ' + results.length + '/' + args.max);

            // we keep asking for more messages if # messages returned by last request is the
            // same at the pagesize AND we still have reached the total number of messages requested
            // same as the opposit of !(msgs.length < pagesize || total == max)
            if (msgs.length == args.pagesize && results.length < args.max) {
                getMessages(
                    {
                        channel:args.channel, max:args.max, reverse:args.reverse, 
                        pagesize:args.pagesize, startToken:args.reverse ? lastTT : firstTT, results:results
                    }, 
                    callback);
            }
            // we've reached the end of possible messages to retrieve or hit the 'max' we asked for
            // so invoke the callback to the original caller of getMessages providing the total message results
            else callback(results);
        }
    );
}

测试寻呼

我们假设我们有一个名为 test1 的频道,存储中有 32 条消息,如下所示:

  • 消息#1
  • 消息 #2

...

  • 消息 #32

每个都有一个独特的时间标记。

调用 getMessages 函数以获得以下预期结果:

1.从最早的消息开始最多获取 50 条消息,一次 5 条消息

getMessages(
    {
        channel: 'test1', max: 50, pagesize: 5, reverse: true
    },
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

2。从最早的消息开始最多获取 20 条消息,一次 5 条消息

getMessages(
    {
        channel: 'test1', max: 20, pagesize: 5, reverse: true
    },
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

3。从最新消息开始最多获取 50 条消息,一次 5 条消息

getMessages(
    {
        channel: 'test1', max: 50, pagesize: 5, reverse: false
    },
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

4.从最新消息开始最多获取 20 条消息,一次 5 条消息

getMessages(
    {
        channel: 'test1', max: 20, pagesize: 5, reverse: false
    },
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

所以你应该看到每次调用 history 的返回消息的 order 总是从旧到新(升序)。但是搜索消息的顺序取决于 reverse 参数。

  • reverse:true 将检索从最旧到最新的消息块:1,2,3,4,5 | 6,7,8,9,10 | ...
  • reverse:false 将检索从最新到最旧的消息块:28,29,30,31,32 | 23,24,25,26,27 | ...

所以重申一下,每个响应中消息的顺序是相同的,但是 search/retrieve 的方向不同。注意:这是新的 Storage/History 设计将通过更简单、更强大的 API 来解决的问题(例如:getMessagesSincegetMessagesBeforegetMessagesBetween)。

现在让我们介绍 startToken 参数,以便我们可以从频道的历史时间轴中的给定点检索消息或从给定点检索消息。您指定的时间标记将是您需要从频道历史记录中的现有消息中选择的时间标记。你和我会有所不同,因为我们在不同的时间用不同的密钥发布。我将在以下示例中使用 startToken:14774567814936359 并假设这是 消息 #17.

的时间标记

5.从时间令牌 14774567814936359 处的消息开始最多获取 20 条消息,一次获取 5 条消息(请记住 start 参数是 exclusive 所以你不会在结果中返回 消息 #17)

getMessages(
    {
        channel: 'test1', max: 20, pagesize: 5, reverse: true,
        startToken:"14774567814936359"
    }, 
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

注意:您必须将时间标记值作为字符串传递(请注意 startToken 参数值在上方和下方引用)否则 JavaScript 会将它们四舍五入。例如,14774567814936359 将变为 14774567814936360,您会得到意想不到的结果。继续尝试不带引号,您将看到提供的时间标记处的消息将被检索,但它应该被排除在外。您可以在浏览器的“网络”选项卡中查看提交的历史记录 URL 中的舍入时间标记。

6.在 timetoken 14774567814936359 消息之前最多获取 20 条消息,一次获取 5 条消息

getMessages(
    {
        channel: 'test1', max: 50, pagesize: 5, reverse: false,
        startToken:"14774567814936359"
    }, 
    function(results) {
        console.log("results: \n" + JSON.stringify(results));       
    }
);

我们可以继续提供更多示例,但我会让您 (Scott) 和其他人询问操作示例,我可以根据需要提供。

订购历史结果

getMessages 函数以预先排序的升序组合生成的消息数组,即使消息是从最新到最旧的顺序检索的。它通过预先新的历史结果和先前迭代结果的当前累积来实现。

如果希望按时间标记的降序对消息进行排序,那么实现排序比较器函数就相当简单。我在这里提供一个简单的例子:

function sortHistory(messages, desc, callback) {
    messages.sort(function(a, b) {
        var e1 = desc ? b : a;
        var e2 = desc ? a : b;
        return parseInt(e1.timetoken) - parseInt(e2.timetoken);
    });
    
    callback(messages);
}

这个sortHistory函数接受messages数组(一个JSON元素的数组:{message, timetokens}),一个指示所需排序顺序的标志(默认为升序,设置为true用于降序),以及将使用生成的排序数组消息调用的函数。

现在让我们用 revers:true 调用 getMessages。这意味着消息将按从旧到新的顺序检索,如果我们想要升序排序,那么它已经按该顺序排列了。

getMessages({channel: 'test1', max: 100, pagesize: 5, reverse: true}, 
    function(results) {
        console.log("presorted: \n" + JSON.stringify(results));       
    }
);

如果我们想按降序对消息进行排序,只需添加对 sortHistory 函数的调用即可。

getMessages({channel: 'test1', max: 100, pagesize: 5, reverse: true}, 
    function(results) {
        console.log("before sort: \n" + JSON.stringify(results));

        // sort messages in descending order
        sortHistory(results, true, function(sorted) {
            console.log("after sort: \n" + JSON.stringify(sorted));
        });        
    }
);

并且如果我们检索 reverse:false 的消息,从新到旧,那么我们仍然会得到按升序组合的消息,即使以 pagesize (16,17, 16,17, 18、19、20 | 11、12、13、14、15...)。但是如果我们想降序排序,同样,只需要调用sortHistory函数。

getMessages({channel: 'test1', max: 100, pagesize: 5, reverse: false}, 
    function(results) {
        console.log("before sort: \n" + JSON.stringify(results));

        // sort messages in descending order
        sortHistory(results, true, function(sorted) {
            console.log("after sort: \n" + JSON.stringify(sorted));
        });        
    }
);

I updated the jsfiddle with new code and I made a lot of mods to this answer.

查看 PubNub JavaScript SDK Storage (history) Docs/Tutorial] for full details and the PubNub Storage and History tutorial