Paginacja wyników w Cassandrze (CQL)

Zastanawiam się, jak mogę osiągnąć paginację za pomocą Cassandry.

Powiedzmy, że mam bloga. Blog zawiera maksymalnie 10 postów na stronę. Aby uzyskać dostęp do kolejnych postów użytkownik musi kliknąć w menu pagination, aby wejść na stronę 2( posty 11-20), stronę 3 (posty 21-30), itp.

Używając SQL pod MySQL, mógłbym zrobić co następuje:

SELECT * FROM posts LIMIT 20,10;

Pierwszy parametr LIMIT jest przesunięty od początku zbioru wyników, a drugi argument to ilość wierszy do pobrania. Powyższy przykład zwraca 10 wierszy począwszy od rzędu 20.

Jak mogę osiągnąć ten sam efekt w CQL?

Znalazłem kilka rozwiązań w Google, ale wszystkie z nich wymagają "ostatniego wyniku z poprzedniego zapytania". To działa na posiadanie" Dalej " przycisk do paginate do innego 10-results-set, ale co, jeśli chcę przejść ze strony 1 do strony 5?

Author: kazy, 2014-11-05

6 answers

 9
Author: phact,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 18:07:11

Nie musisz używać tokenów, jeśli używasz Cassandra 2.0+.

Cassandra 2.0 ma automatyczne przywoływanie. Zamiast używać funkcji tokena do tworzenia stronicowania, jest teraz wbudowana funkcja.

Teraz programiści mogą iterować cały zestaw wyników, bez konieczności dbania o to, aby jego rozmiar był większy niż pamięć. Gdy kod klienta jest powtarzany nad wynikami, niektóre dodatkowe wiersze mogą być pobierane, podczas gdy stare są usuwane.

Patrząc na to w Javie, zwróć uwagę, że instrukcja SELECT zwraca wszystkie wiersze, a liczba wierszy jest ustawiona na 100.

Pokazałem tutaj proste oświadczenie, ale ten sam kod można napisać za pomocą gotowego Oświadczenia, para z wiążącym oświadczeniem. Możliwe jest wyłączenie automatycznego stronicowania, jeśli nie jest to pożądane. Ważne jest również przetestowanie różnych ustawień rozmiaru pobierania, ponieważ będziesz chciał zachować zapamiętywanie na tyle małe, ale nie tak małe, że zostanie pobranych zbyt wiele podróży w obie strony do bazy danych. Sprawdź this blog post, aby zobaczyć jak działa stronicowanie po stronie serwera.

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);
}
 47
Author: Priyank Desai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-27 14:11:23

Ręczne Stronicowanie

Sterownik wyświetla obiekt PagingState, który reprezentuje miejsce, w którym byliśmy w ustawieniu wyniku, gdy ostatnia strona została pobrana:

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

Ten obiekt może być serializowany do tablicy łańcuchowej lub bajtowej:

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

Ta serializowana forma może być zapisana w jakiejś formie trwałego przechowywania, aby później zostać ponownie użyta. Gdy ta wartość zostanie pobrana później, możemy ją deserializować i ponownie umieścić w instrukcji:

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

Zauważ, że stan Przywoławczy może być tylko ponownie użyte z tą samą instrukcją (ten sam ciąg zapytania, te same parametry). Ponadto, jest nieprzezroczysta wartość, która jest przeznaczona tylko do zbierania, przechowywane ponownie używane. Jeśli spróbujesz zmodyfikować jego zawartość lub użyć go ponownie za pomocą innej instrukcji, sterownik spowoduje błąd.

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

 8
Author: Priyank Desai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-07 22:04:06

Jeśli czytasz ten dokument "użyj tokena stanu stronicowania, aby uzyskać następny wynik",

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

Możemy użyć "paging State token" do paginowania na poziomie aplikacji. Więc logika PHP powinna wyglądać tak:

<?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']);
    }
}
?>
 3
Author: Hemant Thorat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 11:06:46

Chociaż count jest dostępny w CQL, do tej pory nie widziałem dobrego rozwiązania dla offset część...

Więc... jednym z rozwiązań, które rozważałem, było tworzenie zestawów stron za pomocą procesu w tle.

W jakiejś tabeli stworzyłbym stronę bloga a jako zbiór odniesień do strony 1, 2, ... 10. Następnie kolejny wpis na stronie bloga B wskazujący na strony od 11 do 20 itd.

Innymi słowy, zbudowałbym własny indeks z kluczem wiersza ustawionym na stronie numer. Nadal możesz uczynić go nieco elastycznym, ponieważ możesz zaoferować użytkownikowi wybór, aby zobaczyć 10, 20 lub 30 referencji na stronie. Na przykład po ustawieniu na 30 wyświetlane są zestawy 1, 2 i 3 jako strona A, zestawy 4, 5, 6 jako strona B itd.)

A jeśli masz proces backendowy do obsługi tego wszystkiego, możesz aktualizować swoje listy, gdy dodawane są nowe strony, a Stare strony są usuwane z bloga. Proces powinien być naprawdę szybki (jak 1 min. na 1 000 000 rzędów, jeśli nawet tak wolno...), a następnie można znaleźć strony do wyświetlenia na liście prawie natychmiast. (Oczywiście, jeśli masz mieć tysiące użytkowników, którzy publikują setki stron... liczba ta może szybko rosnąć.)

Gdzie staje się bardziej skomplikowane, jeśli chcesz zaoferować klauzulę złożoną WHERE. Domyślnie blog pokazuje listę wszystkich postów od najnowszych do najstarszych. Możesz również oferować listy postów z tagiem Cassandra . Może chcesz odwrócić kolejność itp. To utrudnia, chyba że masz jakąś formę Zaawansowanego sposobu tworzenia indeksu(ów). Na moim końcu mam język podobny do C, który idzie i zerknąć i poke do wartości w wierszu (a) wybierz je i jeśli jest zaznaczony (b), aby je posortować. Innymi słowy, po mojej stronie mogę już mieć klauzule tak złożone, jak to, co masz w SQL. Jednak nie dzielę jeszcze swoich list na strony. Następny krok, jak sądzę...

 1
Author: Alexis Wilke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 05:50:21

Używanie sterownika cassandra-node dla node js (koa js,marko js): paginacja Problem

Z powodu braku funkcjonalności skip, musimy obejść. Poniżej znajduje się implementacja ręcznego stronicowania dla aplikacji węzła w przypadku, gdy ktoś może uzyskać pomysł.

  • kod dla prostej listy użytkowników
  • Nawiguj między Stanami następnej i poprzedniej strony
  • łatwy do replikacji

Są dwa rozwiązania, które tutaj przedstawię, ale podałem tylko kod rozwiązania 1 poniżej,

Rozwiązanie 1: utrzymanie Stanów stron dla rekordów next i previous (utrzymanie stosu lub dowolnej struktury danych najlepiej dopasowanej)

Rozwiązanie 2: Pętla przez wszystkie rekordy z limitem i zapisać wszystkie możliwe Stany strony w zmiennej i generować strony w stosunku do ich stron

Używając tego komentowanego kodu w modelu, możemy uzyskać wszystkie stany dla stron

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

Funkcje Routera

    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;
    };

Model Funkcje

    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;
        };

Strona Html

        <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>

Nie mam zbyt dużego doświadczenia z node js i Cassandrą db, To rozwiązanie z pewnością można poprawić. Rozwiązanie 1 działa przykładowy kod, aby rozpocząć od pomysłu stronicowania. Cheers

 0
Author: Suhail Mumtaz Awan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-21 10:48:57