2012-10-17 19 views
8

Używam CouchDB. Chciałbym móc zliczyć wystąpienia wartości określonych pól w zakresie dat, który można określić w czasie zapytania. Wydaje mi się, że jestem w stanie zrobić części tego, ale mam problem ze zrozumieniem najlepszego sposobu, aby to wszystko połączyć.Korzystając z widoku CouchDB, mogę liczyć grupy i filtrować według zakresu klawiszy w tym samym czasie?

Zakładając dokumentów, które mają pola datownika i innego pola, np .:

{ date: '20120101-1853', author: 'bart' } 
{ date: '20120102-1850', author: 'homer'} 
{ date: '20120103-2359', author: 'homer'} 
{ date: '20120104-1200', author: 'lisa'} 
{ date: '20120815-1250', author: 'lisa'} 

mogę łatwo utworzyć widok, który Filtry dokumenty za pomocą elastycznego zakresu dat. Można to zrobić w widoku podobnym do poniższego, wywołanego parametrami zakresu klucza, np. _view/all-docs?startkey=20120101-0000&endkey=20120201-0000.

all-Docs/map.js:

function(doc) { 
    emit(doc.date, doc); 
} 

z powyższych danych, że ten powrót widok couchdb zawierającą tylko 4 pierwsze Dokumenty (tylko Dokumenty w przedziale czasowym).

mogę również utworzyć kwerendę zlicza wystąpienia danej dziedzinie, jak ten, zwany z grupy, tj _view/author-count?group=true:

autor-count/map.js:

function(doc) { 
    emit(doc.author, 1); 
} 

autor-count/reduce.js:

function(keys, values, rereduce) { 
    return sum(values); 
} 

to przyniesie coś takiego:

{ 
    "rows": [ 
     {"key":"bart","value":1}, 
     {"key":"homer","value":2} 
     {"key":"lisa","value":2} 
    ] 
} 

Jednak nie mogę znaleźć najlepszy sposób, aby zarówno filtr według daty i liczyć wystąpienia. Na przykład, dla powyższych danych, chciałbym móc określić parametry zakresu, takie jak startkey=20120101-0000&endkey=20120201-0000 i uzyskać wynik podobny do tego, w którym ostatni dokument został wykluczony z licznika, ponieważ znajduje się poza określonym zakresem dat:

{ 
    "rows": [ 
     {"key":"bart","value":1}, 
     {"key":"homer","value":2} 
     {"key":"lisa","value":1} 
    ] 
} 

Jaki jest najbardziej elegancki sposób na zrobienie tego? Czy można to osiągnąć za pomocą pojedynczego zapytania? Czy powinienem używać innej konstrukcji CouchDB, czy też wystarczy do tego widok?

+2

Moja pierwsza myśl to zapytanie o zakres dat, a następnie użycie funkcji [_list] (http://wiki.apache.org/couchdb/Formatting_with_Show_and_List#Listing_Views_with_CouchDB_0.10_and_later) w celu zrobienia grupy/liczenia. Nie można operować na wielu kluczach w ramach tego samego zapytania, więc potrzebna jest druga warstwa. –

Odpowiedz

0

Musisz utworzyć połączoną widok:

połączone/map.js:

function(doc) { 
    emit([doc.date, doc.author], 1); 
} 

połączone/reduce.js:

_sum 

ten sposób można będzie móc filtrować dokumenty według daty rozpoczęcia/zakończenia.

startkey=[20120101-0000, "a"]&endkey=[20120201-0000, "a"] 
+1

To nie pozwoli mi grupować i liczyć według autora, więc otrzymam coś takiego: http://pastebin.com/raw.php?i=pZCPvic6 (każdy wpis jest wymieniony z liczbą 1). O ile widzę, najlepszym rozwiązaniem jest użycie funkcji _list, jak sugeruje Dominic. – rewbs

0

Chociaż Twój problem jest trudny do rozwiązania w ogólnym przypadku, znajomość niektórych ograniczeń dotyczących ewentualnych zapytań może bardzo pomóc. Na przykład.jeśli wiesz, że będzie szukać na zakresach, które będą obejmować pełne dni/miesięcy można użytkownik tablice z [year, month, day, time] zamiast napisu:

emit([doc.date_year, doc.date_month, doc.date_day, doc.date_time, doc.author] doc); 

Nawet jeśli nie można przewidzieć, że wszystkie ewentualne pytania będą pasować do grupowania na podstawie ten kluczowy klucz, dzieląc klucz, może pomóc zoptymalizować zapytania dotyczące zakresu i zmniejszyć liczbę potrzebnych wyszukiwań (kosztem dodatkowej przestrzeni).

1

można dostać całkiem blisko do pożądanego rezultatu z listy:

{ 
    _id: "_design/authors", 
    views: { 
    authors_by_date: { 
     map: function(doc) { 
     emit(doc.date, doc.author); 
     } 
    } 
    }, 
    lists: { 
    count_occurrences: function(head, req) { 
     start({ headers: { "Content-Type": "application/json" }}); 

     var result = {}; 
     var row; 
     while(row = getRow()) { 
     var val = row.value; 
     if(result[val]) result[val]++; 
     else result[val] = 1; 
     } 
     return result; 
    } 
    } 
} 

Ten projekt może być wymagane jako takie:

http://<couchurl>/<db>/_design/authors/_list/count_occurrences/authors_by_date?startkey=<startDate>&endkey=<endDate> 

To będzie wolniejsze niż normalny map-reduce, i jest trochę obejścia. Niestety, jest to jedyny sposób na wykonanie wielowymiarowego zapytania, "which CouchDB isn’t suited for".

Wynikiem żądania ten projekt będzie coś takiego:

{ 
    "bart": 1, 
    "homer": 2, 
    "lisa": 2 
} 

Co robimy jest zasadniczo emitują dużo elementów, a następnie na podstawie listy do grupy je jak chcemy. Lista może być używana do wyświetlania wyniku w dowolny sposób, ale często będzie wolniejsza. Podczas gdy normalne zmniejszanie map może być buforowane i zmieniać się tylko zgodnie z różnicami, lista będzie musiała być budowana od nowa za każdym razem, gdy zostanie zażądana.

Jest prawie tak powolny, jak uzyskanie wszystkich elementów wynikających z mapy (obciążenie związane z porządkowaniem danych jest znikome): o wiele wolniejsze niż uzyskanie wyniku redukcji.

Jeśli chcesz użyć listy do innego widoku, można po prostu wymienić je w adresie URL żądania:

http://<couchurl>/<db>/_design/authors/_list/count_occurrences/<view> 

Przeczytaj więcej o lists on the couchdb wiki.

+0

Oczywiście możesz edytować listę, aby uzyskać żądany format, jeśli ważne jest, abyś otrzymał pole 'rows' i aby każda pozycja zawierała' klucz' i 'wartość' :) –

Powiązane problemy