CBL iOS - getting information for view for 'attached' models

36 views
Skip to first unread message

Mark

unread,
Jul 11, 2014, 8:03:02 PM7/11/14
to mobile-c...@googlegroups.com
I have a CBLModel that has a property of another CBLModel. Similar to:

@interface CommentModel : CBLModel
@property NSString *comment;
@property UserModel *user;
@end

@interface UserModel : CBLModel
@property NSString *username;
@end


This all works magnificently within the model instance. However, I'm trying to build a view of the comments + the user name. Like so:

CBLView* allCommentsView = [db viewNamed:allComments];
[allCommentsView setMapBlock: MAPBLOCK({
id type = [doc objectForKey: @"type"];
if ([type isEqualToString:@"comment"] ) {
NSString *comment = [doc objectForKey:@"comment"];
UserModel *user = [doc objectForKey:@"user"];
NSString *name = user.username;
emit(@[comment, name], nil);
}
}) version: @"1"];


But this doesn't work. The [doc objectForKey:@"user"] returns the internal id, not the usermodel. This makes sense if you understand that you are being passed the dictionary of the document properties, but it's a problem for my needs. Reading the docs, I shouldn't reach out into the database to look up information about the user. Is there any way to do this without looking it up when I read the related query?

Thanks!

Jens Alfke

unread,
Jul 12, 2014, 1:53:45 AM7/12/14
to mobile-c...@googlegroups.com
On Jul 11, 2014, at 5:02 PM, Mark <mark...@gmail.com> wrote:

But this doesn't work. The [doc objectForKey:@"user"] returns the internal id, not the usermodel. This makes sense if you understand that you are being passed the dictionary of the document properties, but it's a problem for my needs.

Right. A document is not a model, it’s a lower-level entity.

Reading the docs, I shouldn't reach out into the database to look up information about the user.

Yes. You absolutely cannot access external data, including another document, in a map function. The index has to be based on nothing but the contents of the document being indexed. (This is true of any kind of database.)

I’m not sure what your view is trying to do. It looks like you’re emitting key/value pairs where the key is the text of the comment, but that doesn’t make sense as a key. Can you describe what your goal is?

—Jens


Mark

unread,
Jul 12, 2014, 1:42:22 PM7/12/14
to mobile-c...@googlegroups.com
> I’m not sure what your view is trying to do. It looks like you’re emitting key/value pairs where the key is the text of the comment, but that doesn’t make sense as a key. Can you describe what your goal is?

Sure. I'm using the array of keys for a table view. (In the actual app, there are a few more fields I export). I use a related CQLLiveQuery in a nstableviewdatasource. I just pull in the keys for the items in the tableviewcell instead of having to load the model for every row. Similar to:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
CBLQueryRow *thisRow = [self.queryEnumerator rowAtIndex:row];
NSString *text = [thisRow keyAtIndex:0];
id newView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
id textView = [newView viewWithTag:3002];
[textView setStringValue:text];

}
It may be a premature optimization, but it seemed more direct. Would you recommend another solution?

Thanks.

Jens Alfke

unread,
Jul 15, 2014, 1:18:52 PM7/15/14
to mobile-c...@googlegroups.com

On Jul 12, 2014, at 10:42 AM, Mark <mark...@gmail.com> wrote:

Sure. I'm using the array of keys for a table view. (In the actual app, there are a few more fields I export). I use a related CQLLiveQuery in a nstableviewdatasource. I just pull in the keys for the items in the tableviewcell instead of having to load the model for every row.

It sounds like you want to display a list of the comments on a post, then? In that case you don’t want the text of the post to be the key, since the key is what the view is sorted by. It doesn’t sound useful to get a list of all the comments in the entire database in alphabetical order :) Instead you probably want the view to have a primary sort by post ID (so you can use a range to get the comments on a single post) and a secondary sort by date (so you can show them in chronological order).

This section from JChris’s old CouchDB book talks about how to do pretty much exactly that.

As for getting the user name — there are two choices:
  • Put the user name (along with the ID) in the comment document. Then it can be emitted into the view. The downside is that if users can change their names, you have to go through every comment and update the name.
  • Emit only the user ID in the view value, then look up the user name while displaying, i.e. get the document with that ID and get its “name” property. This shouldn’t cause much DB activity, if there aren’t a huge number of users, because CBL caches recently-accessed documents in memory. (Really, it’s exactly what a relational database would be doing when you use a JOIN in the query.) If it does turn out to be an issue, you can construct a view that maps user ID to user name, and keep a live query of it; that will end up caching the mapping and keeping it up to date.

—Jens

Mark

unread,
Jul 19, 2014, 2:28:35 PM7/19/14
to mobile-c...@googlegroups.com
Hi Jens,

Thanks for the tips.
>
>> Sure. I'm using the array of keys for a table view. (In the actual app, there are a few more fields I export). I use a related CQLLiveQuery in a nstableviewdatasource. I just pull in the keys for the items in the tableviewcell instead of having to load the model for every row.
>
> It sounds like you want to display a list of the comments on a post, then? In that case you don’t want the text of the post to be the key, since the key is what the view is sorted by. It doesn’t sound useful to get a list of all the comments in the entire database in alphabetical order :) Instead you probably want the view to have a primary sort by post ID (so you can use a range to get the comments on a single post) and a secondary sort by date (so you can show them in chronological order).
>
> This section from JChris’s old CouchDB book talks about how to do pretty much exactly that.
Thanks. I actually realized I needed them by type, then date. I think I did it this way based on a comment in ... actually I don't remember. The examples for emit always show emitting the whole doc, I never thought to consider you could emit an array - that's where text and user would go if I had the user data.

> As for getting the user name — there are two choices:
> • Put the user name (along with the ID) in the comment document. Then it can be emitted into the view. The downside is that if users can change their names, you have to go through every comment and update the name.
> • Emit only the user ID in the view value, then look up the user name while displaying, i.e. get the document with that ID and get its “name” property. This shouldn’t cause much DB activity, if there aren’t a huge number of users, because CBL caches recently-accessed documents in memory. (Really, it’s exactly what a relational database would be doing when you use a JOIN in the query.) If it does turn out to be an issue, you can construct a view that maps user ID to user name, and keep a live query of it; that will end up caching the mapping and keeping it up to date.
I don't think storing username in the comment item is probably the way to go since the user could change their name - I don't want to have to write a rewrite username bot. I like the index idea - I'll consider that. Since the question though, I've realized I may need more data from the both the comment and user objects so it may make sense to just pull up the objects. I'll have to measure to see if it kills performance.

Thanks for your feedback.


Reply all
Reply to author
Forward
0 new messages