wake-up-neo.com

Ergebnisseiten in Cassandra (CQL)

Ich frage mich, wie ich mit Cassandra eine Paginierung erreichen kann.

Sagen wir, ich habe einen Blog. Das Blog listet maximal 10 Beiträge pro Seite auf. Um auf die nächsten Beiträge zuzugreifen, muss ein Benutzer auf das Seitenaufruf-Menü klicken, um auf Seite 2 (Beiträge 11-20), Seite 3 (Beiträge 21-30) usw. zuzugreifen.

Mit SQL unter MySQL könnte ich Folgendes tun:

SELECT * FROM posts LIMIT 20,10;

Der erste Parameter von LIMIT ist vom Anfang der Ergebnismenge versetzt und das zweite Argument ist die Anzahl der abzurufenden Zeilen. Das obige Beispiel gibt 10 Zeilen ab Zeile 20 zurück.

Wie kann ich in CQL den gleichen Effekt erzielen?

Ich habe einige Lösungen bei Google gefunden, aber alle benötigen "das letzte Ergebnis der vorherigen Abfrage". Es funktioniert, wenn Sie mit der Schaltfläche "Weiter" zu einem anderen Satz von 10 Ergebnissen paginieren. Was aber, wenn ich von Seite 1 auf Seite 5 springen möchte?

28
kazy
9
phact

Sie müssen keine Tokens verwenden, wenn Sie Cassandra 2.0+ verwenden.

Cassandra 2.0 hat Auto Paging. Anstatt die Token-Funktion zum Erstellen von Paging zu verwenden, handelt es sich jetzt um eine integrierte Funktion.

Jetzt können Entwickler die gesamte Ergebnismenge durchlaufen, ohne sich darum zu kümmern, dass die Größe des Speichers größer ist. Während der Clientcode die Ergebnisse durchläuft, können einige zusätzliche Zeilen abgerufen werden, während alte Zeilen gelöscht werden. 

Wenn Sie dies in Java betrachten, beachten Sie, dass die SELECT-Anweisung alle Zeilen zurückgibt und die Anzahl der abgerufenen Zeilen auf 100 festgelegt ist. 

Ich habe hier eine einfache Anweisung gezeigt, aber derselbe Code kann mit einer vorbereiteten Anweisung geschrieben werden, mit einer gebundenen Anweisung. Das automatische Paging kann deaktiviert werden, wenn dies nicht gewünscht ist. Es ist auch wichtig, verschiedene Einstellungen für die Abrufgröße zu testen, da Sie die Speicherung so klein wie möglich halten möchten, jedoch nicht so klein, dass zu viele Roundtrips zur Datenbank durchgeführt werden. Check out this blog post, um zu sehen, wie paging serverseitig funktioniert.

Statement stmt = new SimpleStatement(
                  "SELECT * FROM raw_weather_data"
                  + " WHERE wsid= '725474:99999'"
                    + " AND year = 2005 AND month = 6");
stmt.setFetchSize(24);
ResultSet rs = session.execute(stmt);
Iterator<Row> iter = rs.iterator();
while (!rs.isFullyFetched()) {
   rs.fetchMoreResults();
   Row row = iter.next();
   System.out.println(row);
}
49
Priyank Desai

Manuelles Paging

Der Treiber macht ein PagingState-Objekt verfügbar, das darstellt, wo sich die Ergebnismenge befand, als die letzte Seite abgerufen wurde:

ResultSet resultSet = session.execute("your query");
// iterate the result set...
PagingState pagingState = resultSet.getExecutionInfo().getPagingState();

Dieses Objekt kann zu einem String- oder Byte-Array serialisiert werden:

String string = pagingState.toString();
byte[] bytes = pagingState.toBytes();

Diese serialisierte Form kann in einer Art permanenter Speicherung gespeichert werden, um sie später wiederzuverwenden. Wenn dieser Wert später abgerufen wird, können wir ihn deserialisieren und in einer Anweisung erneut verwenden:

PagingState pagingState = PagingState.fromString(string);
Statement st = new SimpleStatement("your query");
st.setPagingState(pagingState);
ResultSet rs = session.execute(st);

Beachten Sie, dass der Paging-Status nur mit derselben Anweisung (gleiche Abfragezeichenfolge, gleiche Parameter) wiederverwendet werden kann. Es ist auch ein undurchsichtiger Wert, der nur gesammelt, gespeichert und wiederverwendet werden soll. Wenn Sie versuchen, den Inhalt zu ändern oder mit einer anderen Anweisung wiederzuverwenden, wird vom Treiber ein Fehler ausgegeben.

Src: http://datastax.github.io/Java-driver/manual/paging/

8
Priyank Desai

Wenn Sie dieses Dokument "Paging-Status-Token verwenden, um das nächste Ergebnis zu erhalten" lesen,

https://datastax.github.io/php-driver/features/result_paging/

Wir können "Paging-Status-Token" verwenden, um auf Anwendungsebene zu paginieren. Also PHP Logik sollte so aussehen,

<?php
$limit = 10;
$offset = 20;

$cluster   = Cassandra::cluster()->withContactPoints('127.0.0.1')->build();
$session   = $cluster->connect("simplex");
$statement = new Cassandra\SimpleStatement("SELECT * FROM paging_entries Limit ".($limit+$offset));

$result = $session->execute($statement, new Cassandra\ExecutionOptions(array('page_size' => $offset)));
// Now $result has all rows till "$offset" which we can skip and jump to next page to fetch "$limit" rows.

while ($result->pagingStateToken()) {
    $result = $session->execute($statement, new Cassandra\ExecutionOptions($options = array('page_size' => $limit,'paging_state_token' => $result->pagingStateToken())));
    foreach ($result as $row) {
      printf("key: '%s' value: %d\n", $row['key'], $row['value']);
    }
}
?>
2
Hemant Thorat

Obwohl das count in CQL verfügbar ist, habe ich bisher keine gute Lösung für den offsetTeil gesehen ...

Eine Lösung, über die ich nachgedacht habe, war das Erstellen von Seiten mit einem Hintergrundprozess.

In einer Tabelle würde ich die Blogseite A als eine Reihe von Verweisen auf Seite 1, 2, ... erstellen. 10. Dann ein weiterer Eintrag für Blogseite B, der auf die Seiten 11 bis 20 zeigt usw.

Mit anderen Worten, ich würde meinen eigenen Index mit einem Zeilenschlüssel erstellen, der auf die Seitennummer gesetzt ist. Sie können es dennoch etwas flexibel gestalten, da Sie dem Benutzer anbieten können, 10, 20 oder 30 Referenzen pro Seite anzuzeigen. Bei einer Einstellung von 30 zeigen Sie beispielsweise die Sätze 1, 2 und 3 als Seite A an, die Sätze 4, 5, 6 als Seite B usw.).

Wenn Sie über einen Back-End-Prozess verfügen, können Sie Ihre Listen aktualisieren, wenn neue Seiten hinzugefügt und alte Seiten aus dem Blog gelöscht werden. Der Prozess sollte sehr schnell sein (z. B. 1 Minute für 1.000.000 Zeilen, wenn auch so langsam ...), und dann können Sie die Seiten so schnell wie möglich in Ihrer Liste anzeigen. (Wenn Sie Tausende von Benutzern haben, die jeweils Hunderte von Seiten veröffentlichen, kann diese Zahl schnell anwachsen.)

Komplizierter wird es, wenn Sie eine komplexe WHERE-Klausel anbieten möchten. Standardmäßig zeigt ein Blog eine Liste aller Beiträge vom neuesten bis zum ältesten. Sie können auch Listen mit Beiträgen mit dem Tag Cassandra anbieten. Vielleicht möchten Sie die Reihenfolge umkehren, usw. Das macht es schwierig, es sei denn, Sie haben einen fortgeschrittenen Weg, Ihre Indizes zu erstellen. An meinem Ende habe ich eine C-artige Sprache, die zu den Werten in einer Reihe geht, um (a) sie auszuwählen und (b) zu sortieren, um sie zu sortieren. In anderen Worten, ich kann WHERE-Klauseln bereits so komplex haben wie das, was Sie in SQL hätten. Ich teile meine Listen jedoch noch nicht in Seiten auf. Nächster Schritt nehme ich an ...

1
Alexis Wilke

Verwendung des Kassandra-Knotentreibers für Knoten js (koa js, marko js): Pagination Problem

Da keine Skip-Funktion vorhanden ist, müssen wir etwas umgehen. Im Folgenden finden Sie die Implementierung des manuellen Paging für die Knoten-App, falls sich jemand eine Vorstellung davon machen kann.

  • code für einfache Benutzerliste
  • navigieren Sie zwischen den nächsten und vorherigen Seitenstatus
  • einfach zu replizieren

Es gibt zwei Lösungen, die ich hier angeben werde, aber nur den Code für Lösung 1 angegeben habe.

Lösung 1: Seitenstatus für next- und previous -Datensätze beibehalten (Verwalten des Stacks oder der am besten geeigneten Datenstruktur)

Lösung 2: Durchlaufen Sie alle Datensätze mit Limit und speichern Sie alle möglichen Seitenstatusvariablen und generieren Sie Seiten relativ zu ihren Seitenstatus

Mit diesem kommentierten Code in model können wir alle Status für Seiten abrufen

            //for the next flow
            //if (result.nextPage) {
            // Retrieve the following pages:
            // the same row handler from above will be used
            // result.nextPage();
            //}

Routerfunktionen

    var userModel = require('/models/users');
          public.get('/users', users);
          public.post('/users', filterUsers);

    var users = function* () {//get request
        var data = {};
        var pageState = { "next": "", "previous": "" };
        try {
            var userCount = yield userModel.Count();//count all users with basic count query

            var currentPage = 1;
            var pager = yield generatePaging(currentPage, userCount, pagingMaxLimit);
            var userList = yield userModel.List(pager);
            data.pageNumber = currentPage;
            data.TotalPages = pager.TotalPages;
            console.log('--------------what now--------------');
            data.pageState_next = userList.pageStates.next;
            data.pageState_previous = userList.pageStates.previous;
            console.log("next ", data.pageState_next);
            console.log("previous ", data.pageState_previous);

            data.previousStates = null;

            data.isPrevious = false;
            if ((userCount / pagingMaxLimit) > 1) {
                data.isNext = true;
            }

            data.userList = userList;
            data.totalRecords = userCount;
            console.log('--------------------userList--------------------', data.userList);
            //pass to html template
        }
        catch (e) {
            console.log("err ", e);
            log.info("userList error : ", e);
        }
   this.body = this.stream('./views/userList.marko', data);
   this.type = 'text/html';
    };

    //post filter and get list
    var filterUsers = function* () {
        console.log("<------------------Form Post Started----------------->");
        var data = {};
        var totalCount;
        data.isPrevious = true;
        data.isNext = true;

        var form = this.request.body;
        console.log("----------------formdata--------------------", form);
        var currentPage = parseInt(form.hdpagenumber);//page number hidden in html
        console.log("-------before current page------", currentPage);
        var pageState = null;
        try {
            var statesArray = [];
            if (form.hdallpageStates && form.hdallpageStates !== '') {
                statesArray = form.hdallpageStates.split(',');
            }
            console.log(statesArray);

            //develop stack to track paging states
            if (form.hdpagestateRequest === 'next') {
                console.log('--------------------------next---------------------');
                currentPage = currentPage + 1;
                statesArray.Push(form.hdpageState_next);
                pageState = form.hdpageState_next;
            }
            else if (form.hdpagestateRequest === 'previous') {
                console.log('--------------------------pre---------------------');
                currentPage = currentPage - 1;
                var p_st = statesArray.length - 2;//second last index
                console.log('this index of array to be removed ', p_st);
                pageState = statesArray[p_st];
                statesArray.splice(p_st, 1);
                //pageState = statesArray.pop();
            }
            else if (form.hdispaging === 'false') {
                currentPage = 1;
                pageState = null;
                statesArray = [];
            }


            data.previousStates = statesArray;
            console.log("paging true");

            totalCount = yield userModel.Count();

            var pager = yield generatePaging(form.hdpagenumber, totalCount, pagingMaxLimit);
            data.pageNumber = currentPage;
            data.TotalPages = pager.TotalPages;

            //filter function - not yet constructed
            var searchUsers = yield userModel.searchList(pager, pageState);
            data.usersList = searchUsers;
            if (searchUsers.pageStates) {
                data.pageStates = searchUsers.pageStates;
                data.next = searchUsers.nextPage;
                data.pageState_next = searchUsers.pageStates.next;
                data.pageState_previous = searchUsers.pageStates.previous;

                //show previous and next buttons accordingly
                if (currentPage == 1 && pager.TotalPages > 1) {
                    data.isPrevious = false;
                    data.isNext = true;
                }
                else if (currentPage == 1 && pager.TotalPages <= 1) {
                    data.isPrevious = false;
                    data.isNext = false;
                }
                else if (currentPage >= pager.TotalPages) {
                    data.isPrevious = true;
                    data.isNext = false;
                }
                else {
                    data.isPrevious = true;
                    data.isNext = true;
                }
            }
            else {
                data.isPrevious = false;
                data.isNext = false;
            }
            console.log("response ", searchUsers);
            data.totalRecords = totalCount;

           //pass to html template
        }
        catch (e) {
            console.log("err ", e);
            log.info("user list error : ", e);
        }
        console.log("<------------------Form Post Ended----------------->");
   this.body = this.stream('./views/userList.marko', data);
   this.type = 'text/html';
    };

    //Paging function
    var generatePaging = function* (currentpage, count, pageSizeTemp) {
        var paging = new Object();
        var pagesize = pageSizeTemp;
        var totalPages = 0;
        var pageNo = currentpage == null ? null : currentpage;
        var skip = pageNo == null ? 0 : parseInt(pageNo - 1) * pagesize;
        var pageNumber = pageNo != null ? pageNo : 1;
        totalPages = pagesize == null ? 0 : Math.ceil(count / pagesize);
        paging.skip = skip;
        paging.limit = pagesize;
        paging.pageNumber = pageNumber;
        paging.TotalPages = totalPages;
        return paging;
    };

Modellfunktionen

    var clientdb = require('../utils/cassandradb')();
    var Users = function (options) {
      //this.init();
      _.assign(this, options);
    };

    Users.List = function* (limit) {//first time
            var myresult; var res = [];
            res.pageStates = { "next": "", "previous": "" };

            const options = { prepare: true, fetchSize: limit };
            console.log('----------did i appeared first?-----------');

            yield new Promise(function (resolve, reject) {
                clientdb.eachRow('SELECT * FROM users_lookup_history', [], options, function (n, row) {
                    console.log('----paging----rows');
                    res.Push(row);
                }, function (err, result) {
                    if (err) {
                        console.log("error ", err);
                    }
                    else {
                        res.pageStates.next = result.pageState;
                        res.nextPage = result.nextPage;//next page function
                    }
                    resolve(result);
                });
            }).catch(function (e) {
                console.log("error ", e);
            }); //promise ends

            console.log('page state ', res.pageStates);
            return res;
        };

        Users.searchList = function* (pager, pageState) {//paging filtering
            console.log("|------------Query Started-------------|");
            console.log("pageState if any ", pageState);
            var res = [], myresult;
            res.pageStates = { "next": "" };
            var query = "SELECT * FROM users_lookup_history ";
            var params = [];

            console.log('current pageState ', pageState);
            const options = { pageState: pageState, prepare: true, fetchSize: pager.limit };
            console.log('----------------did i appeared first?------------------');

            yield new Promise(function (resolve, reject) {
                clientdb.eachRow(query, [], options, function (n, row) {
                    console.log('----Users paging----rows');
                    res.Push(row);
                }, function (err, result) {
                    if (err) {
                        console.log("error ", err);
                    }
                    else {
                        res.pageStates.next = result.pageState;
                        res.nextPage = result.nextPage;
                    }
                    //for the next flow
                    //if (result.nextPage) {
                    // Retrieve the following pages:
                    // the same row handler from above will be used
                    // result.nextPage();
                    //}
                    resolve(result);
                });
            }).catch(function (e) {
                console.log("error ", e);
                info.log('something');
            }); //promise ends

            console.log('page state ', pageState);

            console.log("|------------Query Ended-------------|");
            return res;
        };

Html-Seite

        <div class="box-footer clearfix">
        <ul class="pagination pagination-sm no-margin pull-left">
             <if test="data.isPrevious == true">
             <li><a class='submitform_previous' href="">Previous</a></li>
             </if>
             <if test="data.isNext == true">
                <li><a class="submitform_next" href="">Next</a></li>
             </if>
         </ul>
         <ul class="pagination pagination-sm no-margin pull-right">
                    <li>Total Records : $data.totalRecords</li>&nbsp;&nbsp;
                    <li> | Total Pages : $data.TotalPages</li>&nbsp;&nbsp;
                    <li> | Current Page : $data.pageNumber</li>&nbsp;&nbsp;
         </ul>
         </div>

Ich habe nicht viel Erfahrung mit dem Knoten js und cassandra db, diese Lösung kann sicherlich verbessert werden. Lösung 1 arbeitet als Beispielcode, um mit der Paging-Idee zu beginnen. Prost

0