9

Mi piace la semplicità, la scalabilità e la facilità d'uso del datastore; e i miglioramenti trovati nella nuova libreria ndb sono favolosi.ricerca di idee/alternative alla fornitura di una pagina/numero di elementi/navigazione di articoli che corrispondono a una query del datastore GAE

Come comprendo le best practice del datastore, non si dovrebbe scrivere codice per fornire il conteggio degli articoli e/o delle pagine dei risultati della query corrispondenti quando il numero di elementi corrispondenti a una query è elevato; perché l'unico modo per farlo è recuperare tutti i risultati che richiedono un uso intensivo delle risorse.

Tuttavia, in molte applicazioni, inclusa la nostra, è comune il desiderio di vedere il conteggio degli articoli corrispondenti e di fornire all'utente la possibilità di navigare verso una pagina specifica di tali risultati. Il problema del paging del datastore è ulteriormente complicato dall'obbligo di aggirare i limiti di fetch (limite, offset = X) come indicato nell'articolo Paging Through Large Datasets. Per supportare l'approccio consigliato, i dati devono includere una colonna con un valore univoco che può essere ordinata nel modo in cui i risultati devono essere visualizzati. Questa colonna definirà un valore iniziale per ogni pagina di risultati; salvandolo, possiamo recuperare la pagina corrispondente in modo efficiente, consentendo la navigazione verso una pagina specifica o successiva come richiesto. Pertanto, se si desidera visualizzare i risultati ordinati in più modi, potrebbe essere necessario mantenere diverse colonne di questo tipo.

Si segnala che a partire dal SDK v1.3.1, Query Cursors sono il modo consigliato di fare datastore di paging. Hanno alcune limitazioni, inclusa la mancanza di supporto per gli operatori di filtri IN e! =. Attualmente alcune delle nostre query importanti utilizzano IN, ma proveremo a scriverle usando O per l'utilizzo con i cursori di query.

Seguendo le linee guida suggerite, un utente potrebbe essere data una (Avanti) e (Prev) pulsanti di navigazione, così come i tasti di pagine specifiche come la navigazione procedeva. Ad esempio se l'utente ha premuto (Avanti) per 3 volte, l'app potrebbe mostrare i seguenti pulsanti, ricordando l'unico record di inizio o cursore per ciascuno per mantenere efficiente la navigazione: (Indietro) (Pagina-1) (Pagina-2)) (Pagina-3) (Pagina-4) (Successivo).

Alcuni hanno suggerito di tenere traccia dei conteggi separatamente, ma questo approccio non è pratico quando gli utenti potranno interrogare su un ricco set di campi che varieranno i risultati restituiti.

sto cercando approfondimenti su questi temi in generale e alle seguenti domande in particolare:

  1. Che navigazione opzioni dei risultati delle query offrite nei vostri apps datastore per aggirare queste limitazioni?

  2. Se fornendo agli utenti conteggio dei risultati efficienti e pagina di navigazione del l'intero set di risultati della query è una priorità, dovrebbe usare il datastore essere abbandonato a favore della GAE MySql solution ora offerto.

  3. Ci sono cambiamenti imminenti nell'architettura della tabella grande o nell'implementazione dello storage di dati che fornirà funzionalità aggiuntive per contando i risultati di una query in modo efficiente?

Molte grazie in anticipo per il vostro aiuto.

risposta

2

Tutto dipende da quanti risultati si ottengono in genere. Per esempio. superando .count() un limite adatto puoi fornire un conteggio esatto se il numero # è ad es. < = 100 e "molti" se ce ne sono altri. Sembra che non sia possibile pre-calcolare tutti i possibili conteggi, ma almeno si potrebbero memorizzare nella cache, risparmiando così molte operazioni di archiviazione dati.

Utilizzando NDB, l'approccio più efficiente può essere quello di richiedere la prima pagina di entità utilizzando fetch_page(), e quindi utilizzare il cursore risultante come punto di partenza per una chiamata count(); o in alternativa, è possibile che si stia meglio eseguendo il recupero() della prima pagina e il conteggio() contemporaneamente utilizzando le sue funzioni asincrone. La seconda opzione potrebbe essere la tua unica scelta se la tua query non supporta i cursori. La maggior parte delle query IN/OR attualmente non supporta i cursori, ma lo fanno se ordini per __key__.

In termini di opzioni dell'interfaccia utente, penso sia sufficiente offrire le opzioni della pagina successiva e precedente; l'interfaccia utente "Gooooooogle" che consente di ignorare diverse pagine è carina ma non la utilizzo quasi mai da solo. (Per implementare la "pagina precedente", invertire l'ordine della query e utilizzare lo stesso cursore utilizzato per la pagina corrente. Sono sicuro che questo è garantito per funzionare.)

+0

Supponendo che usiamo l'approccio C = query.count (N) per mostrare all'utente "1-20 di C" o "1-20 di molti, come possiamo determinare un valore ragionevole, costo saggio, per N. Nel nostro uso caso 100 sarà troppo piccolo.Qualsiasi suggerimento su come dimensionare al meglio questo per mantenere il costo $ in calo? Da documenti NDB: "Si noti che count(), mentre più veloce di fetch(), fa ancora molto lavoro ogni volta che viene chiamato ". Quanta quota è utilizzata? Guido, Grazie per Python, NDB e il tuo aiuto :) I conteggi e nav IMO sono una funzionalità utile per alcune app perché gli utenti possono valutare e regolare le dimensioni dei dati corrispondenti ai propri parametri prima di perforare. –

+1

Puoi calcolare il costo utilizzando questa pagina: http://code.google.com/appengine/docs/billing.html#Billable_Resource_Unit_Costs. AFAIK a Count() è come una query solo per chiavi. conta. (A seconda del professionista blem, se hai un numero limitato di conteggi da memorizzare nella cache, potresti essere in grado di memorizzare i conteggi nel datastore usando il modello del contatore di caratteri.) –

+3

Anche un aggiornamento sulle query IN/OR: puoi trasformare qualsiasi query in un cursore -sostenendo la query aggiungendo un ordinamento di __key__ alla fine dell'ordine di ordinamento esistente. Per esempio. in NDB: 'Employee.query (Employee.name.IN (['Joe', 'Jane'])). order (Employee.name, Employee.key) .fetch_page (N)' - senza l'ordine Employee.key solleva BadArgumentError. –

0
  1. ho notato che Gmail è pronta con alcuni conteggi - si può dire quante email totale che hai ricevuto, e quanti sono nella tua casella di posta, ecc - ma su altri conti, come il full-text ricerche esso dice che stai guardando "1-20 di molti" o "1-20 di circa 130". Hai davvero bisogno di visualizzare i conteggi per ogni query, o potresti pre-calcolare solo quelli importanti?
+0

Tenere traccia di un ben noto totale è decisamente più semplice. In alcuni articoli ho letto che la gente utilizza contatori semplificati per questo su GAE: http://code.google.com/appengine/articles/sharding_counters.html. Il nostro caso d'uso è più simile alle ricerche full-text di gmail. Penso che mostrare i totali e la navigazione delle pagine offra agli utenti una migliore comprensione dei loro risultati di ricerca prima di eseguire il drill-up. Detto questo, sembra che se si utilizza datastore, l'unica opzione è l'approccio "1-20 di molti" che mostra alcune rivelazioni qui. Probabilmente Gmail mostra "1-20 di circa 130" quando il set di risultati è piccolo, forse guardando avanti. –

1

Forse solo scopo per questo stile di paging :

(prima) (Prec) (Pagina1) (Pagina2) (Pagina3) .... (last) (successiva)

In questo modo il numero totale non è necessario - è necessario solo il codice per sapere che ci sono risultati sufficienti per altre 3+ pagine. con una dimensione della pagina di 10 elementi per pagina, devi solo sapere che ci sono più di 30 articoli.

Se si dispone di 60 elementi, (sufficiente per 6 pagine) quando si è già a pagina 4, il codice potrebbe guardare in avanti e realizzare ci sono solo altri 20 record, quindi è possibile visualizzare l'ultimo numero di pagina:

(prima) (Prec) (Pagina4) (Pagina5) (pagina 6) (vicino) (ultimo)

in sostanza per ogni fetch per la pagina corrente, basta prendere abbastanza dischi per altri 3 pagine di dati, contali per vedere quante più pagine hai, quindi esponi il tuo cercapersone di conseguenza.

Inoltre, se si recuperano solo le chiavi, sarà più efficiente del recupero di elementi aggiuntivi. speranza che abbia un senso !! ?? :)

+0

Hey @Joe grazie per il suggerimento. Hai catturato ciò che descrivo sopra nel 5 ° paragrafo della mia domanda originale. La risposta di Guido acuisce l'attenzione su ciò che è possibile. Dato il costo della query di conteggio, per un set di dati di grandi dimensioni useremo sicuramente qualcosa come "..." quando N è inferiore al numero totale di risultati. Vedi la discussione sotto la risposta di Guido per più contesto. –

0

Poiché la domanda era "cercare idee/alternative per fornire una pagina", forse l'alternativa molto semplice di recuperare 10 pagine di elementi key_only, quindi la gestione della navigazione all'interno di questo set è da prendere in considerazione.

ho elaborato su questo in risposta a una domanda simile, troverete il codice di esempio non:

Backward pagination with cursor is working but missing an item

Il codice di esempio sarebbe più appropriato per questa domanda. Ecco un pezzo:

def session_list(): 
    page = request.args.get('page', 0, type=int) 

    sessions_keys = Session.query().order(-Session.time_opened).fetch(100, keys_only=True) 
    sessions_keys, paging = generic_list_paging(sessions_keys, page) 
    # generic_list_paging will select the proper sublist. 
    sessions = [ sk.get() for sk in sessions_keys ] 

    return render_template('generic_list.html', objects=sessions, paging=paging) 

Vedere la domanda di riferimento per più codice.

Ovviamente, se il set di risultati è potenzialmente enorme, deve essere ancora concesso un limite al recupero, il limite rigido è di 1000 elementi, penso. Ovviamente, il risultato è più lungo di circa 10 pagine, all'utente verrà chiesto di perfezionare aggiungendo criteri.

Gestire il paging in poche centinaia di keys_only è davvero molto più semplice, che vale la pena considerare. Rende abbastanza facile fornire la navigazione diretta della pagina come menzionato nella domanda. Gli elementi dell'entità reale vengono recuperati solo per la pagina corrente attuale, il resto è solo le chiavi quindi non è così costoso. E si può prendere in considerazione la possibilità di mantenere le chiavi set_only impostate in memcache per alcuni minuti in modo che un utente che esplora rapidamente le pagine non richieda la stessa query da eseguire nuovamente.