Controlling _id for document connected to CBLModel

163 views
Skip to first unread message

Daniel Ericsson

unread,
Dec 10, 2013, 10:54:09 AM12/10/13
to mobile-c...@googlegroups.com
What options are there, if any, for controlling what the _id for a
document connected to a CBLModel is?

Creating a new CBLModel not connected to a database and setting it's
_id and then saving – or creating a new CBLDocument and then using
-putProperties to set it's _id both result in:

"WARNING*** : Trying to PUT wrong _id to CBLDocument" for me.

thanks for any input,
-- Daniel

Jens Alfke

unread,
Dec 10, 2013, 11:23:12 AM12/10/13
to mobile-c...@googlegroups.com

On Dec 10, 2013, at 7:54 AM, Daniel Ericsson <monow...@gmail.com> wrote:

What options are there, if any, for controlling what the _id for a
document connected to a CBLModel is?

Just override idForNewDocumentInDatabase.

/** The document ID to use when creating a new document.
    Default is nil, which means to assign no ID (the server will assign one). */
- (NSString*) idForNewDocumentInDatabase: (CBLDatabase*)db              __attribute__((nonnull));

Alan McKean

unread,
Feb 10, 2014, 3:48:50 PM2/10/14
to mobile-c...@googlegroups.com
Doesn't idForNewDocumentInDatabase: get called by CBL? I overrode it in my CBLModel subclass but it never gets called.

Jens Alfke

unread,
Feb 10, 2014, 4:34:32 PM2/10/14
to mobile-c...@googlegroups.com

On Feb 10, 2014, at 12:48 PM, Alan McKean <alanm...@me.com> wrote:

Doesn't idForNewDocumentInDatabase: get called by CBL? I overrode it in my CBLModel subclass but it never gets called.

It gets called by -setDatabase:, which is called by -initWithNewDocumentInDatabase:.

How are you creating your model objects?

—Jens

Alan McKean

unread,
Feb 10, 2014, 4:37:43 PM2/10/14
to mobile-c...@googlegroups.com
app.database createDocument. I'll change it.

Alan McKean

unread,
Feb 10, 2014, 4:40:58 PM2/10/14
to mobile-c...@googlegroups.com
Oops. That's creating the CBLDocument, not the CBLModel.

Alan McKean

unread,
Feb 10, 2014, 4:45:27 PM2/10/14
to mobile-c...@googlegroups.com
It seems I have things backward. Here is an example of how I am creating the CBLModels:

+ (Claim *) save:(NSMutableDictionary *)dictionary {

  NSLog(@"%@", dictionary);

  BOOL encrypted = [dictionary objectForKey:@"data"] != nil;

  CBLDocument *doc = [self saveCBLDocument:dictionary encrypted:encrypted];

  return [Claim modelForDocument:doc];

}


It looks like I will have to create the CBLModel before I create the CBLDocument so that it will pick up the overridden version of idForNewDocumentInDatabase:. Yes?

Alan McKean

unread,
Feb 10, 2014, 5:00:09 PM2/10/14
to mobile-c...@googlegroups.com
I'm confused. It looks to me like I need to create the CBLDocument before I create the CBLModel? Here are the code snippets I am using:

// doc is dictionary downloaded from server at launch (in OTSDownloader).
1) Claim *claim = [Claim save:doc mutableCopy];

// Each CBLModel subclass implements save (e.g., Claim):
2) + (Claim *) save:(NSDictionary *)dictionary {
  BOOL encrypted = [dictionary objectForKey:@"data"] != nil;
  CBLDocument *doc = [self saveCBLDocument:dictionary encrypted:encrypted];
  return [Claim modelForDocument:doc];
}

// OTSCBLModel superclass of Claim implements common saving behavior
3) + (CBLDocument *) saveCBLDocument:(NSMutableDictionary *)dictionary encrypted:(BOOL)encrypted {
  NSDictionary *d = dictionary;
  if(encrypted && [dictionary objectForKey:@"data"] != nil) {
    NSString *encryptionKey = [OTSKeychainHelper keychainStringFromMatchingIdentifier:PROVIDER_ENCRYPTION_KEY];
    d = [OTSCrypter decrypt:dictionary encryptionKey:encryptionKey];
  }
  OTSBillerApp *app = (OTSBillerApp *)[[UIApplication sharedApplication] delegate];
  CBLDocument* doc = [app.database createDocument];
  NSError *error;
  if (![doc putProperties: d error: &error]) {
    NSLog(@"Failed saving %@. Error: %@", [dictionary objectForKey:@"type"], [error localizedDescription]);
  }
  return doc;

Jens Alfke

unread,
Feb 10, 2014, 5:04:30 PM2/10/14
to mobile-c...@googlegroups.com

On Feb 10, 2014, at 1:45 PM, Alan McKean <alanm...@me.com> wrote:

It looks like I will have to create the CBLModel before I create the CBLDocument so that it will pick up the overridden version of idForNewDocumentInDatabase:. Yes?

If you create the document first, it's even easier. Just call [db documentWithID:] to tell it what ID you want. Then create a model from that document.

—Jens

Alan McKean

unread,
Feb 10, 2014, 5:28:00 PM2/10/14
to mobile-c...@googlegroups.com
How do I preserve the _rev that I get in the dictionary from the server? I need to preserve both for updating the server objects.

Alan McKean

unread,
Feb 10, 2014, 6:06:54 PM2/10/14
to mobile-c...@googlegroups.com
Creating doc with [database documentWIthId::”....”]. All good. But if I keep the _rev in the dictionary when I putProperties, it fails. Take the _rev out, and it succeeds but sets the _rev to 1-xxxxxx. Is there a way to preserve the _rev property I get from the server?

Like this:
   // d is the dictionary downloaded from the server ... this succeeds but creates the wrong _rev

  CBLDocument* doc = [app.database documentWithID:couchId];

  NSError *error;

  // if I take these two lines out, the putProperties fails

  [d removeObjectForKey:@"_id"];

  [d removeObjectForKey:@"_rev"];

  if (![doc putProperties: d error: &error]) {

    NSLog(@"Failed saving %@. Error: %@", [d objectForKey:@"type"], [error localizedDescription]);

  }

Jens Alfke

unread,
Feb 10, 2014, 6:50:53 PM2/10/14
to mobile-c...@googlegroups.com

On Feb 10, 2014, at 3:06 PM, Alan McKean <alanm...@me.com> wrote:

> Creating doc with [database documentWIthId::”....”]. All good. But if I keep the _rev in the dictionary when I putProperties, it fails. Take the _rev out, and it succeeds but sets the _rev to 1-xxxxxx. Is there a way to preserve the _rev property I get from the server?

What do you mean by "the _rev property I get from the server"? Are you trying to transfer documents without using the replicator, somehow?

The "_rev_ property is created and managed internally. Ordinarily you have no control over it and should just ignore it.

—Jens

Alan McKean

unread,
Feb 10, 2014, 8:17:06 PM2/10/14
to mobile-c...@googlegroups.com
Yes, I am converting my Core Data app to CBL. I have a downloader class that gets my CouchDB data via a passthrough server that checks credentials. I am also HIPAA-compliant so it 'requires' a separation of the db server from the public so I can't expose CouchDB to the public. I have a route in my Node.js server that gives me the changes from the private CouchDB server . I track the 'lastSequenceNumber' in my app and request changes since that on launch. So I get the CouchDB data from the Node.js server and want to put it into CBL. So I have to preserve the _id and _rev properties of each document so that I can update them back on the server.

If I cannot save a document with the _rev matching my incoming data, CBL won't work for my use case. Unless I use replication and design the routes on my Node.js server to match the replication protocol. But I assume I would have to mimic ALL of the protocol, including the handshaking, etc. Is this correct?

Jens Alfke

unread,
Feb 10, 2014, 8:37:54 PM2/10/14
to mobile-c...@googlegroups.com
On Feb 10, 2014, at 5:17 PM, Alan McKean <alanm...@me.com> wrote:

I am also HIPAA-compliant so it 'requires' a separation of the db server from the public so I can't expose CouchDB to the public.

I'm no expert on HIPAA, but we've had other developers here working on compliant apps, and none of them have brought this up as an issue. 

To me it seems to make no effective difference in security/privacy whether the app connects directly to CouchDB or gets the data through some intermediate server. (As a reducto ad absurdum, could you put an HTTP proxy in between and call that a "separation" of the db server?)

One could argue that CouchDB implements both a database and an app server, with the database layer kept protected by the access controls in the app server layer.

I have a route in my Node.js server that gives me the changes from the private CouchDB server . I track the 'lastSequenceNumber' in my app and request changes since that on launch. So I get the CouchDB data from the Node.js server and want to put it into CBL. So I have to preserve the _id and _rev properties of each document so that I can update them back on the server.

In effect you're re-implementing the replicator. That's not a task I would wish on anyone; it's not hard to get it mostly working, but there is a long tail of issues that have kept me tweaking it intermittently for two years. :-p

The CBL replicator uses an internal API that allows it to take already-existing revisions (with _rev already assigned) and insert them into the database. That API isn't exposed publicly and I'd be hesitant to offer any support for it. It requires you do some extra work like obtaining the revision history of the document and downloading all necessary attachments.

—Jens

Alan McKean

unread,
Feb 10, 2014, 8:46:31 PM2/10/14
to mobile-c...@googlegroups.com
Thanks for all of your help. I am investigating using replication.
Reply all
Reply to author
Forward
0 new messages