Can I emit unique values in my map block?

23 views
Skip to first unread message

Brendan Duddridge

unread,
Mar 23, 2016, 4:19:36 AM3/23/16
to Couchbase Mobile
Hi,

How can I emit unique values in my map block? Is there a way to do that easily or will it require keeping an array of already emitted values, ensuring that I don't emit another one with the same value? I just don't want duplicate keys in my view.

Thanks,

Brendan

Brendan Duddridge

unread,
Mar 23, 2016, 4:24:28 AM3/23/16
to Couchbase Mobile
Ok, so I tried it out and keeping track of a dictionary of the values allows me to ensure I don't emit duplicate values.

But is there a better way? Maybe a less memory intensive way?

Thanks,

Brendan

Jens Alfke

unread,
Mar 23, 2016, 11:00:53 AM3/23/16
to mobile-c...@googlegroups.com

On Mar 23, 2016, at 4:19 AM, Brendan Duddridge <bren...@gmail.com> wrote:

How can I emit unique values in my map block? Is there a way to do that easily or will it require keeping an array of already emitted values, ensuring that I don't emit another one with the same value? I just don't want duplicate keys in my view.

Duplicate values or duplicate keys?

If your map block just calls emit() once, the docID will be unique in the index. If you call emit() multiple times, you can append a different string to the docID in each emit() call, like “-1”, “-2”, etc.

But I have a feeling I’m misunderstanding your question…

—Jens

Brendan Duddridge

unread,
Mar 23, 2016, 3:57:42 PM3/23/16
to Couchbase Mobile
I'm referring to the key.

For example, I want to display a list of movie genres that's derived from a list of movies. Many movies have the same genre. But I wanted a view that contained the keys that were the unique Genre names of all the movies in the database.

Before I added the dictionary lookup, I ended up with a list of keys that were duplicates. Because as the map block reads the documents, it emits the value of the Genre field into the view as the key and if it encounters the same value more than once, I was getting duplicates. So now I just build up a dictionary that makes lookups quick so I know to emit a value or not.

But I thought maybe there was a better way.

Here's my code now:

        NSMutableDictionary *existingValues = [[NSMutableDictionary alloc] init];

       [fieldValuesView setMapBlock: MAPBLOCK({

               NSDictionary *valuesData = doc[@"values"];

               if (valuesData) {

                       NSString *value = valuesData[weakSelf.field.document.documentID];

                       if (!existingValues[value]) {

                               if ([NSString ts_isNotEmptyString:value]) {

                                       emit(value, nil);

                                       existingValues[value] = @YES;

                               }

                       }

               }

       }) reduceBlock:^id(NSArray *keys, NSArray *values, BOOL rereduce) {

               return @(keys.count);

       } version: versionNumber];



So that's how I handled it.

Jens Alfke

unread,
Mar 23, 2016, 4:45:34 PM3/23/16
to mobile-c...@googlegroups.com

On Mar 23, 2016, at 3:57 PM, Brendan Duddridge <bren...@gmail.com> wrote:

For example, I want to display a list of movie genres that's derived from a list of movies. Many movies have the same genre. But I wanted a view that contained the keys that were the unique Genre names of all the movies in the database.

That’s what the grouping feature of queries does. Set query.groupLevel=1 and the result will contain one row for every unique key. (See the docs.)

The solution you posted isn’t going to work because the map block uses mutable external state, which isn’t allowed. In this case it looks like there’d be trouble if you deleted all the docs with a specific genre, then added another one with that genre; it wouldn’t emit anything.

—Jens

Brendan Duddridge

unread,
Mar 23, 2016, 7:49:11 PM3/23/16
to Couchbase Mobile
Hi Jens,

Ah ok. That's good to know.

But then my map view table would actually contain all this duplicate data taking up space that it didn't need to?

Thanks,

Brendan

Jens Alfke

unread,
Mar 23, 2016, 10:19:00 PM3/23/16
to mobile-c...@googlegroups.com

On Mar 23, 2016, at 7:49 PM, Brendan Duddridge <bren...@gmail.com> wrote:

But then my map view table would actually contain all this duplicate data taking up space that it didn't need to?

Yeah, but often you can use the same view/index for multiple queries. For example, I wrote a little demo app a long time ago that stores your iTunes library’s metadata in a database and lets you browse it; this one view allows it to query for the Artists list, the Albums list, and track list of the selected album, and the total time duration of whatever’s selected. The query just needs to change the group level.


NSString* artist = doc[@"Artist"];
NSString* name = doc[@"Name"];
if (artist && name) {
if ([doc[@"Compilation"] boolValue]) {
artist = @"-Compilations-";
}
emit(@[artist,
doc[@"Album"] ?: [NSNull null],
doc[@"Track Number"] ?: [NSNull null],
name,
@1],
doc[@"Total Time"]);
}

(full source of the view definition here; I’m not sure where the rest of the app ended up.)

—Jens

Brendan Duddridge

unread,
Mar 24, 2016, 12:10:53 AM3/24/16
to Couchbase Mobile
Thanks for the link to the code. That's awesome! I haven't used grouping at all yet, so it's great to see it in use.

Thanks!

Brendan
Reply all
Reply to author
Forward
0 new messages