You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@couchdb.apache.org by Wout Mertens <wo...@gmail.com> on 2009/04/22 14:40:10 UTC
Proposal: Review DBs
Intro
=====
How do you sort by reduce value? How do you join views? How do you get
unique view results? How do you cache group key reduces?
I think that with the below proposed solution all the above and more
are possible. The general idea is to store view results and run map/
reduce on them. There's been some discussions about this but they went
nowhere. I've been thinking about this issue a bit and I think it can
be done.
I'd like to call this feature a Review DB.
Use cases
=========
- Suppose you want to know what tags are most popular on your blog.
Simply get:
http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
Where tags_by_count is a Review DB that gets input from the tagcount
view and then runs the sort_by_value view on it, a map() function that
simply emits (value,key).
Likewise, show pages in order of popularity, whereby user can vote up
(+1) or down (-1):
http://couchdb/db/_design/mywiki/_review/pagevotes/_view/
sort_by_value
- Given documents with attributes title, date and tags. You'd like to
know the minimum value of date and a breakdown by count for tags, for
every title. Normally you'd use 2 map+reduce views,
minimum_date_by_title and tagcount_by_title, which you would then
query separately. With a Review DB, you can let both views insert
their results in the database and then run a view that combines the
results in one view:
http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
- This is not a way to run an on-the-fly map/reduce on a subset of a
view, like if you want to find the median popularity score of
restaurants with "Tony" in their name that are close to you.
Implementation
==============
A Review DB is a hidden database maintained by CouchDB with these
fields:
- _id of document is the string representation of the key
- "key" is the key of the incoming view row (unique)
- "value" is the value of the incoming view row
I hope that this is sufficiently like a normal view that it can be
stored as a normal view. _id is just there to make it doc-compliant,
it would be much better if "key" were the actual key.
A Review DB is defined in a design document like normal views. Each
review is an entry in the "reviews" hash, and has a "incoming_views"
array that lists all the views that should insert results in the
review db plus the group level, as well as a normal "views" hash for
further map/reduce of the review db (and perhaps another "reviews"
hash for further result processing?).
Maintaining a database of results means that results have to be
updated or even removed when documents change. I tried to make this
work (in theory) for map-only views, but the resulting requirements
are quite messy. You either need to cache the previous results of a
view for each document, or you have to have an old version of the
document available to regenerate those results.
Therefore, a Review DB only accepts results from one or more map
+reduce views. You define beforehand what the group_level of the keys
is that will be inserted.
Furthermore, a Review DB disallows (but doesn't enforce) having 2
views that generate the same keys. Otherwise, refcounting would need
to be used and while that's not difficult, I think there's limited
value in allowing this.
The Review DB needs updating every time the reduction for a group key
of one of the participating views gets updated. Even though a map
+reduce view has unique keys, we need a refcount since we have
multiple views. Whoever got to insert its value last wins.
There is a slight complication: group key values are calculated on-the-
fly from the view result b-tree. So whenever a reduce call results in
a new value for a b-tree node, AND that node is the upper node of a
subtree that is completely part of a group key, that group key needs
to be marked for recalculation.
Likewise, if deletion/addition of a b-tree node results in the removal/
creation of the sole upper node of a group key subtree, that group key
needs to be marked for removal/addition.
This is the algorithm:
- When a reducing view gets updated, and it is part of a Review DB,
use the 2 paragraphs above to keep a list of group keys that need
handling
- After updating the reduce() results, for each of the marked group
keys:
- If a group key gets removed:
- look up doc with key=group key in review db. If exists:
- delete doc
- If a group key gets added:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
- If a group key gets updated:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
As you can see, this is something CouchDB should do since it knows
when it's updating group key reduction values and it knows if this was
an delete, update or addition.
View updates are done when the view is called; Review updates are done
at this time as well. Views on Review DBs are done when they are called.
Summary
=======
Review DBs are a sort of view index that CouchDB can maintain with
little overhead. It caches group key results and allows chained map
+reduce calculations using mostly existing frameworks.
I think this would be a very useful feature for CouchDB to have. There
are regularly requests for storing view results in a database for post-
processing on the mailing lists.
I'm not saying this is a trivial change but it doesn't seem
technically impossible to me either. (unless I missed something again;
this is the 5th iteration of this proposal. Anyway I know *I* wouldn't
be able to code this :-) )
What do you think, oh dear devs?
Wout.
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
This also sounds related to the "View Intersections" proposal
discussed earlier...
http://mail-archives.apache.org/mod_mbox/couchdb-dev/200904.mbox/%3c011A0D62-06C2-490B-A4C4-7EEF0203B6C3@gmail.com%3e
On Wed, Apr 22, 2009 at 9:07 AM, Zachary Zolton
<za...@gmail.com> wrote:
> I would definitely +1 something like this.
>
> I'm essentially do something like this "manually", with an update
> notifier script, and I'd love to see it become a first-class feature
> —instead of my special-case version of it.
>
> On Wed, Apr 22, 2009 at 7:40 AM, Wout Mertens <wo...@gmail.com> wrote:
>> Intro
>> =====
>> How do you sort by reduce value? How do you join views? How do you get
>> unique view results? How do you cache group key reduces?
>>
>> I think that with the below proposed solution all the above and more are
>> possible. The general idea is to store view results and run map/reduce on
>> them. There's been some discussions about this but they went nowhere. I've
>> been thinking about this issue a bit and I think it can be done.
>>
>> I'd like to call this feature a Review DB.
>>
>> Use cases
>> =========
>> - Suppose you want to know what tags are most popular on your blog. Simply
>> get:
>>
>> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>>
>> Where tags_by_count is a Review DB that gets input from the tagcount view
>> and then runs the sort_by_value view on it, a map() function that simply
>> emits (value,key).
>>
>> Likewise, show pages in order of popularity, whereby user can vote up (+1)
>> or down (-1):
>>
>> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>>
>> - Given documents with attributes title, date and tags. You'd like to know
>> the minimum value of date and a breakdown by count for tags, for every
>> title. Normally you'd use 2 map+reduce views, minimum_date_by_title and
>> tagcount_by_title, which you would then query separately. With a Review DB,
>> you can let both views insert their results in the database and then run a
>> view that combines the results in one view:
>>
>> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>>
>> - This is not a way to run an on-the-fly map/reduce on a subset of a view,
>> like if you want to find the median popularity score of restaurants with
>> "Tony" in their name that are close to you.
>>
>> Implementation
>> ==============
>> A Review DB is a hidden database maintained by CouchDB with these fields:
>> - _id of document is the string representation of the key
>> - "key" is the key of the incoming view row (unique)
>> - "value" is the value of the incoming view row
>>
>> I hope that this is sufficiently like a normal view that it can be stored as
>> a normal view. _id is just there to make it doc-compliant, it would be much
>> better if "key" were the actual key.
>>
>> A Review DB is defined in a design document like normal views. Each review
>> is an entry in the "reviews" hash, and has a "incoming_views" array that
>> lists all the views that should insert results in the review db plus the
>> group level, as well as a normal "views" hash for further map/reduce of the
>> review db (and perhaps another "reviews" hash for further result
>> processing?).
>>
>> Maintaining a database of results means that results have to be updated or
>> even removed when documents change. I tried to make this work (in theory)
>> for map-only views, but the resulting requirements are quite messy. You
>> either need to cache the previous results of a view for each document, or
>> you have to have an old version of the document available to regenerate
>> those results.
>>
>> Therefore, a Review DB only accepts results from one or more map+reduce
>> views. You define beforehand what the group_level of the keys is that will
>> be inserted.
>>
>> Furthermore, a Review DB disallows (but doesn't enforce) having 2 views that
>> generate the same keys. Otherwise, refcounting would need to be used and
>> while that's not difficult, I think there's limited value in allowing this.
>>
>> The Review DB needs updating every time the reduction for a group key of one
>> of the participating views gets updated. Even though a map+reduce view has
>> unique keys, we need a refcount since we have multiple views. Whoever got to
>> insert its value last wins.
>>
>> There is a slight complication: group key values are calculated on-the-fly
>> from the view result b-tree. So whenever a reduce call results in a new
>> value for a b-tree node, AND that node is the upper node of a subtree that
>> is completely part of a group key, that group key needs to be marked for
>> recalculation.
>>
>> Likewise, if deletion/addition of a b-tree node results in the
>> removal/creation of the sole upper node of a group key subtree, that group
>> key needs to be marked for removal/addition.
>>
>> This is the algorithm:
>> - When a reducing view gets updated, and it is part of a Review DB, use the
>> 2 paragraphs above to keep a list of group keys that need handling
>> - After updating the reduce() results, for each of the marked group keys:
>> - If a group key gets removed:
>> - look up doc with key=group key in review db. If exists:
>> - delete doc
>> - If a group key gets added:
>> - look up doc with key=group key in review db. If exists:
>> - set doc.value to the row value
>> - else
>> - create doc with id=group key in string form, key=group key,
>> value=value
>> - If a group key gets updated:
>> - look up doc with key=group key in review db. If exists:
>> - set doc.value to the row value
>> - else
>> - create doc with id=group key in string form, key=group key,
>> value=value
>>
>> As you can see, this is something CouchDB should do since it knows when it's
>> updating group key reduction values and it knows if this was an delete,
>> update or addition.
>>
>> View updates are done when the view is called; Review updates are done at
>> this time as well. Views on Review DBs are done when they are called.
>>
>> Summary
>> =======
>> Review DBs are a sort of view index that CouchDB can maintain with little
>> overhead. It caches group key results and allows chained map+reduce
>> calculations using mostly existing frameworks.
>>
>> I think this would be a very useful feature for CouchDB to have. There are
>> regularly requests for storing view results in a database for
>> post-processing on the mailing lists.
>>
>> I'm not saying this is a trivial change but it doesn't seem technically
>> impossible to me either. (unless I missed something again; this is the 5th
>> iteration of this proposal. Anyway I know *I* wouldn't be able to code this
>> :-) )
>>
>> What do you think, oh dear devs?
>>
>> Wout.
>>
>
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
I would definitely +1 something like this.
I'm essentially do something like this "manually", with an update
notifier script, and I'd love to see it become a first-class feature
—instead of my special-case version of it.
On Wed, Apr 22, 2009 at 7:40 AM, Wout Mertens <wo...@gmail.com> wrote:
> Intro
> =====
> How do you sort by reduce value? How do you join views? How do you get
> unique view results? How do you cache group key reduces?
>
> I think that with the below proposed solution all the above and more are
> possible. The general idea is to store view results and run map/reduce on
> them. There's been some discussions about this but they went nowhere. I've
> been thinking about this issue a bit and I think it can be done.
>
> I'd like to call this feature a Review DB.
>
> Use cases
> =========
> - Suppose you want to know what tags are most popular on your blog. Simply
> get:
>
> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>
> Where tags_by_count is a Review DB that gets input from the tagcount view
> and then runs the sort_by_value view on it, a map() function that simply
> emits (value,key).
>
> Likewise, show pages in order of popularity, whereby user can vote up (+1)
> or down (-1):
>
> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>
> - Given documents with attributes title, date and tags. You'd like to know
> the minimum value of date and a breakdown by count for tags, for every
> title. Normally you'd use 2 map+reduce views, minimum_date_by_title and
> tagcount_by_title, which you would then query separately. With a Review DB,
> you can let both views insert their results in the database and then run a
> view that combines the results in one view:
>
> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>
> - This is not a way to run an on-the-fly map/reduce on a subset of a view,
> like if you want to find the median popularity score of restaurants with
> "Tony" in their name that are close to you.
>
> Implementation
> ==============
> A Review DB is a hidden database maintained by CouchDB with these fields:
> - _id of document is the string representation of the key
> - "key" is the key of the incoming view row (unique)
> - "value" is the value of the incoming view row
>
> I hope that this is sufficiently like a normal view that it can be stored as
> a normal view. _id is just there to make it doc-compliant, it would be much
> better if "key" were the actual key.
>
> A Review DB is defined in a design document like normal views. Each review
> is an entry in the "reviews" hash, and has a "incoming_views" array that
> lists all the views that should insert results in the review db plus the
> group level, as well as a normal "views" hash for further map/reduce of the
> review db (and perhaps another "reviews" hash for further result
> processing?).
>
> Maintaining a database of results means that results have to be updated or
> even removed when documents change. I tried to make this work (in theory)
> for map-only views, but the resulting requirements are quite messy. You
> either need to cache the previous results of a view for each document, or
> you have to have an old version of the document available to regenerate
> those results.
>
> Therefore, a Review DB only accepts results from one or more map+reduce
> views. You define beforehand what the group_level of the keys is that will
> be inserted.
>
> Furthermore, a Review DB disallows (but doesn't enforce) having 2 views that
> generate the same keys. Otherwise, refcounting would need to be used and
> while that's not difficult, I think there's limited value in allowing this.
>
> The Review DB needs updating every time the reduction for a group key of one
> of the participating views gets updated. Even though a map+reduce view has
> unique keys, we need a refcount since we have multiple views. Whoever got to
> insert its value last wins.
>
> There is a slight complication: group key values are calculated on-the-fly
> from the view result b-tree. So whenever a reduce call results in a new
> value for a b-tree node, AND that node is the upper node of a subtree that
> is completely part of a group key, that group key needs to be marked for
> recalculation.
>
> Likewise, if deletion/addition of a b-tree node results in the
> removal/creation of the sole upper node of a group key subtree, that group
> key needs to be marked for removal/addition.
>
> This is the algorithm:
> - When a reducing view gets updated, and it is part of a Review DB, use the
> 2 paragraphs above to keep a list of group keys that need handling
> - After updating the reduce() results, for each of the marked group keys:
> - If a group key gets removed:
> - look up doc with key=group key in review db. If exists:
> - delete doc
> - If a group key gets added:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
> - If a group key gets updated:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
>
> As you can see, this is something CouchDB should do since it knows when it's
> updating group key reduction values and it knows if this was an delete,
> update or addition.
>
> View updates are done when the view is called; Review updates are done at
> this time as well. Views on Review DBs are done when they are called.
>
> Summary
> =======
> Review DBs are a sort of view index that CouchDB can maintain with little
> overhead. It caches group key results and allows chained map+reduce
> calculations using mostly existing frameworks.
>
> I think this would be a very useful feature for CouchDB to have. There are
> regularly requests for storing view results in a database for
> post-processing on the mailing lists.
>
> I'm not saying this is a trivial change but it doesn't seem technically
> impossible to me either. (unless I missed something again; this is the 5th
> iteration of this proposal. Anyway I know *I* wouldn't be able to code this
> :-) )
>
> What do you think, oh dear devs?
>
> Wout.
>
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
On Apr 28, 2009, at 3:46 PM, Adam Kocoloski wrote:
> I'm not an expert on the btree code, but I think this statement
>
>> So whenever a reduce call results in a new value for a b-tree node,
>> AND that node is the upper node of a subtree that is completely
>> part of a group key, that group key needs to be marked for
>> recalculation.
>
>
> misses some updates. I thought things looked something like
>
> root
> |
> ---------------
> kp1 kp2
> | |
> --------- ---------
> foo foo bar bar bar bar
>
> where foo and bar are emitted keys from a map (values not show for
> the sake of brevity). kp1 and kp2 hold reduce(foo,foo,bar) and
> reduce(bar,bar,bar) respectively, and root holds rereduce(kp1,kp2).
That is my understanding as well.
> When we request a view with group=true, Couch is smart enough to use
> the reduction stored in kp2 directly, but it has to call
> reduce(foo,foo) and reduce(bar) on the fly for the nodes underneath
> kp1 (and then rereduce the bar reductions from kp1 and kp2 to get
> the result for bar). If I interpreted Wout's statement correctly,
> it ignores the case where any of the nodes under kp1 change, since
> kp1 is not "the upper node of a subtree that is completely part of a
> group key".
In this case, both foos are the upper node of a subtree that is
completely part of a group key. bar isn't.
Same graph with the group keys indicated:
root
|
------BAR------
kp1 kp2
| |
--FOO---- ---------
foo foo bar bar bar bar
So whenever the first bar changes, kp1 needs to be calculated and then
BAR is marked as needing updating, since kp1 is a top node under BAR.
FOO is only marked for updating when either of the foos change.
> I think the problem of tracking which group keys to update can be
> made simpler. We really only need to see the key updates coming out
> of the incremental map. Couch knows the lists of keys to add and
> keys to remove at that point; the set of all unique keys in those
> two lists (the "changeset") is the set of group keys that would need
> to be updated in the Review DB. Here I'm using "update" in the
> general sense of add/remove/change; the way I see it, we could just
> query the view for all the keys in the changeset. If the MR view
> has no results for a given key, that obviously means delete the
> associated document from the Review DB.
That is a very good idea! It's not as efficient as marking group keys
like I proposed (maps need not necessarily change reduce values), but
it's a lot easier to code.
> group_levels are a straightforward extension -- we just check what
> group_level we'll be using in the Review DB when we calculate the
> changeset.
Exactly.
So the algorithm becomes:
- When updating a view, keep track of all mentioned keys in the
previous and current map() output, keep only group_level key parts.
- After updating the reduce() results, for each of the marked group
keys:
- If a group key gets removed:
- look up doc with key=group key in review db. If exists:
- delete doc
- If a group key gets added:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
- If a group key gets updated:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
Good thinking!
Wout.
Re: Proposal: Review DBs
Posted by Adam Kocoloski <ko...@apache.org>.
On Apr 28, 2009, at 8:13 AM, Wout Mertens wrote:
> On Apr 28, 2009, at 11:42 AM, Brian Candler wrote:
>
>> Furthermore, if I understand rightly, these grouped reduce values
>> are *not*
>> persisted anywhere (*). That is: every time you do a
>> reduce=true&group=true
>> query then the entire map index is scanned for distinct keys and
>> then each
>> set of values with equal keys is passed afresh to the reduce
>> function.
>>
>> This means that it won't be possible to do incremental changes to
>> the review
>> DB, since these grouped keys and reduce values aren't associated
>> with the
>> docids they came from. You'd have to calculate it all from scratch
>> every
>> time, in which case you might as well just get the client to do it.
>> AFAICS,
>> CouchDB could cache the result only when the database is not
>> receiving any
>> updates.
>
> You're correct in that group reduce values are not persisted,
> however the algorithm I proposed can do incremental updates
> nonetheless:
> =========
> Group key values are calculated on-the-fly from the view result b-
> tree. So whenever a reduce call results in a new value for a b-tree
> node, AND that node is the upper node of a subtree that is
> completely part of a group key, that group key needs to be marked
> for recalculation.
>
> Likewise, if deletion/addition of a b-tree node results in the
> removal/creation of the sole upper node of a group key subtree, that
> group key needs to be marked for removal/addition.
>
> This is the algorithm:
> - When a reducing view gets updated, and it is part of a Review DB,
> use the 2 paragraphs above to keep a list of group keys that need
> handling
> - After updating the reduce() results, for each of the marked group
> keys:
> - If a group key gets removed:
> - look up doc with key=group key in review db. If exists:
> - delete doc
> - If a group key gets added:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
> - If a group key gets updated:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
> ========
> Is that explanation clear?
>
> I'm imagining that at any point on the b-tree where a reduce value
> is received, CouchDB can see if that node is one of the top nodes
> for a group key and can mark that group key somehow. I think that
> should be feasible (although I admit I've never tried to grok the
> code).
>
> Cheers,
>
> Wout.
I'm not an expert on the btree code, but I think this statement
> So whenever a reduce call results in a new value for a b-tree node,
> AND that node is the upper node of a subtree that is completely part
> of a group key, that group key needs to be marked for recalculation.
misses some updates. I thought things looked something like
root
|
---------------
kp1 kp2
| |
--------- ---------
foo foo bar bar bar bar
where foo and bar are emitted keys from a map (values not show for the
sake of brevity). kp1 and kp2 hold reduce(foo,foo,bar) and
reduce(bar,bar,bar) respectively, and root holds rereduce(kp1,kp2).
When we request a view with group=true, Couch is smart enough to use
the reduction stored in kp2 directly, but it has to call
reduce(foo,foo) and reduce(bar) on the fly for the nodes underneath
kp1 (and then rereduce the bar reductions from kp1 and kp2 to get the
result for bar). If I interpreted Wout's statement correctly, it
ignores the case where any of the nodes under kp1 change, since kp1 is
not "the upper node of a subtree that is completely part of a group
key".
I think the problem of tracking which group keys to update can be made
simpler. We really only need to see the key updates coming out of the
incremental map. Couch knows the lists of keys to add and keys to
remove at that point; the set of all unique keys in those two lists
(the "changeset") is the set of group keys that would need to be
updated in the Review DB. Here I'm using "update" in the general
sense of add/remove/change; the way I see it, we could just query the
view for all the keys in the changeset. If the MR view has no results
for a given key, that obviously means delete the associated document
from the Review DB.
group_levels are a straightforward extension -- we just check what
group_level we'll be using in the Review DB when we calculate the
changeset.
Regards, Adam
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
On Apr 28, 2009, at 11:42 AM, Brian Candler wrote:
> On Sun, Apr 26, 2009 at 11:26:33PM +0200, Wout Mertens wrote:
>> In a nutshell, I'm hoping that:
>> * A review is a new sort of view that has an "inputs" array in its
>> definition.
>> * Only MR views are allowed as inputs, no KV duplication allowed.
>> * It builds a persistent index of the incoming views when those get
>> updated.
>> * That index is then used to build the view index for the review when
>> the review gets updated.
>> * I think I covered the most important algorithms needed to implement
>> this in my original proposal.
>>
>> Does this sound feasible? If so I'll update my proposal accordingly.
>
> Could you define a bit more where the "inputs" array comes from?
Oh right, I didn't explain that well, and I never said how to add the
group_level. Here's an example for counting the number of unique names
in a database:
{
"_id": "_design/foo",
"views": {
"unique_names": {
"map": "function(doc) {emit(doc.name, 1);}",
"reduce": "function(keys, values, rereduce) {return
sum(values);}"
}
"unique_names_count": {
"inputs": {"unique_names":1},
"map": "function(doc) {emit(doc.key, 1);}",
"reduce": "function(keys, values, rereduce) {return
sum(values);}"
}
}
}
In this case, unique_names_count is the review db. It gets input from
the unique_names view with group_level set to 1. Note that it's a hash
since each view only gets added once and each view has a unique name.
(BTW, I can't figure out what to set group_level to if I want the
equivalent of group=true. group_level=exact doesn't work although
group_level=1000 might be good enough. How about allowing
group_level=-1?)
(Also, in this case just "unique_names_count": {"inputs":
{"unique_names":1}} would suffice since getting that view with limit=0
will give you the total_rows number which is what was needed)
> Furthermore, if I understand rightly, these grouped reduce values
> are *not*
> persisted anywhere (*). That is: every time you do a
> reduce=true&group=true
> query then the entire map index is scanned for distinct keys and
> then each
> set of values with equal keys is passed afresh to the reduce function.
>
> This means that it won't be possible to do incremental changes to
> the review
> DB, since these grouped keys and reduce values aren't associated
> with the
> docids they came from. You'd have to calculate it all from scratch
> every
> time, in which case you might as well just get the client to do it.
> AFAICS,
> CouchDB could cache the result only when the database is not
> receiving any
> updates.
You're correct in that group reduce values are not persisted, however
the algorithm I proposed can do incremental updates nonetheless:
=========
Group key values are calculated on-the-fly from the view result b-
tree. So whenever a reduce call results in a new value for a b-tree
node, AND that node is the upper node of a subtree that is completely
part of a group key, that group key needs to be marked for
recalculation.
Likewise, if deletion/addition of a b-tree node results in the removal/
creation of the sole upper node of a group key subtree, that group key
needs to be marked for removal/addition.
This is the algorithm:
- When a reducing view gets updated, and it is part of a Review DB,
use the 2 paragraphs above to keep a list of group keys that need
handling
- After updating the reduce() results, for each of the marked group
keys:
- If a group key gets removed:
- look up doc with key=group key in review db. If exists:
- delete doc
- If a group key gets added:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
- If a group key gets updated:
- look up doc with key=group key in review db. If exists:
- set doc.value to the row value
- else
- create doc with id=group key in string form, key=group key,
value=value
========
Is that explanation clear?
I'm imagining that at any point on the b-tree where a reduce value is
received, CouchDB can see if that node is one of the top nodes for a
group key and can mark that group key somehow. I think that should be
feasible (although I admit I've never tried to grok the code).
Cheers,
Wout.
Re: Proposal: Review DBs
Posted by Brian Candler <B....@pobox.com>.
On Sun, Apr 26, 2009 at 11:26:33PM +0200, Wout Mertens wrote:
> In a nutshell, I'm hoping that:
> * A review is a new sort of view that has an "inputs" array in its
> definition.
> * Only MR views are allowed as inputs, no KV duplication allowed.
> * It builds a persistent index of the incoming views when those get
> updated.
> * That index is then used to build the view index for the review when
> the review gets updated.
> * I think I covered the most important algorithms needed to implement
> this in my original proposal.
>
> Does this sound feasible? If so I'll update my proposal accordingly.
Could you define a bit more where the "inputs" array comes from?
It can't be the overall reduce value for the whole database - that would
just be one single value :-) So it has to be the reduce value in some
grouped form, but there are multiple ways to slice that: e.g. group=true,
group_level=1, group_level=2 etc. This is something which will have to be
chosen by the user.
Furthermore, if I understand rightly, these grouped reduce values are *not*
persisted anywhere (*). That is: every time you do a reduce=true&group=true
query then the entire map index is scanned for distinct keys and then each
set of values with equal keys is passed afresh to the reduce function.
This means that it won't be possible to do incremental changes to the review
DB, since these grouped keys and reduce values aren't associated with the
docids they came from. You'd have to calculate it all from scratch every
time, in which case you might as well just get the client to do it. AFAICS,
CouchDB could cache the result only when the database is not receiving any
updates.
OTOH, it's quite possible that I have misunderstood entirely :-)
Regards,
Brian.
(*) Each B-tree node containing N key/value pairs also contains a single
reduce value for those N documents. However the B-tree nodes are not aligned
in any way with the map keys. These precalculated values do allow quicker
calculation of reduce values where a large number of documents emit the same
key, spanning multiple B-tree nodes, since the already-reduced values can be
re-reduced together.
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
On Apr 27, 2009, at 5:20 AM, Chris Anderson wrote:
> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com>
> wrote:
>>
>>> You'd still be left with the problem of generating unique docids
>>> for the documents in the Review DB, but I think that's a problem
>>> that needs to be solved. The restriction to only MR views with no
>>> duplicate keys across views seems too strong to me.
>>
>> Well, since the Review DB is a local(*) hidden database that's
>> handled a bit specially, I think the easiest is to assign _id a
>> sequence number and create a default view that indexes the
>> documents by doc.key (for updating the value for that key). There
>> will never be contention and we're only interested in the key index.
>
> We discussed this a little at CouchHack and I argued that the
> simplest solution is actually good for a few reasons.
>
> The simple solution: provide a mechanism to copy the rows of a
> grouped reduce function to a new database.
Ok, the problems I see with that though are:
- How to assign _ids to the rows
- Separate design doc needed for each DB
- Spreads application logic
- All data not available from one parent URL a la CouchApps
- Namespace pollution, all these utility DBs
Or do you mean this as a single-shot data dump? Couldn't that get
quite expensive, storage wise?
> Good because it is most like Hadoop/Google style map reduce. In that
> paradigm, the output of a map/reduce job is not incremental, and it
> is persisted in a way that allows for multiple later reduce stages
> to be run on it. It's common in Hadoop to chain many m/r stages, and
> to try a few iterations of each stage while developing code.
Hmmm. If a hidden DB/view index is used, then the same function
hashing techniques will work to decide which index to use for
intermediate queries. I see no functional difference here.
> I like this also because it provides the needed functionality
> without adding any new primitives to CouchDB.
But how would that mechanism be used if there's no new primitives? If
CouchDB would allow an extra field "inputs" on the view definitions,
that's it as far as user-visible changes go in the current thinking
for review DBs.
> The only downside of this approach is that it is not incremental.
> I'm not sure that incremental chainability has much promise, as the
> index management could be a pain, especially if you have branching
> chains.
Hmmm, I think that I showed that it needn't be. Any update to a view
would trigger review index updates for all the views that have that
view as input. Subsequent updates of those views then get propagated
onwards in the same fashion. Nothing painful...
If you want the latest info, first update the input views and then the
review view.
> Another upside is that by reducing to a db, you give the user power
> to do things like use replication to merge multiple data sets before
> applying more views.
That's true... And I suppose it would be very useful in that case. I
think there's room for both approaches perhaps?
Wout.
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
Perhaps the background job could maintain some versioning number, in
the base DB's design doc, which clients could use to know which
version of the derived database to hit.
See? ;^) This might be simpler, as a 1st-class CouchDB feature, than a
bolted-on script.
On Mon, Apr 27, 2009 at 2:48 PM, Paul Davis <pa...@gmail.com> wrote:
> Zachary,
>
> Hmm. Naming your derived databases as base_db-stage-etag doesn't sound like
> a bad idea. Though I dunno how you'd communicate to clients to start hitting
> the new versions and also doesn't tell the admins when to drop old indices.
>
> The only thing that comes to mind is to stick some intermediary in between
> clients and the actual derived data to make it transparent and also to allow
> you to know when you can clean up old versions etc.
>
> I'll keep thinking on it.
>
> Paul
>
> Zachary Zolton wrote:
>>
>> okay, i'm starting to get ya. my question is, if i'm constantly
>> dropping/recreating/reindexing the derived database, how can i keep
>> serving requests from my website?
>>
>> one possible solution would be to time/etag/etc-stamp the derived db
>> name, but that would seem to add a number of moving parts to my
>> system.
>>
>> hmm... any ideas of how to pull a quick switcheroo on the backend of
>> my system, without too much hassle in the client code?
>>
>> On Mon, Apr 27, 2009 at 1:44 PM, Paul Davis <pa...@gmail.com>
>> wrote:
>>
>>>
>>> Zachary,
>>>
>>> No worries, the rough outline I'd do here is something like:
>>>
>>> 1. Figure out some member structure in the _design document that will
>>> represent your data flow. For them moment I would do something extremely
>>> simple as in:
>>>
>>> Assume:
>>> db_name = "base_db"
>>>
>>> {
>>> "_id": " _design/base",
>>> "views": {
>>> "stage-1": {
>>> "map": "function(doc) ...",
>>> "reduce": "function(keys, values, rereduce) ..."
>>> }
>>> },
>>> "review": [
>>> {"map": "function(doc) ...", "reduce": "function(keys, vals,
>>> rereduce)
>>> ..."},
>>> {"map": "function(doc) ...", "reduce": "function(keys, vals,
>>> rereduce)
>>> ..."}
>>> ]
>>> }
>>>
>>> So the review member becomes the stages in your data flow. I'm avoiding
>>> any
>>> forking or merging in this example in honor of the "make it work, make it
>>> not suck" development flow.
>>>
>>> Now the basic algorithm would be something like:
>>>
>>> For each array element in the "review" member, create a db something
>>> like:
>>>
>>> base_db-stage-1 with a design document that contains a view with the
>>> first
>>> element of the "reviews" member.
>>> base_db-stage-2 with the second member and so on.
>>>
>>> Then your script can check the view status in each database either with a
>>> cron (or an update_notifier) to do so, you can just:
>>>
>>> HEAD /base_db/_design/base/_view/stage-1
>>>
>>> And then check the returned ETag. For the moment this is exactly
>>> equivalent
>>> to checking the database's update_seq because of how the etag is
>>> calculated,
>>> but in the future when we track the last update_seq for each view change
>>> this will be a free upgrade. Plus there's a bit more logical-ness to
>>> checking "view state" instead of "db state".
>>>
>>> When the etag's don't match, you can just drop the next db in the flow,
>>> create it, and then copy the view output. The drop/create just makes the
>>> algorithm easily implementable for now. In the future there can be some
>>> extra logic to only change the new view as far as it requires by
>>> iterating
>>> over the two views and doing a merge sortish type of thing. I think...
>>> Sounds like there should be a way at least.
>>>
>>> Once that works we can look at bolting on different fancy things like
>>> having
>>> forking map/reduce mechanisms and my current pet idea of adding in the
>>> merge
>>> stuff that has been talked about.
>>>
>>> This is actually starting to sound like a fun little project....
>>>
>>> HTH,
>>> Paul Davis
>>>
>>> Zachary Zolton wrote:
>>>
>>>>
>>>> paul
>>>>
>>>> alright... you've gotta give me the remedial explanation of what you
>>>> meant here! (sorry, i'm still noob-ish)
>>>>
>>>> so, are you saying that i shouldn't even check for individual doc
>>>> updates, but instead just recreate the entire database? that sounds
>>>> like a job for cron, more so than the update notifier, right?
>>>>
>>>> i'd put up my current ruby script, but it deals update notifications
>>>> in a way that's very specific to my data —probably very naïvely, to
>>>> boot!
>>>>
>>>> zach
>>>>
>>>> On Mon, Apr 27, 2009 at 11:28 AM, Paul Davis
>>>> <pa...@gmail.com> wrote:
>>>>
>>>>
>>>>>
>>>>> Zachary,
>>>>>
>>>>> Awesome. The thing with non-incremental updates is that the basic
>>>>> algorithm
>>>>> would be to just look for updates to the view and on update, delete the
>>>>> review DB, create a new one, and then dump the new data into it. I
>>>>> wouldn't
>>>>> try too hard for the optimizing updates at this point in time.
>>>>>
>>>>> Getting a ruby script out to show the basics should probably be the
>>>>> first
>>>>> step. Beyond that we'll have to take it a step at a time.
>>>>>
>>>>> HTH,
>>>>> Paul Davis
>>>>>
>>>>>
>>>>> Zachary Zolton wrote:
>>>>>
>>>>>
>>>>>>
>>>>>> @jchris et al,
>>>>>>
>>>>>> if you had any pointer, on how to implement this, i have a strong
>>>>>> motivation to try my hand at it.
>>>>>>
>>>>>> i have a janky ruby script running as an update notifier that looks
>>>>>> for certain criteria, idiomatic to my data, that puts docs into a
>>>>>> derived database. but i'm not terribly happy with my current
>>>>>> implementation...
>>>>>>
>>>>>> is there a general-purpose algorithm for dealing with updates?
>>>>>>
>>>>>>
>>>>>> cheers,
>>>>>>
>>>>>> zach
>>>>>>
>>>>>>
>>>>>> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Sent from my iPhone
>>>>>>>
>>>>>>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Hi Adam,
>>>>>>>>
>>>>>>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Hi Wout, thanks for writing this up.
>>>>>>>>>
>>>>>>>>> One comment about the map-only views: I think you'll find that
>>>>>>>>> Couch
>>>>>>>>> has
>>>>>>>>> already done a good bit of the work needed to support them, too.
>>>>>>>>> Couch
>>>>>>>>> maintains a btree for each design doc keyed on docid that stores
>>>>>>>>> all
>>>>>>>>> the
>>>>>>>>> view keys emitted by the maps over each document. When a document
>>>>>>>>> is
>>>>>>>>> updated and then analyzed, Couch has to consult that btree, purge
>>>>>>>>> all
>>>>>>>>> the
>>>>>>>>> KVs associated with the old version of the doc from each view, and
>>>>>>>>> then
>>>>>>>>> insert the new KVs. So the tracking information correlating docids
>>>>>>>>> and
>>>>>>>>> view
>>>>>>>>> keys is already available.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> See I did not know that :-) Although I should have guessed.
>>>>>>>>
>>>>>>>> However, in the mail before this one I argued that it doesn't make
>>>>>>>> sense
>>>>>>>> to combine or chain map-only views since you can always write a map
>>>>>>>> function
>>>>>>>> that does it in one step. Do you agree?
>>>>>>>>
>>>>>>>> You might also know the answer to this: is it possible to make the
>>>>>>>> Review
>>>>>>>> DB be a sort of view index on the current database? All it needs are
>>>>>>>> JSON
>>>>>>>> keys and values, no other fields.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> You'd still be left with the problem of generating unique docids
>>>>>>>>> for
>>>>>>>>> the
>>>>>>>>> documents in the Review DB, but I think that's a problem that needs
>>>>>>>>> to
>>>>>>>>> be
>>>>>>>>> solved. The restriction to only MR views with no duplicate keys
>>>>>>>>> across
>>>>>>>>> views seems too strong to me.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> Well, since the Review DB is a local(*) hidden database that's
>>>>>>>> handled
>>>>>>>> a
>>>>>>>> bit specially, I think the easiest is to assign _id a sequence
>>>>>>>> number
>>>>>>>> and
>>>>>>>> create a default view that indexes the documents by doc.key (for
>>>>>>>> updating
>>>>>>>> the value for that key). There will never be contention and we're
>>>>>>>> only
>>>>>>>> interested in the key index.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> We discussed this a little at CouchHack and I argued that the
>>>>>>> simplest
>>>>>>> solution is actually good for a few reasons.
>>>>>>>
>>>>>>> The simple solution: provide a mechanism to copy the rows of a
>>>>>>> grouped
>>>>>>> reduce function to a new database.
>>>>>>>
>>>>>>> Good because it is most like Hadoop/Google style map reduce. In that
>>>>>>> paradigm, the output of a map/reduce job is not incremental, and it
>>>>>>> is
>>>>>>> persisted in a way that allows for multiple later reduce stages to be
>>>>>>> run
>>>>>>> on
>>>>>>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>>>>>>> iterations of each stage while developing code.
>>>>>>>
>>>>>>> I like this also because it provides the needed functionality without
>>>>>>> adding
>>>>>>> any new primitives to CouchDB.
>>>>>>>
>>>>>>> The only downside of this approach is that it is not incremental. I'm
>>>>>>> not
>>>>>>> sure that incremental chainability has much promise, as the index
>>>>>>> management
>>>>>>> could be a pain, especially if you have branching chains.
>>>>>>>
>>>>>>> Another upside is that by reducing to a db, you give the user power
>>>>>>> to
>>>>>>> do
>>>>>>> things like use replication to merge multiple data sets before
>>>>>>> applying
>>>>>>> more
>>>>>>> views.
>>>>>>>
>>>>>>> I don't want to discourage anyone from experimenting with code, just
>>>>>>> want
>>>>>>> to
>>>>>>> point out this simple solution which would be Very Easy to implement.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> (*)local: I'm assuming that views are not replicated and need to be
>>>>>>>> recalculated for each CouchDB node. If they are replicated somehow,
>>>>>>>> I
>>>>>>>> think
>>>>>>>> it would still work but we'd have to look at it a little more.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> With that said, I'd prefer to spend my time extending the view
>>>>>>>>> engine
>>>>>>>>> to
>>>>>>>>> handle chainable MR workflows in a single shot. Especially in the
>>>>>>>>> simple
>>>>>>>>> sort_by_value case it just seems like a cleaner way to go about
>>>>>>>>> things.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>>>>>>
>>>>>>>> In a nutshell, I'm hoping that:
>>>>>>>> * A review is a new sort of view that has an "inputs" array in its
>>>>>>>> definition.
>>>>>>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>>>>>>> * It builds a persistent index of the incoming views when those get
>>>>>>>> updated.
>>>>>>>> * That index is then used to build the view index for the review
>>>>>>>> when
>>>>>>>> the
>>>>>>>> review gets updated.
>>>>>>>> * I think I covered the most important algorithms needed to
>>>>>>>> implement
>>>>>>>> this
>>>>>>>> in my original proposal.
>>>>>>>>
>>>>>>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>>>>>>
>>>>>>>> Wout.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>
>>>>>
>>>
>>>
>
>
Re: Proposal: Review DBs
Posted by Paul Davis <pa...@gmail.com>.
Zachary,
Hmm. Naming your derived databases as base_db-stage-etag doesn't sound
like a bad idea. Though I dunno how you'd communicate to clients to
start hitting the new versions and also doesn't tell the admins when to
drop old indices.
The only thing that comes to mind is to stick some intermediary in
between clients and the actual derived data to make it transparent and
also to allow you to know when you can clean up old versions etc.
I'll keep thinking on it.
Paul
Zachary Zolton wrote:
> okay, i'm starting to get ya. my question is, if i'm constantly
> dropping/recreating/reindexing the derived database, how can i keep
> serving requests from my website?
>
> one possible solution would be to time/etag/etc-stamp the derived db
> name, but that would seem to add a number of moving parts to my
> system.
>
> hmm... any ideas of how to pull a quick switcheroo on the backend of
> my system, without too much hassle in the client code?
>
> On Mon, Apr 27, 2009 at 1:44 PM, Paul Davis <pa...@gmail.com> wrote:
>
>> Zachary,
>>
>> No worries, the rough outline I'd do here is something like:
>>
>> 1. Figure out some member structure in the _design document that will
>> represent your data flow. For them moment I would do something extremely
>> simple as in:
>>
>> Assume:
>> db_name = "base_db"
>>
>> {
>> "_id": " _design/base",
>> "views": {
>> "stage-1": {
>> "map": "function(doc) ...",
>> "reduce": "function(keys, values, rereduce) ..."
>> }
>> },
>> "review": [
>> {"map": "function(doc) ...", "reduce": "function(keys, vals, rereduce)
>> ..."},
>> {"map": "function(doc) ...", "reduce": "function(keys, vals, rereduce)
>> ..."}
>> ]
>> }
>>
>> So the review member becomes the stages in your data flow. I'm avoiding any
>> forking or merging in this example in honor of the "make it work, make it
>> not suck" development flow.
>>
>> Now the basic algorithm would be something like:
>>
>> For each array element in the "review" member, create a db something like:
>>
>> base_db-stage-1 with a design document that contains a view with the first
>> element of the "reviews" member.
>> base_db-stage-2 with the second member and so on.
>>
>> Then your script can check the view status in each database either with a
>> cron (or an update_notifier) to do so, you can just:
>>
>> HEAD /base_db/_design/base/_view/stage-1
>>
>> And then check the returned ETag. For the moment this is exactly equivalent
>> to checking the database's update_seq because of how the etag is calculated,
>> but in the future when we track the last update_seq for each view change
>> this will be a free upgrade. Plus there's a bit more logical-ness to
>> checking "view state" instead of "db state".
>>
>> When the etag's don't match, you can just drop the next db in the flow,
>> create it, and then copy the view output. The drop/create just makes the
>> algorithm easily implementable for now. In the future there can be some
>> extra logic to only change the new view as far as it requires by iterating
>> over the two views and doing a merge sortish type of thing. I think...
>> Sounds like there should be a way at least.
>>
>> Once that works we can look at bolting on different fancy things like having
>> forking map/reduce mechanisms and my current pet idea of adding in the merge
>> stuff that has been talked about.
>>
>> This is actually starting to sound like a fun little project....
>>
>> HTH,
>> Paul Davis
>>
>> Zachary Zolton wrote:
>>
>>> paul
>>>
>>> alright... you've gotta give me the remedial explanation of what you
>>> meant here! (sorry, i'm still noob-ish)
>>>
>>> so, are you saying that i shouldn't even check for individual doc
>>> updates, but instead just recreate the entire database? that sounds
>>> like a job for cron, more so than the update notifier, right?
>>>
>>> i'd put up my current ruby script, but it deals update notifications
>>> in a way that's very specific to my data —probably very naïvely, to
>>> boot!
>>>
>>> zach
>>>
>>> On Mon, Apr 27, 2009 at 11:28 AM, Paul Davis
>>> <pa...@gmail.com> wrote:
>>>
>>>
>>>> Zachary,
>>>>
>>>> Awesome. The thing with non-incremental updates is that the basic
>>>> algorithm
>>>> would be to just look for updates to the view and on update, delete the
>>>> review DB, create a new one, and then dump the new data into it. I
>>>> wouldn't
>>>> try too hard for the optimizing updates at this point in time.
>>>>
>>>> Getting a ruby script out to show the basics should probably be the first
>>>> step. Beyond that we'll have to take it a step at a time.
>>>>
>>>> HTH,
>>>> Paul Davis
>>>>
>>>>
>>>> Zachary Zolton wrote:
>>>>
>>>>
>>>>> @jchris et al,
>>>>>
>>>>> if you had any pointer, on how to implement this, i have a strong
>>>>> motivation to try my hand at it.
>>>>>
>>>>> i have a janky ruby script running as an update notifier that looks
>>>>> for certain criteria, idiomatic to my data, that puts docs into a
>>>>> derived database. but i'm not terribly happy with my current
>>>>> implementation...
>>>>>
>>>>> is there a general-purpose algorithm for dealing with updates?
>>>>>
>>>>>
>>>>> cheers,
>>>>>
>>>>> zach
>>>>>
>>>>>
>>>>> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com>
>>>>> wrote:
>>>>>
>>>>>
>>>>>
>>>>>> Sent from my iPhone
>>>>>>
>>>>>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com>
>>>>>> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> Hi Adam,
>>>>>>>
>>>>>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> Hi Wout, thanks for writing this up.
>>>>>>>>
>>>>>>>> One comment about the map-only views: I think you'll find that Couch
>>>>>>>> has
>>>>>>>> already done a good bit of the work needed to support them, too.
>>>>>>>> Couch
>>>>>>>> maintains a btree for each design doc keyed on docid that stores all
>>>>>>>> the
>>>>>>>> view keys emitted by the maps over each document. When a document is
>>>>>>>> updated and then analyzed, Couch has to consult that btree, purge all
>>>>>>>> the
>>>>>>>> KVs associated with the old version of the doc from each view, and
>>>>>>>> then
>>>>>>>> insert the new KVs. So the tracking information correlating docids
>>>>>>>> and
>>>>>>>> view
>>>>>>>> keys is already available.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>> See I did not know that :-) Although I should have guessed.
>>>>>>>
>>>>>>> However, in the mail before this one I argued that it doesn't make
>>>>>>> sense
>>>>>>> to combine or chain map-only views since you can always write a map
>>>>>>> function
>>>>>>> that does it in one step. Do you agree?
>>>>>>>
>>>>>>> You might also know the answer to this: is it possible to make the
>>>>>>> Review
>>>>>>> DB be a sort of view index on the current database? All it needs are
>>>>>>> JSON
>>>>>>> keys and values, no other fields.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> You'd still be left with the problem of generating unique docids for
>>>>>>>> the
>>>>>>>> documents in the Review DB, but I think that's a problem that needs
>>>>>>>> to
>>>>>>>> be
>>>>>>>> solved. The restriction to only MR views with no duplicate keys
>>>>>>>> across
>>>>>>>> views seems too strong to me.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>> Well, since the Review DB is a local(*) hidden database that's handled
>>>>>>> a
>>>>>>> bit specially, I think the easiest is to assign _id a sequence number
>>>>>>> and
>>>>>>> create a default view that indexes the documents by doc.key (for
>>>>>>> updating
>>>>>>> the value for that key). There will never be contention and we're only
>>>>>>> interested in the key index.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> We discussed this a little at CouchHack and I argued that the simplest
>>>>>> solution is actually good for a few reasons.
>>>>>>
>>>>>> The simple solution: provide a mechanism to copy the rows of a grouped
>>>>>> reduce function to a new database.
>>>>>>
>>>>>> Good because it is most like Hadoop/Google style map reduce. In that
>>>>>> paradigm, the output of a map/reduce job is not incremental, and it is
>>>>>> persisted in a way that allows for multiple later reduce stages to be
>>>>>> run
>>>>>> on
>>>>>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>>>>>> iterations of each stage while developing code.
>>>>>>
>>>>>> I like this also because it provides the needed functionality without
>>>>>> adding
>>>>>> any new primitives to CouchDB.
>>>>>>
>>>>>> The only downside of this approach is that it is not incremental. I'm
>>>>>> not
>>>>>> sure that incremental chainability has much promise, as the index
>>>>>> management
>>>>>> could be a pain, especially if you have branching chains.
>>>>>>
>>>>>> Another upside is that by reducing to a db, you give the user power to
>>>>>> do
>>>>>> things like use replication to merge multiple data sets before applying
>>>>>> more
>>>>>> views.
>>>>>>
>>>>>> I don't want to discourage anyone from experimenting with code, just
>>>>>> want
>>>>>> to
>>>>>> point out this simple solution which would be Very Easy to implement.
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> (*)local: I'm assuming that views are not replicated and need to be
>>>>>>> recalculated for each CouchDB node. If they are replicated somehow, I
>>>>>>> think
>>>>>>> it would still work but we'd have to look at it a little more.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> With that said, I'd prefer to spend my time extending the view engine
>>>>>>>> to
>>>>>>>> handle chainable MR workflows in a single shot. Especially in the
>>>>>>>> simple
>>>>>>>> sort_by_value case it just seems like a cleaner way to go about
>>>>>>>> things.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>>>>>
>>>>>>> In a nutshell, I'm hoping that:
>>>>>>> * A review is a new sort of view that has an "inputs" array in its
>>>>>>> definition.
>>>>>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>>>>>> * It builds a persistent index of the incoming views when those get
>>>>>>> updated.
>>>>>>> * That index is then used to build the view index for the review when
>>>>>>> the
>>>>>>> review gets updated.
>>>>>>> * I think I covered the most important algorithms needed to implement
>>>>>>> this
>>>>>>> in my original proposal.
>>>>>>>
>>>>>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>>>>>
>>>>>>> Wout.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>
>>
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
okay, i'm starting to get ya. my question is, if i'm constantly
dropping/recreating/reindexing the derived database, how can i keep
serving requests from my website?
one possible solution would be to time/etag/etc-stamp the derived db
name, but that would seem to add a number of moving parts to my
system.
hmm... any ideas of how to pull a quick switcheroo on the backend of
my system, without too much hassle in the client code?
On Mon, Apr 27, 2009 at 1:44 PM, Paul Davis <pa...@gmail.com> wrote:
> Zachary,
>
> No worries, the rough outline I'd do here is something like:
>
> 1. Figure out some member structure in the _design document that will
> represent your data flow. For them moment I would do something extremely
> simple as in:
>
> Assume:
> db_name = "base_db"
>
> {
> "_id": " _design/base",
> "views": {
> "stage-1": {
> "map": "function(doc) ...",
> "reduce": "function(keys, values, rereduce) ..."
> }
> },
> "review": [
> {"map": "function(doc) ...", "reduce": "function(keys, vals, rereduce)
> ..."},
> {"map": "function(doc) ...", "reduce": "function(keys, vals, rereduce)
> ..."}
> ]
> }
>
> So the review member becomes the stages in your data flow. I'm avoiding any
> forking or merging in this example in honor of the "make it work, make it
> not suck" development flow.
>
> Now the basic algorithm would be something like:
>
> For each array element in the "review" member, create a db something like:
>
> base_db-stage-1 with a design document that contains a view with the first
> element of the "reviews" member.
> base_db-stage-2 with the second member and so on.
>
> Then your script can check the view status in each database either with a
> cron (or an update_notifier) to do so, you can just:
>
> HEAD /base_db/_design/base/_view/stage-1
>
> And then check the returned ETag. For the moment this is exactly equivalent
> to checking the database's update_seq because of how the etag is calculated,
> but in the future when we track the last update_seq for each view change
> this will be a free upgrade. Plus there's a bit more logical-ness to
> checking "view state" instead of "db state".
>
> When the etag's don't match, you can just drop the next db in the flow,
> create it, and then copy the view output. The drop/create just makes the
> algorithm easily implementable for now. In the future there can be some
> extra logic to only change the new view as far as it requires by iterating
> over the two views and doing a merge sortish type of thing. I think...
> Sounds like there should be a way at least.
>
> Once that works we can look at bolting on different fancy things like having
> forking map/reduce mechanisms and my current pet idea of adding in the merge
> stuff that has been talked about.
>
> This is actually starting to sound like a fun little project....
>
> HTH,
> Paul Davis
>
> Zachary Zolton wrote:
>>
>> paul
>>
>> alright... you've gotta give me the remedial explanation of what you
>> meant here! (sorry, i'm still noob-ish)
>>
>> so, are you saying that i shouldn't even check for individual doc
>> updates, but instead just recreate the entire database? that sounds
>> like a job for cron, more so than the update notifier, right?
>>
>> i'd put up my current ruby script, but it deals update notifications
>> in a way that's very specific to my data —probably very naïvely, to
>> boot!
>>
>> zach
>>
>> On Mon, Apr 27, 2009 at 11:28 AM, Paul Davis
>> <pa...@gmail.com> wrote:
>>
>>>
>>> Zachary,
>>>
>>> Awesome. The thing with non-incremental updates is that the basic
>>> algorithm
>>> would be to just look for updates to the view and on update, delete the
>>> review DB, create a new one, and then dump the new data into it. I
>>> wouldn't
>>> try too hard for the optimizing updates at this point in time.
>>>
>>> Getting a ruby script out to show the basics should probably be the first
>>> step. Beyond that we'll have to take it a step at a time.
>>>
>>> HTH,
>>> Paul Davis
>>>
>>>
>>> Zachary Zolton wrote:
>>>
>>>>
>>>> @jchris et al,
>>>>
>>>> if you had any pointer, on how to implement this, i have a strong
>>>> motivation to try my hand at it.
>>>>
>>>> i have a janky ruby script running as an update notifier that looks
>>>> for certain criteria, idiomatic to my data, that puts docs into a
>>>> derived database. but i'm not terribly happy with my current
>>>> implementation...
>>>>
>>>> is there a general-purpose algorithm for dealing with updates?
>>>>
>>>>
>>>> cheers,
>>>>
>>>> zach
>>>>
>>>>
>>>> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com>
>>>> wrote:
>>>>
>>>>
>>>>>
>>>>> Sent from my iPhone
>>>>>
>>>>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com>
>>>>> wrote:
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> Hi Adam,
>>>>>>
>>>>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Hi Wout, thanks for writing this up.
>>>>>>>
>>>>>>> One comment about the map-only views: I think you'll find that Couch
>>>>>>> has
>>>>>>> already done a good bit of the work needed to support them, too.
>>>>>>> Couch
>>>>>>> maintains a btree for each design doc keyed on docid that stores all
>>>>>>> the
>>>>>>> view keys emitted by the maps over each document. When a document is
>>>>>>> updated and then analyzed, Couch has to consult that btree, purge all
>>>>>>> the
>>>>>>> KVs associated with the old version of the doc from each view, and
>>>>>>> then
>>>>>>> insert the new KVs. So the tracking information correlating docids
>>>>>>> and
>>>>>>> view
>>>>>>> keys is already available.
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> See I did not know that :-) Although I should have guessed.
>>>>>>
>>>>>> However, in the mail before this one I argued that it doesn't make
>>>>>> sense
>>>>>> to combine or chain map-only views since you can always write a map
>>>>>> function
>>>>>> that does it in one step. Do you agree?
>>>>>>
>>>>>> You might also know the answer to this: is it possible to make the
>>>>>> Review
>>>>>> DB be a sort of view index on the current database? All it needs are
>>>>>> JSON
>>>>>> keys and values, no other fields.
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> You'd still be left with the problem of generating unique docids for
>>>>>>> the
>>>>>>> documents in the Review DB, but I think that's a problem that needs
>>>>>>> to
>>>>>>> be
>>>>>>> solved. The restriction to only MR views with no duplicate keys
>>>>>>> across
>>>>>>> views seems too strong to me.
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> Well, since the Review DB is a local(*) hidden database that's handled
>>>>>> a
>>>>>> bit specially, I think the easiest is to assign _id a sequence number
>>>>>> and
>>>>>> create a default view that indexes the documents by doc.key (for
>>>>>> updating
>>>>>> the value for that key). There will never be contention and we're only
>>>>>> interested in the key index.
>>>>>>
>>>>>>
>>>>>
>>>>> We discussed this a little at CouchHack and I argued that the simplest
>>>>> solution is actually good for a few reasons.
>>>>>
>>>>> The simple solution: provide a mechanism to copy the rows of a grouped
>>>>> reduce function to a new database.
>>>>>
>>>>> Good because it is most like Hadoop/Google style map reduce. In that
>>>>> paradigm, the output of a map/reduce job is not incremental, and it is
>>>>> persisted in a way that allows for multiple later reduce stages to be
>>>>> run
>>>>> on
>>>>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>>>>> iterations of each stage while developing code.
>>>>>
>>>>> I like this also because it provides the needed functionality without
>>>>> adding
>>>>> any new primitives to CouchDB.
>>>>>
>>>>> The only downside of this approach is that it is not incremental. I'm
>>>>> not
>>>>> sure that incremental chainability has much promise, as the index
>>>>> management
>>>>> could be a pain, especially if you have branching chains.
>>>>>
>>>>> Another upside is that by reducing to a db, you give the user power to
>>>>> do
>>>>> things like use replication to merge multiple data sets before applying
>>>>> more
>>>>> views.
>>>>>
>>>>> I don't want to discourage anyone from experimenting with code, just
>>>>> want
>>>>> to
>>>>> point out this simple solution which would be Very Easy to implement.
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> (*)local: I'm assuming that views are not replicated and need to be
>>>>>> recalculated for each CouchDB node. If they are replicated somehow, I
>>>>>> think
>>>>>> it would still work but we'd have to look at it a little more.
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> With that said, I'd prefer to spend my time extending the view engine
>>>>>>> to
>>>>>>> handle chainable MR workflows in a single shot. Especially in the
>>>>>>> simple
>>>>>>> sort_by_value case it just seems like a cleaner way to go about
>>>>>>> things.
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>>>>
>>>>>> In a nutshell, I'm hoping that:
>>>>>> * A review is a new sort of view that has an "inputs" array in its
>>>>>> definition.
>>>>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>>>>> * It builds a persistent index of the incoming views when those get
>>>>>> updated.
>>>>>> * That index is then used to build the view index for the review when
>>>>>> the
>>>>>> review gets updated.
>>>>>> * I think I covered the most important algorithms needed to implement
>>>>>> this
>>>>>> in my original proposal.
>>>>>>
>>>>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>>>>
>>>>>> Wout.
>>>>>>
>>>>>>
>>>
>>>
>
>
Re: Proposal: Review DBs
Posted by Paul Davis <pa...@gmail.com>.
Zachary,
No worries, the rough outline I'd do here is something like:
1. Figure out some member structure in the _design document that will
represent your data flow. For them moment I would do something extremely
simple as in:
Assume:
db_name = "base_db"
{
"_id": " _design/base",
"views": {
"stage-1": {
"map": "function(doc) ...",
"reduce": "function(keys, values, rereduce) ..."
}
},
"review": [
{"map": "function(doc) ...", "reduce": "function(keys, vals,
rereduce) ..."},
{"map": "function(doc) ...", "reduce": "function(keys, vals,
rereduce) ..."}
]
}
So the review member becomes the stages in your data flow. I'm avoiding
any forking or merging in this example in honor of the "make it work,
make it not suck" development flow.
Now the basic algorithm would be something like:
For each array element in the "review" member, create a db something like:
base_db-stage-1 with a design document that contains a view with the
first element of the "reviews" member.
base_db-stage-2 with the second member and so on.
Then your script can check the view status in each database either with
a cron (or an update_notifier) to do so, you can just:
HEAD /base_db/_design/base/_view/stage-1
And then check the returned ETag. For the moment this is exactly
equivalent to checking the database's update_seq because of how the etag
is calculated, but in the future when we track the last update_seq for
each view change this will be a free upgrade. Plus there's a bit more
logical-ness to checking "view state" instead of "db state".
When the etag's don't match, you can just drop the next db in the flow,
create it, and then copy the view output. The drop/create just makes the
algorithm easily implementable for now. In the future there can be some
extra logic to only change the new view as far as it requires by
iterating over the two views and doing a merge sortish type of thing. I
think... Sounds like there should be a way at least.
Once that works we can look at bolting on different fancy things like
having forking map/reduce mechanisms and my current pet idea of adding
in the merge stuff that has been talked about.
This is actually starting to sound like a fun little project....
HTH,
Paul Davis
Zachary Zolton wrote:
> paul
>
> alright... you've gotta give me the remedial explanation of what you
> meant here! (sorry, i'm still noob-ish)
>
> so, are you saying that i shouldn't even check for individual doc
> updates, but instead just recreate the entire database? that sounds
> like a job for cron, more so than the update notifier, right?
>
> i'd put up my current ruby script, but it deals update notifications
> in a way that's very specific to my data —probably very naïvely, to
> boot!
>
> zach
>
> On Mon, Apr 27, 2009 at 11:28 AM, Paul Davis
> <pa...@gmail.com> wrote:
>
>> Zachary,
>>
>> Awesome. The thing with non-incremental updates is that the basic algorithm
>> would be to just look for updates to the view and on update, delete the
>> review DB, create a new one, and then dump the new data into it. I wouldn't
>> try too hard for the optimizing updates at this point in time.
>>
>> Getting a ruby script out to show the basics should probably be the first
>> step. Beyond that we'll have to take it a step at a time.
>>
>> HTH,
>> Paul Davis
>>
>>
>> Zachary Zolton wrote:
>>
>>> @jchris et al,
>>>
>>> if you had any pointer, on how to implement this, i have a strong
>>> motivation to try my hand at it.
>>>
>>> i have a janky ruby script running as an update notifier that looks
>>> for certain criteria, idiomatic to my data, that puts docs into a
>>> derived database. but i'm not terribly happy with my current
>>> implementation...
>>>
>>> is there a general-purpose algorithm for dealing with updates?
>>>
>>>
>>> cheers,
>>>
>>> zach
>>>
>>>
>>> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com> wrote:
>>>
>>>
>>>> Sent from my iPhone
>>>>
>>>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com> wrote:
>>>>
>>>>
>>>>
>>>>> Hi Adam,
>>>>>
>>>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>>>
>>>>>
>>>>>
>>>>>> Hi Wout, thanks for writing this up.
>>>>>>
>>>>>> One comment about the map-only views: I think you'll find that Couch
>>>>>> has
>>>>>> already done a good bit of the work needed to support them, too. Couch
>>>>>> maintains a btree for each design doc keyed on docid that stores all
>>>>>> the
>>>>>> view keys emitted by the maps over each document. When a document is
>>>>>> updated and then analyzed, Couch has to consult that btree, purge all
>>>>>> the
>>>>>> KVs associated with the old version of the doc from each view, and then
>>>>>> insert the new KVs. So the tracking information correlating docids and
>>>>>> view
>>>>>> keys is already available.
>>>>>>
>>>>>>
>>>>> See I did not know that :-) Although I should have guessed.
>>>>>
>>>>> However, in the mail before this one I argued that it doesn't make sense
>>>>> to combine or chain map-only views since you can always write a map
>>>>> function
>>>>> that does it in one step. Do you agree?
>>>>>
>>>>> You might also know the answer to this: is it possible to make the
>>>>> Review
>>>>> DB be a sort of view index on the current database? All it needs are
>>>>> JSON
>>>>> keys and values, no other fields.
>>>>>
>>>>>
>>>>>
>>>>>> You'd still be left with the problem of generating unique docids for
>>>>>> the
>>>>>> documents in the Review DB, but I think that's a problem that needs to
>>>>>> be
>>>>>> solved. The restriction to only MR views with no duplicate keys across
>>>>>> views seems too strong to me.
>>>>>>
>>>>>>
>>>>> Well, since the Review DB is a local(*) hidden database that's handled a
>>>>> bit specially, I think the easiest is to assign _id a sequence number
>>>>> and
>>>>> create a default view that indexes the documents by doc.key (for
>>>>> updating
>>>>> the value for that key). There will never be contention and we're only
>>>>> interested in the key index.
>>>>>
>>>>>
>>>> We discussed this a little at CouchHack and I argued that the simplest
>>>> solution is actually good for a few reasons.
>>>>
>>>> The simple solution: provide a mechanism to copy the rows of a grouped
>>>> reduce function to a new database.
>>>>
>>>> Good because it is most like Hadoop/Google style map reduce. In that
>>>> paradigm, the output of a map/reduce job is not incremental, and it is
>>>> persisted in a way that allows for multiple later reduce stages to be run
>>>> on
>>>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>>>> iterations of each stage while developing code.
>>>>
>>>> I like this also because it provides the needed functionality without
>>>> adding
>>>> any new primitives to CouchDB.
>>>>
>>>> The only downside of this approach is that it is not incremental. I'm not
>>>> sure that incremental chainability has much promise, as the index
>>>> management
>>>> could be a pain, especially if you have branching chains.
>>>>
>>>> Another upside is that by reducing to a db, you give the user power to do
>>>> things like use replication to merge multiple data sets before applying
>>>> more
>>>> views.
>>>>
>>>> I don't want to discourage anyone from experimenting with code, just want
>>>> to
>>>> point out this simple solution which would be Very Easy to implement.
>>>>
>>>>
>>>>
>>>>> (*)local: I'm assuming that views are not replicated and need to be
>>>>> recalculated for each CouchDB node. If they are replicated somehow, I
>>>>> think
>>>>> it would still work but we'd have to look at it a little more.
>>>>>
>>>>>
>>>>>
>>>>>> With that said, I'd prefer to spend my time extending the view engine
>>>>>> to
>>>>>> handle chainable MR workflows in a single shot. Especially in the
>>>>>> simple
>>>>>> sort_by_value case it just seems like a cleaner way to go about things.
>>>>>>
>>>>>>
>>>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>>>
>>>>> In a nutshell, I'm hoping that:
>>>>> * A review is a new sort of view that has an "inputs" array in its
>>>>> definition.
>>>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>>>> * It builds a persistent index of the incoming views when those get
>>>>> updated.
>>>>> * That index is then used to build the view index for the review when
>>>>> the
>>>>> review gets updated.
>>>>> * I think I covered the most important algorithms needed to implement
>>>>> this
>>>>> in my original proposal.
>>>>>
>>>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>>>
>>>>> Wout.
>>>>>
>>>>>
>>
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
paul
alright... you've gotta give me the remedial explanation of what you
meant here! (sorry, i'm still noob-ish)
so, are you saying that i shouldn't even check for individual doc
updates, but instead just recreate the entire database? that sounds
like a job for cron, more so than the update notifier, right?
i'd put up my current ruby script, but it deals update notifications
in a way that's very specific to my data —probably very naïvely, to
boot!
zach
On Mon, Apr 27, 2009 at 11:28 AM, Paul Davis
<pa...@gmail.com> wrote:
> Zachary,
>
> Awesome. The thing with non-incremental updates is that the basic algorithm
> would be to just look for updates to the view and on update, delete the
> review DB, create a new one, and then dump the new data into it. I wouldn't
> try too hard for the optimizing updates at this point in time.
>
> Getting a ruby script out to show the basics should probably be the first
> step. Beyond that we'll have to take it a step at a time.
>
> HTH,
> Paul Davis
>
>
> Zachary Zolton wrote:
>>
>> @jchris et al,
>>
>> if you had any pointer, on how to implement this, i have a strong
>> motivation to try my hand at it.
>>
>> i have a janky ruby script running as an update notifier that looks
>> for certain criteria, idiomatic to my data, that puts docs into a
>> derived database. but i'm not terribly happy with my current
>> implementation...
>>
>> is there a general-purpose algorithm for dealing with updates?
>>
>>
>> cheers,
>>
>> zach
>>
>>
>> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com> wrote:
>>
>>>
>>> Sent from my iPhone
>>>
>>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com> wrote:
>>>
>>>
>>>>
>>>> Hi Adam,
>>>>
>>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>>
>>>>
>>>>>
>>>>> Hi Wout, thanks for writing this up.
>>>>>
>>>>> One comment about the map-only views: I think you'll find that Couch
>>>>> has
>>>>> already done a good bit of the work needed to support them, too. Couch
>>>>> maintains a btree for each design doc keyed on docid that stores all
>>>>> the
>>>>> view keys emitted by the maps over each document. When a document is
>>>>> updated and then analyzed, Couch has to consult that btree, purge all
>>>>> the
>>>>> KVs associated with the old version of the doc from each view, and then
>>>>> insert the new KVs. So the tracking information correlating docids and
>>>>> view
>>>>> keys is already available.
>>>>>
>>>>
>>>> See I did not know that :-) Although I should have guessed.
>>>>
>>>> However, in the mail before this one I argued that it doesn't make sense
>>>> to combine or chain map-only views since you can always write a map
>>>> function
>>>> that does it in one step. Do you agree?
>>>>
>>>> You might also know the answer to this: is it possible to make the
>>>> Review
>>>> DB be a sort of view index on the current database? All it needs are
>>>> JSON
>>>> keys and values, no other fields.
>>>>
>>>>
>>>>>
>>>>> You'd still be left with the problem of generating unique docids for
>>>>> the
>>>>> documents in the Review DB, but I think that's a problem that needs to
>>>>> be
>>>>> solved. The restriction to only MR views with no duplicate keys across
>>>>> views seems too strong to me.
>>>>>
>>>>
>>>> Well, since the Review DB is a local(*) hidden database that's handled a
>>>> bit specially, I think the easiest is to assign _id a sequence number
>>>> and
>>>> create a default view that indexes the documents by doc.key (for
>>>> updating
>>>> the value for that key). There will never be contention and we're only
>>>> interested in the key index.
>>>>
>>>
>>> We discussed this a little at CouchHack and I argued that the simplest
>>> solution is actually good for a few reasons.
>>>
>>> The simple solution: provide a mechanism to copy the rows of a grouped
>>> reduce function to a new database.
>>>
>>> Good because it is most like Hadoop/Google style map reduce. In that
>>> paradigm, the output of a map/reduce job is not incremental, and it is
>>> persisted in a way that allows for multiple later reduce stages to be run
>>> on
>>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>>> iterations of each stage while developing code.
>>>
>>> I like this also because it provides the needed functionality without
>>> adding
>>> any new primitives to CouchDB.
>>>
>>> The only downside of this approach is that it is not incremental. I'm not
>>> sure that incremental chainability has much promise, as the index
>>> management
>>> could be a pain, especially if you have branching chains.
>>>
>>> Another upside is that by reducing to a db, you give the user power to do
>>> things like use replication to merge multiple data sets before applying
>>> more
>>> views.
>>>
>>> I don't want to discourage anyone from experimenting with code, just want
>>> to
>>> point out this simple solution which would be Very Easy to implement.
>>>
>>>
>>>>
>>>> (*)local: I'm assuming that views are not replicated and need to be
>>>> recalculated for each CouchDB node. If they are replicated somehow, I
>>>> think
>>>> it would still work but we'd have to look at it a little more.
>>>>
>>>>
>>>>>
>>>>> With that said, I'd prefer to spend my time extending the view engine
>>>>> to
>>>>> handle chainable MR workflows in a single shot. Especially in the
>>>>> simple
>>>>> sort_by_value case it just seems like a cleaner way to go about things.
>>>>>
>>>>
>>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>>
>>>> In a nutshell, I'm hoping that:
>>>> * A review is a new sort of view that has an "inputs" array in its
>>>> definition.
>>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>>> * It builds a persistent index of the incoming views when those get
>>>> updated.
>>>> * That index is then used to build the view index for the review when
>>>> the
>>>> review gets updated.
>>>> * I think I covered the most important algorithms needed to implement
>>>> this
>>>> in my original proposal.
>>>>
>>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>>
>>>> Wout.
>>>>
>
>
Re: Proposal: Review DBs
Posted by Paul Davis <pa...@gmail.com>.
Zachary,
Awesome. The thing with non-incremental updates is that the basic
algorithm would be to just look for updates to the view and on update,
delete the review DB, create a new one, and then dump the new data into
it. I wouldn't try too hard for the optimizing updates at this point in
time.
Getting a ruby script out to show the basics should probably be the
first step. Beyond that we'll have to take it a step at a time.
HTH,
Paul Davis
Zachary Zolton wrote:
> @jchris et al,
>
> if you had any pointer, on how to implement this, i have a strong
> motivation to try my hand at it.
>
> i have a janky ruby script running as an update notifier that looks
> for certain criteria, idiomatic to my data, that puts docs into a
> derived database. but i'm not terribly happy with my current
> implementation...
>
> is there a general-purpose algorithm for dealing with updates?
>
>
> cheers,
>
> zach
>
>
> On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com> wrote:
>
>> Sent from my iPhone
>>
>> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com> wrote:
>>
>>
>>> Hi Adam,
>>>
>>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>>
>>>
>>>> Hi Wout, thanks for writing this up.
>>>>
>>>> One comment about the map-only views: I think you'll find that Couch has
>>>> already done a good bit of the work needed to support them, too. Couch
>>>> maintains a btree for each design doc keyed on docid that stores all the
>>>> view keys emitted by the maps over each document. When a document is
>>>> updated and then analyzed, Couch has to consult that btree, purge all the
>>>> KVs associated with the old version of the doc from each view, and then
>>>> insert the new KVs. So the tracking information correlating docids and view
>>>> keys is already available.
>>>>
>>> See I did not know that :-) Although I should have guessed.
>>>
>>> However, in the mail before this one I argued that it doesn't make sense
>>> to combine or chain map-only views since you can always write a map function
>>> that does it in one step. Do you agree?
>>>
>>> You might also know the answer to this: is it possible to make the Review
>>> DB be a sort of view index on the current database? All it needs are JSON
>>> keys and values, no other fields.
>>>
>>>
>>>> You'd still be left with the problem of generating unique docids for the
>>>> documents in the Review DB, but I think that's a problem that needs to be
>>>> solved. The restriction to only MR views with no duplicate keys across
>>>> views seems too strong to me.
>>>>
>>> Well, since the Review DB is a local(*) hidden database that's handled a
>>> bit specially, I think the easiest is to assign _id a sequence number and
>>> create a default view that indexes the documents by doc.key (for updating
>>> the value for that key). There will never be contention and we're only
>>> interested in the key index.
>>>
>> We discussed this a little at CouchHack and I argued that the simplest
>> solution is actually good for a few reasons.
>>
>> The simple solution: provide a mechanism to copy the rows of a grouped
>> reduce function to a new database.
>>
>> Good because it is most like Hadoop/Google style map reduce. In that
>> paradigm, the output of a map/reduce job is not incremental, and it is
>> persisted in a way that allows for multiple later reduce stages to be run on
>> it. It's common in Hadoop to chain many m/r stages, and to try a few
>> iterations of each stage while developing code.
>>
>> I like this also because it provides the needed functionality without adding
>> any new primitives to CouchDB.
>>
>> The only downside of this approach is that it is not incremental. I'm not
>> sure that incremental chainability has much promise, as the index management
>> could be a pain, especially if you have branching chains.
>>
>> Another upside is that by reducing to a db, you give the user power to do
>> things like use replication to merge multiple data sets before applying more
>> views.
>>
>> I don't want to discourage anyone from experimenting with code, just want to
>> point out this simple solution which would be Very Easy to implement.
>>
>>
>>> (*)local: I'm assuming that views are not replicated and need to be
>>> recalculated for each CouchDB node. If they are replicated somehow, I think
>>> it would still work but we'd have to look at it a little more.
>>>
>>>
>>>> With that said, I'd prefer to spend my time extending the view engine to
>>>> handle chainable MR workflows in a single shot. Especially in the simple
>>>> sort_by_value case it just seems like a cleaner way to go about things.
>>>>
>>> Yes, that seems to be the gist of all repliers and I agree :-)
>>>
>>> In a nutshell, I'm hoping that:
>>> * A review is a new sort of view that has an "inputs" array in its
>>> definition.
>>> * Only MR views are allowed as inputs, no KV duplication allowed.
>>> * It builds a persistent index of the incoming views when those get
>>> updated.
>>> * That index is then used to build the view index for the review when the
>>> review gets updated.
>>> * I think I covered the most important algorithms needed to implement this
>>> in my original proposal.
>>>
>>> Does this sound feasible? If so I'll update my proposal accordingly.
>>>
>>> Wout.
>>>
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
@jchris et al,
if you had any pointer, on how to implement this, i have a strong
motivation to try my hand at it.
i have a janky ruby script running as an update notifier that looks
for certain criteria, idiomatic to my data, that puts docs into a
derived database. but i'm not terribly happy with my current
implementation...
is there a general-purpose algorithm for dealing with updates?
cheers,
zach
On Sun, Apr 26, 2009 at 10:20 PM, Chris Anderson <jc...@gmail.com> wrote:
>
>
> Sent from my iPhone
>
> On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com> wrote:
>
>> Hi Adam,
>>
>> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>>
>>> Hi Wout, thanks for writing this up.
>>>
>>> One comment about the map-only views: I think you'll find that Couch has
>>> already done a good bit of the work needed to support them, too. Couch
>>> maintains a btree for each design doc keyed on docid that stores all the
>>> view keys emitted by the maps over each document. When a document is
>>> updated and then analyzed, Couch has to consult that btree, purge all the
>>> KVs associated with the old version of the doc from each view, and then
>>> insert the new KVs. So the tracking information correlating docids and view
>>> keys is already available.
>>
>> See I did not know that :-) Although I should have guessed.
>>
>> However, in the mail before this one I argued that it doesn't make sense
>> to combine or chain map-only views since you can always write a map function
>> that does it in one step. Do you agree?
>>
>> You might also know the answer to this: is it possible to make the Review
>> DB be a sort of view index on the current database? All it needs are JSON
>> keys and values, no other fields.
>>
>>> You'd still be left with the problem of generating unique docids for the
>>> documents in the Review DB, but I think that's a problem that needs to be
>>> solved. The restriction to only MR views with no duplicate keys across
>>> views seems too strong to me.
>>
>> Well, since the Review DB is a local(*) hidden database that's handled a
>> bit specially, I think the easiest is to assign _id a sequence number and
>> create a default view that indexes the documents by doc.key (for updating
>> the value for that key). There will never be contention and we're only
>> interested in the key index.
>
> We discussed this a little at CouchHack and I argued that the simplest
> solution is actually good for a few reasons.
>
> The simple solution: provide a mechanism to copy the rows of a grouped
> reduce function to a new database.
>
> Good because it is most like Hadoop/Google style map reduce. In that
> paradigm, the output of a map/reduce job is not incremental, and it is
> persisted in a way that allows for multiple later reduce stages to be run on
> it. It's common in Hadoop to chain many m/r stages, and to try a few
> iterations of each stage while developing code.
>
> I like this also because it provides the needed functionality without adding
> any new primitives to CouchDB.
>
> The only downside of this approach is that it is not incremental. I'm not
> sure that incremental chainability has much promise, as the index management
> could be a pain, especially if you have branching chains.
>
> Another upside is that by reducing to a db, you give the user power to do
> things like use replication to merge multiple data sets before applying more
> views.
>
> I don't want to discourage anyone from experimenting with code, just want to
> point out this simple solution which would be Very Easy to implement.
>
>>
>>
>> (*)local: I'm assuming that views are not replicated and need to be
>> recalculated for each CouchDB node. If they are replicated somehow, I think
>> it would still work but we'd have to look at it a little more.
>>
>>> With that said, I'd prefer to spend my time extending the view engine to
>>> handle chainable MR workflows in a single shot. Especially in the simple
>>> sort_by_value case it just seems like a cleaner way to go about things.
>>
>> Yes, that seems to be the gist of all repliers and I agree :-)
>>
>> In a nutshell, I'm hoping that:
>> * A review is a new sort of view that has an "inputs" array in its
>> definition.
>> * Only MR views are allowed as inputs, no KV duplication allowed.
>> * It builds a persistent index of the incoming views when those get
>> updated.
>> * That index is then used to build the view index for the review when the
>> review gets updated.
>> * I think I covered the most important algorithms needed to implement this
>> in my original proposal.
>>
>> Does this sound feasible? If so I'll update my proposal accordingly.
>>
>> Wout.
>
Re: Proposal: Review DBs
Posted by Chris Anderson <jc...@gmail.com>.
Sent from my iPhone
On Apr 26, 2009, at 2:26 PM, Wout Mertens <wo...@gmail.com>
wrote:
> Hi Adam,
>
> On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
>
>> Hi Wout, thanks for writing this up.
>>
>> One comment about the map-only views: I think you'll find that
>> Couch has already done a good bit of the work needed to support
>> them, too. Couch maintains a btree for each design doc keyed on
>> docid that stores all the view keys emitted by the maps over each
>> document. When a document is updated and then analyzed, Couch has
>> to consult that btree, purge all the KVs associated with the old
>> version of the doc from each view, and then insert the new KVs. So
>> the tracking information correlating docids and view keys is
>> already available.
>
> See I did not know that :-) Although I should have guessed.
>
> However, in the mail before this one I argued that it doesn't make
> sense to combine or chain map-only views since you can always write
> a map function that does it in one step. Do you agree?
>
> You might also know the answer to this: is it possible to make the
> Review DB be a sort of view index on the current database? All it
> needs are JSON keys and values, no other fields.
>
>> You'd still be left with the problem of generating unique docids
>> for the documents in the Review DB, but I think that's a problem
>> that needs to be solved. The restriction to only MR views with no
>> duplicate keys across views seems too strong to me.
>
> Well, since the Review DB is a local(*) hidden database that's
> handled a bit specially, I think the easiest is to assign _id a
> sequence number and create a default view that indexes the documents
> by doc.key (for updating the value for that key). There will never
> be contention and we're only interested in the key index.
We discussed this a little at CouchHack and I argued that the simplest
solution is actually good for a few reasons.
The simple solution: provide a mechanism to copy the rows of a grouped
reduce function to a new database.
Good because it is most like Hadoop/Google style map reduce. In that
paradigm, the output of a map/reduce job is not incremental, and it is
persisted in a way that allows for multiple later reduce stages to be
run on it. It's common in Hadoop to chain many m/r stages, and to try
a few iterations of each stage while developing code.
I like this also because it provides the needed functionality without
adding any new primitives to CouchDB.
The only downside of this approach is that it is not incremental. I'm
not sure that incremental chainability has much promise, as the index
management could be a pain, especially if you have branching chains.
Another upside is that by reducing to a db, you give the user power to
do things like use replication to merge multiple data sets before
applying more views.
I don't want to discourage anyone from experimenting with code, just
want to point out this simple solution which would be Very Easy to
implement.
>
>
> (*)local: I'm assuming that views are not replicated and need to be
> recalculated for each CouchDB node. If they are replicated somehow,
> I think it would still work but we'd have to look at it a little more.
>
>> With that said, I'd prefer to spend my time extending the view
>> engine to handle chainable MR workflows in a single shot.
>> Especially in the simple sort_by_value case it just seems like a
>> cleaner way to go about things.
>
> Yes, that seems to be the gist of all repliers and I agree :-)
>
> In a nutshell, I'm hoping that:
> * A review is a new sort of view that has an "inputs" array in its
> definition.
> * Only MR views are allowed as inputs, no KV duplication allowed.
> * It builds a persistent index of the incoming views when those get
> updated.
> * That index is then used to build the view index for the review
> when the review gets updated.
> * I think I covered the most important algorithms needed to
> implement this in my original proposal.
>
> Does this sound feasible? If so I'll update my proposal accordingly.
>
> Wout.
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
Hi Adam,
On Apr 22, 2009, at 4:48 PM, Adam Kocoloski wrote:
> Hi Wout, thanks for writing this up.
>
> One comment about the map-only views: I think you'll find that
> Couch has already done a good bit of the work needed to support
> them, too. Couch maintains a btree for each design doc keyed on
> docid that stores all the view keys emitted by the maps over each
> document. When a document is updated and then analyzed, Couch has
> to consult that btree, purge all the KVs associated with the old
> version of the doc from each view, and then insert the new KVs. So
> the tracking information correlating docids and view keys is already
> available.
See I did not know that :-) Although I should have guessed.
However, in the mail before this one I argued that it doesn't make
sense to combine or chain map-only views since you can always write a
map function that does it in one step. Do you agree?
You might also know the answer to this: is it possible to make the
Review DB be a sort of view index on the current database? All it
needs are JSON keys and values, no other fields.
> You'd still be left with the problem of generating unique docids for
> the documents in the Review DB, but I think that's a problem that
> needs to be solved. The restriction to only MR views with no
> duplicate keys across views seems too strong to me.
Well, since the Review DB is a local(*) hidden database that's handled
a bit specially, I think the easiest is to assign _id a sequence
number and create a default view that indexes the documents by doc.key
(for updating the value for that key). There will never be contention
and we're only interested in the key index.
(*)local: I'm assuming that views are not replicated and need to be
recalculated for each CouchDB node. If they are replicated somehow, I
think it would still work but we'd have to look at it a little more.
> With that said, I'd prefer to spend my time extending the view
> engine to handle chainable MR workflows in a single shot.
> Especially in the simple sort_by_value case it just seems like a
> cleaner way to go about things.
Yes, that seems to be the gist of all repliers and I agree :-)
In a nutshell, I'm hoping that:
* A review is a new sort of view that has an "inputs" array in its
definition.
* Only MR views are allowed as inputs, no KV duplication allowed.
* It builds a persistent index of the incoming views when those get
updated.
* That index is then used to build the view index for the review when
the review gets updated.
* I think I covered the most important algorithms needed to implement
this in my original proposal.
Does this sound feasible? If so I'll update my proposal accordingly.
Wout.
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
On Apr 22, 2009, at 8:31 PM, Adam Kocoloski wrote:
> Hi Zachary, something like that. The more I think about the problem
> the more I converge on a solution like what Wout has proposed. Some
> quick thoughts:
>
> * Remapping the output of a map isn't terribly useful. All the
> power is in remapping the output of a reduction.
That's the conclusion I came to as well. I actually had that worked
out as first but I saw that by eliminating it, the implementation
became simpler. Of course, I didn't know that CouchDB kept the list of
keys that a document emits, so I'm reworking my proposal.
> * Incremental generation of some complex multi-MR view still
> requires persisting the output of each step individually, even if
> you're only interested in the final result. At least, I don't yet
> see a clever way around it.
Me neither :-) Actually, I wonder how CouchDB handles _temp_views;
those must be persistent as well, in order for paging to work, no?
> * Dumping the output of the first MR(s) into a Review DB is an easy
> way to take advantage of code that's already written, but it's a bit
> wasteful. We could just take the results directly from the view
> btree(s) and send them to the next step in the workflow.
Well with my proposal I was hoping that someone would step forward and
say "you can store these key/value pairs as a view index, no
problem!" :)
> * I'm not yet sold on the HTTP API in Wout's proposal. I think I'd
> prefer to keep the existing API, and in the _design doc specify the
> full workflow required to generate a given view.
Hmmm... That could also work... It certainly makes for shorter URLs. I
was thinking that chunking the URL up like that in _review/_view/
_review it provides a lot of flexibility in view generation. However,
I see your point, there's no need for such flexibility. If multiple
_review/_view combinations are needed, they can just as well be listed
out specifically and CouchDB would take care not to duplicate indexes
where appropriate (like views).
So the first example would be
http://couchdb/db/_design/myblog/_view/tags_by_count
with the tags_by_count view defined as (for example)
"inputs":["tagcount"]
"map":"function(doc) { emit(doc.value,doc.key); }"
and tagcount of course being
"map":"function(doc) { for (var i in doc.tags) { emit(i, 1); }"
"reduce":"function(k,v,r) { return sum(v); }"
You're right, that is better.
Wout.
> Cheers, Adam
>
> On Apr 22, 2009, at 10:53 AM, Zachary Zolton wrote:
>
>> Such as having view definition, in the design doc, contain an array
>> of
>> objects, each with the map/reduce function pair attributes?
>>
>> On Wed, Apr 22, 2009 at 9:48 AM, Adam Kocoloski
>> <ko...@apache.org> wrote:
>>> Hi Wout, thanks for writing this up.
>>>
>>> One comment about the map-only views: I think you'll find that
>>> Couch has
>>> already done a good bit of the work needed to support them, too.
>>> Couch
>>> maintains a btree for each design doc keyed on docid that stores
>>> all the
>>> view keys emitted by the maps over each document. When a document
>>> is
>>> updated and then analyzed, Couch has to consult that btree, purge
>>> all the
>>> KVs associated with the old version of the doc from each view, and
>>> then
>>> insert the new KVs. So the tracking information correlating
>>> docids and view
>>> keys is already available.
>>>
>>> You'd still be left with the problem of generating unique docids
>>> for the
>>> documents in the Review DB, but I think that's a problem that
>>> needs to be
>>> solved. The restriction to only MR views with no duplicate keys
>>> across
>>> views seems too strong to me.
>>>
>>> With that said, I'd prefer to spend my time extending the view
>>> engine to
>>> handle chainable MR workflows in a single shot. Especially in the
>>> simple
>>> sort_by_value case it just seems like a cleaner way to go about
>>> things.
>>> Cheers,
>>>
>>> Adam
>>>
>>> On Apr 22, 2009, at 8:40 AM, Wout Mertens wrote:
>>>
>>>> Intro
>>>> =====
>>>> How do you sort by reduce value? How do you join views? How do
>>>> you get
>>>> unique view results? How do you cache group key reduces?
>>>>
>>>> I think that with the below proposed solution all the above and
>>>> more are
>>>> possible. The general idea is to store view results and run map/
>>>> reduce on
>>>> them. There's been some discussions about this but they went
>>>> nowhere. I've
>>>> been thinking about this issue a bit and I think it can be done.
>>>>
>>>> I'd like to call this feature a Review DB.
>>>>
>>>> Use cases
>>>> =========
>>>> - Suppose you want to know what tags are most popular on your
>>>> blog. Simply
>>>> get:
>>>>
>>>>
>>>> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>>>>
>>>> Where tags_by_count is a Review DB that gets input from the
>>>> tagcount view
>>>> and then runs the sort_by_value view on it, a map() function that
>>>> simply
>>>> emits (value,key).
>>>>
>>>> Likewise, show pages in order of popularity, whereby user can
>>>> vote up (+1)
>>>> or down (-1):
>>>>
>>>> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>>>>
>>>> - Given documents with attributes title, date and tags. You'd
>>>> like to know
>>>> the minimum value of date and a breakdown by count for tags, for
>>>> every
>>>> title. Normally you'd use 2 map+reduce views,
>>>> minimum_date_by_title and
>>>> tagcount_by_title, which you would then query separately. With a
>>>> Review DB,
>>>> you can let both views insert their results in the database and
>>>> then run a
>>>> view that combines the results in one view:
>>>>
>>>>
>>>> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>>>>
>>>> - This is not a way to run an on-the-fly map/reduce on a subset
>>>> of a view,
>>>> like if you want to find the median popularity score of
>>>> restaurants with
>>>> "Tony" in their name that are close to you.
>>>>
>>>> Implementation
>>>> ==============
>>>> A Review DB is a hidden database maintained by CouchDB with these
>>>> fields:
>>>> - _id of document is the string representation of the key
>>>> - "key" is the key of the incoming view row (unique)
>>>> - "value" is the value of the incoming view row
>>>>
>>>> I hope that this is sufficiently like a normal view that it can
>>>> be stored
>>>> as a normal view. _id is just there to make it doc-compliant, it
>>>> would be
>>>> much better if "key" were the actual key.
>>>>
>>>> A Review DB is defined in a design document like normal views.
>>>> Each review
>>>> is an entry in the "reviews" hash, and has a "incoming_views"
>>>> array that
>>>> lists all the views that should insert results in the review db
>>>> plus the
>>>> group level, as well as a normal "views" hash for further map/
>>>> reduce of the
>>>> review db (and perhaps another "reviews" hash for further result
>>>> processing?).
>>>>
>>>> Maintaining a database of results means that results have to be
>>>> updated or
>>>> even removed when documents change. I tried to make this work (in
>>>> theory)
>>>> for map-only views, but the resulting requirements are quite
>>>> messy. You
>>>> either need to cache the previous results of a view for each
>>>> document, or
>>>> you have to have an old version of the document available to
>>>> regenerate
>>>> those results.
>>>>
>>>> Therefore, a Review DB only accepts results from one or more map
>>>> +reduce
>>>> views. You define beforehand what the group_level of the keys is
>>>> that will
>>>> be inserted.
>>>>
>>>> Furthermore, a Review DB disallows (but doesn't enforce) having 2
>>>> views
>>>> that generate the same keys. Otherwise, refcounting would need to
>>>> be used
>>>> and while that's not difficult, I think there's limited value in
>>>> allowing
>>>> this.
>>>>
>>>> The Review DB needs updating every time the reduction for a group
>>>> key of
>>>> one of the participating views gets updated. Even though a map
>>>> +reduce view
>>>> has unique keys, we need a refcount since we have multiple views.
>>>> Whoever
>>>> got to insert its value last wins.
>>>>
>>>> There is a slight complication: group key values are calculated
>>>> on-the-fly
>>>> from the view result b-tree. So whenever a reduce call results in
>>>> a new
>>>> value for a b-tree node, AND that node is the upper node of a
>>>> subtree that
>>>> is completely part of a group key, that group key needs to be
>>>> marked for
>>>> recalculation.
>>>>
>>>> Likewise, if deletion/addition of a b-tree node results in the
>>>> removal/creation of the sole upper node of a group key subtree,
>>>> that group
>>>> key needs to be marked for removal/addition.
>>>>
>>>> This is the algorithm:
>>>> - When a reducing view gets updated, and it is part of a Review
>>>> DB, use
>>>> the 2 paragraphs above to keep a list of group keys that need
>>>> handling
>>>> - After updating the reduce() results, for each of the marked
>>>> group keys:
>>>> - If a group key gets removed:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - delete doc
>>>> - If a group key gets added:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - set doc.value to the row value
>>>> - else
>>>> - create doc with id=group key in string form, key=group key,
>>>> value=value
>>>> - If a group key gets updated:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - set doc.value to the row value
>>>> - else
>>>> - create doc with id=group key in string form, key=group key,
>>>> value=value
>>>>
>>>> As you can see, this is something CouchDB should do since it
>>>> knows when
>>>> it's updating group key reduction values and it knows if this was
>>>> an delete,
>>>> update or addition.
>>>>
>>>> View updates are done when the view is called; Review updates are
>>>> done at
>>>> this time as well. Views on Review DBs are done when they are
>>>> called.
>>>>
>>>> Summary
>>>> =======
>>>> Review DBs are a sort of view index that CouchDB can maintain
>>>> with little
>>>> overhead. It caches group key results and allows chained map+reduce
>>>> calculations using mostly existing frameworks.
>>>>
>>>> I think this would be a very useful feature for CouchDB to have.
>>>> There are
>>>> regularly requests for storing view results in a database for
>>>> post-processing on the mailing lists.
>>>>
>>>> I'm not saying this is a trivial change but it doesn't seem
>>>> technically
>>>> impossible to me either. (unless I missed something again; this
>>>> is the 5th
>>>> iteration of this proposal. Anyway I know *I* wouldn't be able to
>>>> code this
>>>> :-) )
>>>>
>>>> What do you think, oh dear devs?
>>>>
>>>> Wout.
>>>
>>>
>
Re: Proposal: Review DBs
Posted by Wout Mertens <wo...@gmail.com>.
On Apr 23, 2009, at 2:52 AM, Paul Davis wrote:
> I'm still kicking around ideas on how I might approach such an
> implementation. There are some obvious downfalls that Wout has come up
> on. Specifically, some obvious implementations would place some
> arduous constraints on emitted keys (namely: uniqueness, type must be
> string, string must not start with an underscore).
I really would prefer the review DB to be, in fact, a view index, but
since I don't know the code, I was trying to provide the ability to go
either way.
As for the uniqueness, I don't know if that is such a heavy
constraint. After all, it is M views that can emit multiple non-unique
KV pairs. There is no value in running a second M(R) after M, since
you can write any map_b(map_a()) as a new map_c(). Likewise the
results of map_a()+map_b() can be obtained by a map_c() function.
Therefore, review DBs should only contain the results of MR views,
that by definition deliver unique KV pairs. When combining 2 MR views
you can have KV collisions, but in all cases the MRs could be
rewritten to avoid collisions (simply postfix the keys that the M
emits).
As for the _id of a result row: If the review db would simply be a
view index, no _id is needed. If it has to be a db, the _id can simply
be the string version of the JSON representation of the key (it will
never start with a _ but it's quite wasteful) or some sequence number
for the review db.
> So there are some things to contemplate, but I think the general idea
> is pretty solid. Also as we start putting some serious effort into
> clustering CouchDB there's the eyebrow raising aspect that if we
> persist to DB's we might be able to leverage a lot of that for some
> added awesomeness.
I feel the same way.
I've also been contemplating using this persistence for _temp_views. I
think it can be done, given garbage collection on the temporary review
dbs. Then you could use the CouchDB view server farm to calculate
multi-dimensional views (all documents with tag A AND tag B AND
younger than 2 days).
Wout.
Re: Proposal: Review DBs
Posted by Paul Davis <pa...@gmail.com>.
Durring #couchhack we kicked around some ideas on how to get chainable
map reduce. I came in angling on a full work flow specified in a
single design doc that would basically be the current view API but on
sterroids.
The other idea that was presented fairly convincingly was this idea of
persisting the output of views into a second DB and then running more
map/reduce operations on the derived database. The main characteristic
of this method would be that it could be implemented in an external
library (though for efficiency would probably be Erlang). The main
idea was that there would be little if any alteration to the current
view mechanism.
Wout's proposal definitely shares alot of characteristics with the
second proposal. I would argue as Adam does that changing the HTTP API
or even adding to it just creates more confusion. With the recent
release of Hovercraft we have a great point to start adding the
necessary view bits to actually do all of this persistence.
I'm still kicking around ideas on how I might approach such an
implementation. There are some obvious downfalls that Wout has come up
on. Specifically, some obvious implementations would place some
arduous constraints on emitted keys (namely: uniqueness, type must be
string, string must not start with an underscore).
So there are some things to contemplate, but I think the general idea
is pretty solid. Also as we start putting some serious effort into
clustering CouchDB there's the eyebrow raising aspect that if we
persist to DB's we might be able to leverage a lot of that for some
added awesomeness.
Anyway, those are just some raw thoughts.
Paul Davis
On Wed, Apr 22, 2009 at 2:31 PM, Adam Kocoloski <ko...@apache.org> wrote:
> Hi Zachary, something like that. The more I think about the problem the
> more I converge on a solution like what Wout has proposed. Some quick
> thoughts:
>
> * Remapping the output of a map isn't terribly useful. All the power is in
> remapping the output of a reduction.
>
> * Incremental generation of some complex multi-MR view still requires
> persisting the output of each step individually, even if you're only
> interested in the final result. At least, I don't yet see a clever way
> around it.
>
> * Dumping the output of the first MR(s) into a Review DB is an easy way to
> take advantage of code that's already written, but it's a bit wasteful. We
> could just take the results directly from the view btree(s) and send them to
> the next step in the workflow.
>
> * I'm not yet sold on the HTTP API in Wout's proposal. I think I'd prefer
> to keep the existing API, and in the _design doc specify the full workflow
> required to generate a given view.
>
> Cheers, Adam
>
> On Apr 22, 2009, at 10:53 AM, Zachary Zolton wrote:
>
>> Such as having view definition, in the design doc, contain an array of
>> objects, each with the map/reduce function pair attributes?
>>
>> On Wed, Apr 22, 2009 at 9:48 AM, Adam Kocoloski <ko...@apache.org>
>> wrote:
>>>
>>> Hi Wout, thanks for writing this up.
>>>
>>> One comment about the map-only views: I think you'll find that Couch has
>>> already done a good bit of the work needed to support them, too. Couch
>>> maintains a btree for each design doc keyed on docid that stores all the
>>> view keys emitted by the maps over each document. When a document is
>>> updated and then analyzed, Couch has to consult that btree, purge all the
>>> KVs associated with the old version of the doc from each view, and then
>>> insert the new KVs. So the tracking information correlating docids and
>>> view
>>> keys is already available.
>>>
>>> You'd still be left with the problem of generating unique docids for the
>>> documents in the Review DB, but I think that's a problem that needs to be
>>> solved. The restriction to only MR views with no duplicate keys across
>>> views seems too strong to me.
>>>
>>> With that said, I'd prefer to spend my time extending the view engine to
>>> handle chainable MR workflows in a single shot. Especially in the simple
>>> sort_by_value case it just seems like a cleaner way to go about things.
>>> Cheers,
>>>
>>> Adam
>>>
>>> On Apr 22, 2009, at 8:40 AM, Wout Mertens wrote:
>>>
>>>> Intro
>>>> =====
>>>> How do you sort by reduce value? How do you join views? How do you get
>>>> unique view results? How do you cache group key reduces?
>>>>
>>>> I think that with the below proposed solution all the above and more are
>>>> possible. The general idea is to store view results and run map/reduce
>>>> on
>>>> them. There's been some discussions about this but they went nowhere.
>>>> I've
>>>> been thinking about this issue a bit and I think it can be done.
>>>>
>>>> I'd like to call this feature a Review DB.
>>>>
>>>> Use cases
>>>> =========
>>>> - Suppose you want to know what tags are most popular on your blog.
>>>> Simply
>>>> get:
>>>>
>>>>
>>>>
>>>> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>>>>
>>>> Where tags_by_count is a Review DB that gets input from the tagcount
>>>> view
>>>> and then runs the sort_by_value view on it, a map() function that simply
>>>> emits (value,key).
>>>>
>>>> Likewise, show pages in order of popularity, whereby user can vote up
>>>> (+1)
>>>> or down (-1):
>>>>
>>>> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>>>>
>>>> - Given documents with attributes title, date and tags. You'd like to
>>>> know
>>>> the minimum value of date and a breakdown by count for tags, for every
>>>> title. Normally you'd use 2 map+reduce views, minimum_date_by_title and
>>>> tagcount_by_title, which you would then query separately. With a Review
>>>> DB,
>>>> you can let both views insert their results in the database and then run
>>>> a
>>>> view that combines the results in one view:
>>>>
>>>>
>>>>
>>>> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>>>>
>>>> - This is not a way to run an on-the-fly map/reduce on a subset of a
>>>> view,
>>>> like if you want to find the median popularity score of restaurants with
>>>> "Tony" in their name that are close to you.
>>>>
>>>> Implementation
>>>> ==============
>>>> A Review DB is a hidden database maintained by CouchDB with these
>>>> fields:
>>>> - _id of document is the string representation of the key
>>>> - "key" is the key of the incoming view row (unique)
>>>> - "value" is the value of the incoming view row
>>>>
>>>> I hope that this is sufficiently like a normal view that it can be
>>>> stored
>>>> as a normal view. _id is just there to make it doc-compliant, it would
>>>> be
>>>> much better if "key" were the actual key.
>>>>
>>>> A Review DB is defined in a design document like normal views. Each
>>>> review
>>>> is an entry in the "reviews" hash, and has a "incoming_views" array that
>>>> lists all the views that should insert results in the review db plus the
>>>> group level, as well as a normal "views" hash for further map/reduce of
>>>> the
>>>> review db (and perhaps another "reviews" hash for further result
>>>> processing?).
>>>>
>>>> Maintaining a database of results means that results have to be updated
>>>> or
>>>> even removed when documents change. I tried to make this work (in
>>>> theory)
>>>> for map-only views, but the resulting requirements are quite messy. You
>>>> either need to cache the previous results of a view for each document,
>>>> or
>>>> you have to have an old version of the document available to regenerate
>>>> those results.
>>>>
>>>> Therefore, a Review DB only accepts results from one or more map+reduce
>>>> views. You define beforehand what the group_level of the keys is that
>>>> will
>>>> be inserted.
>>>>
>>>> Furthermore, a Review DB disallows (but doesn't enforce) having 2 views
>>>> that generate the same keys. Otherwise, refcounting would need to be
>>>> used
>>>> and while that's not difficult, I think there's limited value in
>>>> allowing
>>>> this.
>>>>
>>>> The Review DB needs updating every time the reduction for a group key of
>>>> one of the participating views gets updated. Even though a map+reduce
>>>> view
>>>> has unique keys, we need a refcount since we have multiple views.
>>>> Whoever
>>>> got to insert its value last wins.
>>>>
>>>> There is a slight complication: group key values are calculated
>>>> on-the-fly
>>>> from the view result b-tree. So whenever a reduce call results in a new
>>>> value for a b-tree node, AND that node is the upper node of a subtree
>>>> that
>>>> is completely part of a group key, that group key needs to be marked for
>>>> recalculation.
>>>>
>>>> Likewise, if deletion/addition of a b-tree node results in the
>>>> removal/creation of the sole upper node of a group key subtree, that
>>>> group
>>>> key needs to be marked for removal/addition.
>>>>
>>>> This is the algorithm:
>>>> - When a reducing view gets updated, and it is part of a Review DB, use
>>>> the 2 paragraphs above to keep a list of group keys that need handling
>>>> - After updating the reduce() results, for each of the marked group
>>>> keys:
>>>> - If a group key gets removed:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - delete doc
>>>> - If a group key gets added:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - set doc.value to the row value
>>>> - else
>>>> - create doc with id=group key in string form, key=group key,
>>>> value=value
>>>> - If a group key gets updated:
>>>> - look up doc with key=group key in review db. If exists:
>>>> - set doc.value to the row value
>>>> - else
>>>> - create doc with id=group key in string form, key=group key,
>>>> value=value
>>>>
>>>> As you can see, this is something CouchDB should do since it knows when
>>>> it's updating group key reduction values and it knows if this was an
>>>> delete,
>>>> update or addition.
>>>>
>>>> View updates are done when the view is called; Review updates are done
>>>> at
>>>> this time as well. Views on Review DBs are done when they are called.
>>>>
>>>> Summary
>>>> =======
>>>> Review DBs are a sort of view index that CouchDB can maintain with
>>>> little
>>>> overhead. It caches group key results and allows chained map+reduce
>>>> calculations using mostly existing frameworks.
>>>>
>>>> I think this would be a very useful feature for CouchDB to have. There
>>>> are
>>>> regularly requests for storing view results in a database for
>>>> post-processing on the mailing lists.
>>>>
>>>> I'm not saying this is a trivial change but it doesn't seem technically
>>>> impossible to me either. (unless I missed something again; this is the
>>>> 5th
>>>> iteration of this proposal. Anyway I know *I* wouldn't be able to code
>>>> this
>>>> :-) )
>>>>
>>>> What do you think, oh dear devs?
>>>>
>>>> Wout.
>>>
>>>
>
>
Re: Proposal: Review DBs
Posted by Adam Kocoloski <ko...@apache.org>.
Hi Zachary, something like that. The more I think about the problem
the more I converge on a solution like what Wout has proposed. Some
quick thoughts:
* Remapping the output of a map isn't terribly useful. All the power
is in remapping the output of a reduction.
* Incremental generation of some complex multi-MR view still requires
persisting the output of each step individually, even if you're only
interested in the final result. At least, I don't yet see a clever
way around it.
* Dumping the output of the first MR(s) into a Review DB is an easy
way to take advantage of code that's already written, but it's a bit
wasteful. We could just take the results directly from the view
btree(s) and send them to the next step in the workflow.
* I'm not yet sold on the HTTP API in Wout's proposal. I think I'd
prefer to keep the existing API, and in the _design doc specify the
full workflow required to generate a given view.
Cheers, Adam
On Apr 22, 2009, at 10:53 AM, Zachary Zolton wrote:
> Such as having view definition, in the design doc, contain an array of
> objects, each with the map/reduce function pair attributes?
>
> On Wed, Apr 22, 2009 at 9:48 AM, Adam Kocoloski
> <ko...@apache.org> wrote:
>> Hi Wout, thanks for writing this up.
>>
>> One comment about the map-only views: I think you'll find that
>> Couch has
>> already done a good bit of the work needed to support them, too.
>> Couch
>> maintains a btree for each design doc keyed on docid that stores
>> all the
>> view keys emitted by the maps over each document. When a document is
>> updated and then analyzed, Couch has to consult that btree, purge
>> all the
>> KVs associated with the old version of the doc from each view, and
>> then
>> insert the new KVs. So the tracking information correlating docids
>> and view
>> keys is already available.
>>
>> You'd still be left with the problem of generating unique docids
>> for the
>> documents in the Review DB, but I think that's a problem that needs
>> to be
>> solved. The restriction to only MR views with no duplicate keys
>> across
>> views seems too strong to me.
>>
>> With that said, I'd prefer to spend my time extending the view
>> engine to
>> handle chainable MR workflows in a single shot. Especially in the
>> simple
>> sort_by_value case it just seems like a cleaner way to go about
>> things.
>> Cheers,
>>
>> Adam
>>
>> On Apr 22, 2009, at 8:40 AM, Wout Mertens wrote:
>>
>>> Intro
>>> =====
>>> How do you sort by reduce value? How do you join views? How do you
>>> get
>>> unique view results? How do you cache group key reduces?
>>>
>>> I think that with the below proposed solution all the above and
>>> more are
>>> possible. The general idea is to store view results and run map/
>>> reduce on
>>> them. There's been some discussions about this but they went
>>> nowhere. I've
>>> been thinking about this issue a bit and I think it can be done.
>>>
>>> I'd like to call this feature a Review DB.
>>>
>>> Use cases
>>> =========
>>> - Suppose you want to know what tags are most popular on your
>>> blog. Simply
>>> get:
>>>
>>>
>>> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>>>
>>> Where tags_by_count is a Review DB that gets input from the
>>> tagcount view
>>> and then runs the sort_by_value view on it, a map() function that
>>> simply
>>> emits (value,key).
>>>
>>> Likewise, show pages in order of popularity, whereby user can vote
>>> up (+1)
>>> or down (-1):
>>>
>>> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>>>
>>> - Given documents with attributes title, date and tags. You'd like
>>> to know
>>> the minimum value of date and a breakdown by count for tags, for
>>> every
>>> title. Normally you'd use 2 map+reduce views,
>>> minimum_date_by_title and
>>> tagcount_by_title, which you would then query separately. With a
>>> Review DB,
>>> you can let both views insert their results in the database and
>>> then run a
>>> view that combines the results in one view:
>>>
>>>
>>> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>>>
>>> - This is not a way to run an on-the-fly map/reduce on a subset of
>>> a view,
>>> like if you want to find the median popularity score of
>>> restaurants with
>>> "Tony" in their name that are close to you.
>>>
>>> Implementation
>>> ==============
>>> A Review DB is a hidden database maintained by CouchDB with these
>>> fields:
>>> - _id of document is the string representation of the key
>>> - "key" is the key of the incoming view row (unique)
>>> - "value" is the value of the incoming view row
>>>
>>> I hope that this is sufficiently like a normal view that it can be
>>> stored
>>> as a normal view. _id is just there to make it doc-compliant, it
>>> would be
>>> much better if "key" were the actual key.
>>>
>>> A Review DB is defined in a design document like normal views.
>>> Each review
>>> is an entry in the "reviews" hash, and has a "incoming_views"
>>> array that
>>> lists all the views that should insert results in the review db
>>> plus the
>>> group level, as well as a normal "views" hash for further map/
>>> reduce of the
>>> review db (and perhaps another "reviews" hash for further result
>>> processing?).
>>>
>>> Maintaining a database of results means that results have to be
>>> updated or
>>> even removed when documents change. I tried to make this work (in
>>> theory)
>>> for map-only views, but the resulting requirements are quite
>>> messy. You
>>> either need to cache the previous results of a view for each
>>> document, or
>>> you have to have an old version of the document available to
>>> regenerate
>>> those results.
>>>
>>> Therefore, a Review DB only accepts results from one or more map
>>> +reduce
>>> views. You define beforehand what the group_level of the keys is
>>> that will
>>> be inserted.
>>>
>>> Furthermore, a Review DB disallows (but doesn't enforce) having 2
>>> views
>>> that generate the same keys. Otherwise, refcounting would need to
>>> be used
>>> and while that's not difficult, I think there's limited value in
>>> allowing
>>> this.
>>>
>>> The Review DB needs updating every time the reduction for a group
>>> key of
>>> one of the participating views gets updated. Even though a map
>>> +reduce view
>>> has unique keys, we need a refcount since we have multiple views.
>>> Whoever
>>> got to insert its value last wins.
>>>
>>> There is a slight complication: group key values are calculated on-
>>> the-fly
>>> from the view result b-tree. So whenever a reduce call results in
>>> a new
>>> value for a b-tree node, AND that node is the upper node of a
>>> subtree that
>>> is completely part of a group key, that group key needs to be
>>> marked for
>>> recalculation.
>>>
>>> Likewise, if deletion/addition of a b-tree node results in the
>>> removal/creation of the sole upper node of a group key subtree,
>>> that group
>>> key needs to be marked for removal/addition.
>>>
>>> This is the algorithm:
>>> - When a reducing view gets updated, and it is part of a Review
>>> DB, use
>>> the 2 paragraphs above to keep a list of group keys that need
>>> handling
>>> - After updating the reduce() results, for each of the marked
>>> group keys:
>>> - If a group key gets removed:
>>> - look up doc with key=group key in review db. If exists:
>>> - delete doc
>>> - If a group key gets added:
>>> - look up doc with key=group key in review db. If exists:
>>> - set doc.value to the row value
>>> - else
>>> - create doc with id=group key in string form, key=group key,
>>> value=value
>>> - If a group key gets updated:
>>> - look up doc with key=group key in review db. If exists:
>>> - set doc.value to the row value
>>> - else
>>> - create doc with id=group key in string form, key=group key,
>>> value=value
>>>
>>> As you can see, this is something CouchDB should do since it knows
>>> when
>>> it's updating group key reduction values and it knows if this was
>>> an delete,
>>> update or addition.
>>>
>>> View updates are done when the view is called; Review updates are
>>> done at
>>> this time as well. Views on Review DBs are done when they are
>>> called.
>>>
>>> Summary
>>> =======
>>> Review DBs are a sort of view index that CouchDB can maintain with
>>> little
>>> overhead. It caches group key results and allows chained map+reduce
>>> calculations using mostly existing frameworks.
>>>
>>> I think this would be a very useful feature for CouchDB to have.
>>> There are
>>> regularly requests for storing view results in a database for
>>> post-processing on the mailing lists.
>>>
>>> I'm not saying this is a trivial change but it doesn't seem
>>> technically
>>> impossible to me either. (unless I missed something again; this is
>>> the 5th
>>> iteration of this proposal. Anyway I know *I* wouldn't be able to
>>> code this
>>> :-) )
>>>
>>> What do you think, oh dear devs?
>>>
>>> Wout.
>>
>>
Re: Proposal: Review DBs
Posted by Zachary Zolton <za...@gmail.com>.
Such as having view definition, in the design doc, contain an array of
objects, each with the map/reduce function pair attributes?
On Wed, Apr 22, 2009 at 9:48 AM, Adam Kocoloski <ko...@apache.org> wrote:
> Hi Wout, thanks for writing this up.
>
> One comment about the map-only views: I think you'll find that Couch has
> already done a good bit of the work needed to support them, too. Couch
> maintains a btree for each design doc keyed on docid that stores all the
> view keys emitted by the maps over each document. When a document is
> updated and then analyzed, Couch has to consult that btree, purge all the
> KVs associated with the old version of the doc from each view, and then
> insert the new KVs. So the tracking information correlating docids and view
> keys is already available.
>
> You'd still be left with the problem of generating unique docids for the
> documents in the Review DB, but I think that's a problem that needs to be
> solved. The restriction to only MR views with no duplicate keys across
> views seems too strong to me.
>
> With that said, I'd prefer to spend my time extending the view engine to
> handle chainable MR workflows in a single shot. Especially in the simple
> sort_by_value case it just seems like a cleaner way to go about things.
> Cheers,
>
> Adam
>
> On Apr 22, 2009, at 8:40 AM, Wout Mertens wrote:
>
>> Intro
>> =====
>> How do you sort by reduce value? How do you join views? How do you get
>> unique view results? How do you cache group key reduces?
>>
>> I think that with the below proposed solution all the above and more are
>> possible. The general idea is to store view results and run map/reduce on
>> them. There's been some discussions about this but they went nowhere. I've
>> been thinking about this issue a bit and I think it can be done.
>>
>> I'd like to call this feature a Review DB.
>>
>> Use cases
>> =========
>> - Suppose you want to know what tags are most popular on your blog. Simply
>> get:
>>
>>
>> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>>
>> Where tags_by_count is a Review DB that gets input from the tagcount view
>> and then runs the sort_by_value view on it, a map() function that simply
>> emits (value,key).
>>
>> Likewise, show pages in order of popularity, whereby user can vote up (+1)
>> or down (-1):
>>
>> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/sort_by_value
>>
>> - Given documents with attributes title, date and tags. You'd like to know
>> the minimum value of date and a breakdown by count for tags, for every
>> title. Normally you'd use 2 map+reduce views, minimum_date_by_title and
>> tagcount_by_title, which you would then query separately. With a Review DB,
>> you can let both views insert their results in the database and then run a
>> view that combines the results in one view:
>>
>>
>> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>>
>> - This is not a way to run an on-the-fly map/reduce on a subset of a view,
>> like if you want to find the median popularity score of restaurants with
>> "Tony" in their name that are close to you.
>>
>> Implementation
>> ==============
>> A Review DB is a hidden database maintained by CouchDB with these fields:
>> - _id of document is the string representation of the key
>> - "key" is the key of the incoming view row (unique)
>> - "value" is the value of the incoming view row
>>
>> I hope that this is sufficiently like a normal view that it can be stored
>> as a normal view. _id is just there to make it doc-compliant, it would be
>> much better if "key" were the actual key.
>>
>> A Review DB is defined in a design document like normal views. Each review
>> is an entry in the "reviews" hash, and has a "incoming_views" array that
>> lists all the views that should insert results in the review db plus the
>> group level, as well as a normal "views" hash for further map/reduce of the
>> review db (and perhaps another "reviews" hash for further result
>> processing?).
>>
>> Maintaining a database of results means that results have to be updated or
>> even removed when documents change. I tried to make this work (in theory)
>> for map-only views, but the resulting requirements are quite messy. You
>> either need to cache the previous results of a view for each document, or
>> you have to have an old version of the document available to regenerate
>> those results.
>>
>> Therefore, a Review DB only accepts results from one or more map+reduce
>> views. You define beforehand what the group_level of the keys is that will
>> be inserted.
>>
>> Furthermore, a Review DB disallows (but doesn't enforce) having 2 views
>> that generate the same keys. Otherwise, refcounting would need to be used
>> and while that's not difficult, I think there's limited value in allowing
>> this.
>>
>> The Review DB needs updating every time the reduction for a group key of
>> one of the participating views gets updated. Even though a map+reduce view
>> has unique keys, we need a refcount since we have multiple views. Whoever
>> got to insert its value last wins.
>>
>> There is a slight complication: group key values are calculated on-the-fly
>> from the view result b-tree. So whenever a reduce call results in a new
>> value for a b-tree node, AND that node is the upper node of a subtree that
>> is completely part of a group key, that group key needs to be marked for
>> recalculation.
>>
>> Likewise, if deletion/addition of a b-tree node results in the
>> removal/creation of the sole upper node of a group key subtree, that group
>> key needs to be marked for removal/addition.
>>
>> This is the algorithm:
>> - When a reducing view gets updated, and it is part of a Review DB, use
>> the 2 paragraphs above to keep a list of group keys that need handling
>> - After updating the reduce() results, for each of the marked group keys:
>> - If a group key gets removed:
>> - look up doc with key=group key in review db. If exists:
>> - delete doc
>> - If a group key gets added:
>> - look up doc with key=group key in review db. If exists:
>> - set doc.value to the row value
>> - else
>> - create doc with id=group key in string form, key=group key,
>> value=value
>> - If a group key gets updated:
>> - look up doc with key=group key in review db. If exists:
>> - set doc.value to the row value
>> - else
>> - create doc with id=group key in string form, key=group key,
>> value=value
>>
>> As you can see, this is something CouchDB should do since it knows when
>> it's updating group key reduction values and it knows if this was an delete,
>> update or addition.
>>
>> View updates are done when the view is called; Review updates are done at
>> this time as well. Views on Review DBs are done when they are called.
>>
>> Summary
>> =======
>> Review DBs are a sort of view index that CouchDB can maintain with little
>> overhead. It caches group key results and allows chained map+reduce
>> calculations using mostly existing frameworks.
>>
>> I think this would be a very useful feature for CouchDB to have. There are
>> regularly requests for storing view results in a database for
>> post-processing on the mailing lists.
>>
>> I'm not saying this is a trivial change but it doesn't seem technically
>> impossible to me either. (unless I missed something again; this is the 5th
>> iteration of this proposal. Anyway I know *I* wouldn't be able to code this
>> :-) )
>>
>> What do you think, oh dear devs?
>>
>> Wout.
>
>
Re: Proposal: Review DBs
Posted by Adam Kocoloski <ko...@apache.org>.
Hi Wout, thanks for writing this up.
One comment about the map-only views: I think you'll find that Couch
has already done a good bit of the work needed to support them, too.
Couch maintains a btree for each design doc keyed on docid that stores
all the view keys emitted by the maps over each document. When a
document is updated and then analyzed, Couch has to consult that
btree, purge all the KVs associated with the old version of the doc
from each view, and then insert the new KVs. So the tracking
information correlating docids and view keys is already available.
You'd still be left with the problem of generating unique docids for
the documents in the Review DB, but I think that's a problem that
needs to be solved. The restriction to only MR views with no
duplicate keys across views seems too strong to me.
With that said, I'd prefer to spend my time extending the view engine
to handle chainable MR workflows in a single shot. Especially in the
simple sort_by_value case it just seems like a cleaner way to go about
things. Cheers,
Adam
On Apr 22, 2009, at 8:40 AM, Wout Mertens wrote:
> Intro
> =====
> How do you sort by reduce value? How do you join views? How do you
> get unique view results? How do you cache group key reduces?
>
> I think that with the below proposed solution all the above and more
> are possible. The general idea is to store view results and run map/
> reduce on them. There's been some discussions about this but they
> went nowhere. I've been thinking about this issue a bit and I think
> it can be done.
>
> I'd like to call this feature a Review DB.
>
> Use cases
> =========
> - Suppose you want to know what tags are most popular on your blog.
> Simply get:
>
> http://couchdb/db/_design/myblog/_review/tags_by_count/_view/sort_by_value
>
> Where tags_by_count is a Review DB that gets input from the tagcount
> view and then runs the sort_by_value view on it, a map() function
> that simply emits (value,key).
>
> Likewise, show pages in order of popularity, whereby user can vote
> up (+1) or down (-1):
>
> http://couchdb/db/_design/mywiki/_review/pagevotes/_view/
> sort_by_value
>
> - Given documents with attributes title, date and tags. You'd like
> to know the minimum value of date and a breakdown by count for tags,
> for every title. Normally you'd use 2 map+reduce views,
> minimum_date_by_title and tagcount_by_title, which you would then
> query separately. With a Review DB, you can let both views insert
> their results in the database and then run a view that combines the
> results in one view:
>
> http://couchdb/db/_design/mybookstore/_review/mybooks/_view/aggregate_book_data
>
> - This is not a way to run an on-the-fly map/reduce on a subset of a
> view, like if you want to find the median popularity score of
> restaurants with "Tony" in their name that are close to you.
>
> Implementation
> ==============
> A Review DB is a hidden database maintained by CouchDB with these
> fields:
> - _id of document is the string representation of the key
> - "key" is the key of the incoming view row (unique)
> - "value" is the value of the incoming view row
>
> I hope that this is sufficiently like a normal view that it can be
> stored as a normal view. _id is just there to make it doc-compliant,
> it would be much better if "key" were the actual key.
>
> A Review DB is defined in a design document like normal views. Each
> review is an entry in the "reviews" hash, and has a "incoming_views"
> array that lists all the views that should insert results in the
> review db plus the group level, as well as a normal "views" hash for
> further map/reduce of the review db (and perhaps another "reviews"
> hash for further result processing?).
>
> Maintaining a database of results means that results have to be
> updated or even removed when documents change. I tried to make this
> work (in theory) for map-only views, but the resulting requirements
> are quite messy. You either need to cache the previous results of a
> view for each document, or you have to have an old version of the
> document available to regenerate those results.
>
> Therefore, a Review DB only accepts results from one or more map
> +reduce views. You define beforehand what the group_level of the
> keys is that will be inserted.
>
> Furthermore, a Review DB disallows (but doesn't enforce) having 2
> views that generate the same keys. Otherwise, refcounting would need
> to be used and while that's not difficult, I think there's limited
> value in allowing this.
>
> The Review DB needs updating every time the reduction for a group
> key of one of the participating views gets updated. Even though a map
> +reduce view has unique keys, we need a refcount since we have
> multiple views. Whoever got to insert its value last wins.
>
> There is a slight complication: group key values are calculated on-
> the-fly from the view result b-tree. So whenever a reduce call
> results in a new value for a b-tree node, AND that node is the upper
> node of a subtree that is completely part of a group key, that group
> key needs to be marked for recalculation.
>
> Likewise, if deletion/addition of a b-tree node results in the
> removal/creation of the sole upper node of a group key subtree, that
> group key needs to be marked for removal/addition.
>
> This is the algorithm:
> - When a reducing view gets updated, and it is part of a Review DB,
> use the 2 paragraphs above to keep a list of group keys that need
> handling
> - After updating the reduce() results, for each of the marked group
> keys:
> - If a group key gets removed:
> - look up doc with key=group key in review db. If exists:
> - delete doc
> - If a group key gets added:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
> - If a group key gets updated:
> - look up doc with key=group key in review db. If exists:
> - set doc.value to the row value
> - else
> - create doc with id=group key in string form, key=group key,
> value=value
>
> As you can see, this is something CouchDB should do since it knows
> when it's updating group key reduction values and it knows if this
> was an delete, update or addition.
>
> View updates are done when the view is called; Review updates are
> done at this time as well. Views on Review DBs are done when they
> are called.
>
> Summary
> =======
> Review DBs are a sort of view index that CouchDB can maintain with
> little overhead. It caches group key results and allows chained map
> +reduce calculations using mostly existing frameworks.
>
> I think this would be a very useful feature for CouchDB to have.
> There are regularly requests for storing view results in a database
> for post-processing on the mailing lists.
>
> I'm not saying this is a trivial change but it doesn't seem
> technically impossible to me either. (unless I missed something
> again; this is the 5th iteration of this proposal. Anyway I know *I*
> wouldn't be able to code this :-) )
>
> What do you think, oh dear devs?
>
> Wout.