ANN: iOS CBLModel support for inverse to-many relations

107 views
Skip to first unread message

Jens Alfke

unread,
Feb 25, 2015, 11:14:03 PM2/25/15
to mobile-c...@googlegroups.com
New feature available on the iOS/Mac master branch, as described in issue #606:

Added CBLModel support for computed inverse-relation properties (a typical ORM feature, also found in Core Data.)

For example, if the Album class has a relation @property Artist* artist (i.e. the JSON property value is the docID of a document corresponding to an Artist model), then it should be possible to define a property on Artist like @property (readonly) NSArray* albums, whose value is an array of the Albumobjects whose artist property points to the receiver.

Behind the scenes, there's no JSON property backing this. Its value is computed on demand by querying a view that indexes the artist property of every album document. (If this turns out to be too slow we can cache the value until the view index changes.) 

Caveats:

  • The property value is recomputed on every call
  • These properties are not KV-observable (that would be great to add later, but it would require something like a LiveQuery, and would have to be carefully optimized to be scaleable.)

To define a property like this, you have to do three things:
1. Define the [dynamic] property itself, with type NSArray*.
2. Implement a class method +propertyItemClass that returns the class that has the inverse relation, i.e. the class of the items in the array to be returned.
3. Implement a class method +propertyInverseRelation that returns a string naming the relation property in the other class that should be queried.

In the example:

@interface Artist
    @property (readonly) NSArray* albums;
    ...

@implementation Artist
    ...
    @dynamic albums;
    + (Class) albumsItemClass { return [Album class]; }
    + (NSString*) albumsInverseRelation { return @"artist"; }
    ...

Todd Freese

unread,
Feb 26, 2015, 11:07:42 AM2/26/15
to mobile-c...@googlegroups.com
Very nice!

Todd

Christoph Berlin

unread,
Feb 27, 2015, 12:46:28 PM2/27/15
to mobile-c...@googlegroups.com
Hi Jens, very nice. Quick question: is or will this feature be supported on the Sync Gateway to allowing us to determine channel access rules? Based on your scenario with albums and records it may not make sense but if you apply the following scenario it could come in handy:

1) Each user has a user profile (document)
2) Account can have multiple users as "members", each represented by a user profile document

With this inverse look up I could easily determine access for user profile documents (actual users are a different story) whether the user profiles should be added to the account channel.

Thanks Christoph

Jens Alfke

unread,
Feb 27, 2015, 1:02:56 PM2/27/15
to mobile-c...@googlegroups.com

On Feb 27, 2015, at 9:46 AM, Christoph Berlin <hoptoawe...@gmail.com> wrote:

Hi Jens, very nice. Quick question: is or will this feature be supported on the Sync Gateway to allowing us to determine channel access rules?

Inverse relations aren’t useable in a sync function (or a map function for that matter) because their value depends on other documents; so the value of the relation can change without the document itself changing. But the sync and map functions need to be more ‘pure’ functions that don’t depend on state outside the document.

—Jens

so...@bravebits.vn

unread,
Apr 10, 2016, 10:16:43 PM4/10/16
to Couchbase Mobile
Hi Jens,

I have a question that already posted on Github but it's not a right place for ask question, show i move it to this:

Hi,

I'm finding the way to find inverse a to-many relation. Just read #606 and also Wiki about Inverse Relation but seem that it doesn't fits my situation.

I have Customer and CustomerGroup class. Customer class have a array of CustomerGroup. How can i get all Customer that belong to CustomerGroup.

```
class Customer: BaseCBLObject {
    @NSManaged
    var groups: [CustomerGroup]

    class func groupsItemClass() -> AnyClass {
        return CustomerGroup.self
    }
}
```
```
class CustomerGroup: BaseCBLObject {
    @NSManaged
    var allCustomers: [Customer]

    class func allCustomersItemForClass() -> AnyClass {
        return Customer.self
    }
    
    class func allCustomersInverseRelation() -> String {
        return "groups"
    }
}
```

Currently if i have CustomerGroups instance `group` then get all customer by call `group.allCustomers` or explicit call `group.findInverseRelation:fromClass` it will return empty array.

Thanks

--------
* Version:   1.2
* Client OS: 9.x
* Server:    4.0

Jens Alfke

unread,
Apr 11, 2016, 12:08:38 PM4/11/16
to mobile-c...@googlegroups.com
You’ve got a bidirectional many-to-many relation. That’s not supported yet.

Specifically, the inverse-relation code in CBLModel assumes the other class (the one with the forwards relation) has a singular property (a string) that points to the destination class, not a to-many property (an array).

It wouldn’t be terribly difficult to add support for this; it just hasn’t been done. I’ll reopen the issue you opened on Github, since it turns out to be a feature request not just a question :)

—Jens

so...@bravebits.vn

unread,
Apr 12, 2016, 12:17:48 AM4/12/16
to Couchbase Mobile
Thanks, currently i had to change to use single property and looking forward this feature will be released.

Thanks.
Reply all
Reply to author
Forward
0 new messages