Realm Many to Many Query (Inverse too!) - ios

My app's db has a many to many relationship between a Feed object and a Tweet object. This is to keep track of which feeds every tweet belongs in. If you're familiar with Twitter, imagine the main feed, a list feed, a user profile feed, etc.
How can I make a query using an NSPredicate to get a list of Tweets that exist in a specific Feed (and, inversely, get a list of Feeds that a Tweet exists in)? It seems that queries on inverse relationships does not work in Realm, so what are my options?

If I understand your question correctly this part of the documentation should be helpful:
Inverse Relationships Links are unidirectional. So if a to-many
property Person.dogs links to a Dog instance and a to-one property
Dog.owner links to Person, these links are independent from one
another. Appending a Dog to a Person instance’s dogs property, doesn’t
automatically set the dog’s owner property to this Person. Because
manually synchronizing pairs of relationships is error prone, complex
and duplicates information, Realm exposes an API to retrieve backlinks
described below.
With inverse relationships, you can obtain all objects linking to a
given object through a specific property. For example, calling
Object().linkingObjects(_:forProperty:) on a Dog instance will return
all objects of the specified class linking to the calling instance
with the specified property.
I guess you can do something like:
//assuming your Tweet object has a property like "let feeds = List<Feed>()"
someTweet.linkingObjects(Feed.self, forProperty: "feeds") //should return feeds your Tweet is in
But still I don't think I understand your question clearly. From my point of view your first requirement:
get a list of Tweets that exist in a specific Feed
should have a straightforward solution such as having a property in your Feed object like:
let tweets = List<Tweet>()
I wish you can clarify your situation further.

I wonder if it's possible to simplify the model a bit so many-to-many isn't necessary.
My understanding of Twitter is that tweets aren't 'owned' by any feeds. They simply exist on the platform, and are referenced by any number of feeds, but don't actually belong to any specific feed.
So a model setup like this should be appropriate:
class Tweet : Object {
}
class Feed : Object {
let tweets = List<Tweet>()
}
You can do a reverse lookup on a Tweet to see if there are any feeds in which it is currently visible, and you can simply use the tweets property of Feed objects to see which tweets they're displaying
Since the linkingObjects reverse lookup method of Realm simply returns a standard Swift Array, if you did want to filter that further, you could just use the system APIs (like filter or map) to refine it further.
Otherwise, if you really do want to be able to use Realm's NSPredicate filtering system both ways, then, as messy as it is, you would need to manually have each model linking to a list of the other:
class Tweet : Object {
let feeds = List<Feed>()
}
class Feed : Object {
let tweets = List<Tweet>()
}
While it's not recommended (Since it adds additional work), it's not disallowed.
Good luck!

Related

Is there a way to access properties of an x-coredata:// object returned from an NSFetchRequest?

TL;DR: Is there a way to programmatically read/recall (NOT write!) an instance of a Core Data entity using the p-numbered "serial number" that's tacked on to the instance's x-coredata:// identifier? Is this a good/bad idea?
I'm using a method similar to the following to retrieve the instances of an Entity called from a Core Data data store:
var managedContext: NSManagedObjectContext!
let fetchRequest : NSFetchRequest<TrackInfo> = TrackInfo.fetchRequest()
fetchResults = try! managedContext.fetch(fetchRequest)
for (i, _) in Global.Vars.numberOfTrackButtons! {
let workingTrackInfo = fetchResults.randomElement()!
print("current track is: \(workingTrackInfo)")
The list of tracks comes back in fetchResults as an array, and I can select one of them at random (fetchResults.randomElement()). From there, I can examine the details of that one item by coercing it to a string and displaying it in the console (the print statement). I don't list the code below, but using workingTrackInfo I am able to see that instance, read its properties into other variables, etc.
In the console, iOS/Xcode lists the selected item as follows:
current track is: <MyProjectName.TrackInfo: 0x60000374c2d0> (entity:
TrackInfo; id: 0xa7dc809ab862d89d
<x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22>;
data: <fault>)
The line beginning with x-coredata: got my attention. It's formatted like a URL, consisting of what I assume is a UUID for the specific Core Data store associated with the current build of the app (i.e. not a stable address that you could hardcode; you'd need to programmatically look up the Core Data store, similar to the functions we use for programmatically locating the Documents Folder, App Bundle, etc.) The third item is the name of the Entity in my Core Data model -- easy enough.
But that last number is what I'm curious about. From examining the SQLite database associated with this data store, it appears to be a sort of "instance serial number" associated with the Z_PK field in the data model.
I AM NOT interested in trying to circumvent Core Data's normal mechanisms to modify the contents of a managed object. Apple is very clear about that being a bad idea.
What I AM interested in is whether it's possible to address a particular Core Data instance using this "serial number".**
In my application, where I'm randomly selecting one track out of what might be hundreds or even thousands of tracks, I'd be interested in, among other things, the ability to select a single track on the basis of that p-number serial, where I simply ask for an individual instance by generating a random p-number, tack it on to a x-coredata:// statement formatted like the one listed above, and loading the result (on a read-only basis!) into a variable for further use elsewhere in the app.
For testing purposes, I've tried simply hardcoding x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22 as a URL, but XCode doesn't seem to like it. Is there some other data Type (e.g. an NSManagedObject?) that allows you to set an x-coredata:// "URL" as its contents?
QUESTIONS: Has anyone done anything like this; are there any memory/threading considerations why grabbing instance names in this manner is a bad idea (I'm an iOS/Core Data noob, so I don't know what I don't know; please humor me!); what would the syntax/method for these types of statements be?
Thanks!
You are quite close.
x-coredata://2B5DDCDB-0F2C-4CDF-A7B9-D4C43785FDE7/TrackInfo/p22
is the uriRepresentation() of the NSManagedObjectID of the record.
You get this URL from an NSManagedObject with
let workingTrackInfo = fetchResults.randomElement()!
let objectIDURL = workingTrackInfo.objectID.uriRepresentation()
With this URL you can get the managed Object ID from the NSPersistentStoreCoordinator and the coordinator from the managed object context.
Then call object(with: on the context to get the object.
let persistentStoreCoordinator = managedContext.persistentStoreCoordinator!
if let objectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: objectIDURL) {
let object = managedContext.object(with: objectID) as! TrackInfo
print(object)
}

Any way to get category ID from a SearchResult object?

I'm trying to get a category ID from a SearchResult object. I looked through the docs, didn't see anything that returns categoryID nor did I see anything that returns a YouTube.Videos.List object.
Here's my simple code. Just trying to loop and get category IDs for all.
for (SearchResult video:searchResultList)
{
//Get category ID of video
}
You'll have to make a second call to get the category IDs, because (as you note) it's only included on the snippet of a videos.list object. I'm thinking the least painful way to do this would be to pass in the IDs as a comma-delimited string to the "ID" parameter of the videos.list call. It's a bit unfortunate, because it makes it difficult to do things such as, say, present categorically sorted search results to your user, but for now it's the only option.

Core data parent and child entity

I have a small problem in Parent and child entity in core data model. My core data model has a Person entity which can have many googleplus account or twitter account linked to it. Since these two entities; GooglePlus and Twitter are somehow similar with the properties they have, I thought of creating an abstract entity called SocialConnection. Now, my Person entity has to-many relationship to SocialConnection entity which inturn is parent to both GooglePlus and Twitter.
Person <----->> SocialIdentifier ---- Child ---- GooglePlus
|
|
Child
|
|
Twitter
My graphical model is shown in the figure below.
Now, I have a situation here. Person has a property called socialConnections which will fetch all the connections regardless of if it is Twitter object or GooglePlus object. But, I would like to fetch just twitter sometime and GooglePlus sometimes. How would I do it.
I think one idea would be to have a backward relationship from SocialIdentifier pointing to the Person entity and let Person create forward relationship with each of the child.
It would be represented as;
Person <----------- SocialIdentifier -------- Child --------GooglePlus----->> Person
|
|
Child
|
|
Person <<------- Twitter
Graphical representation;
But, even with this representation, I am facing some problem. I could get googles and twitters from Person model but it has no reference to the SocialConnections ie. all the connections.
And, I feel this is redundant as the first one simplifies the design a lot. I am sure a little tweak to the first one would yield the desired one but I could not figure it out.
How could I fetch twitters or googles directly from the first model or is it possible to fetch all the connections from Person model without using predicate ofcourse. I am sure there are some pretty neat solution to this.
I would like to thank you for your help and suggestions beforehand.
I wouldn't set up new relationships for this. There are a couple of different approaches you could use.
Fetched property
Give Person a new fetched property named twitters. Set the destination entity to Twitter and the fetch predicate to person==$FETCH_SOURCE. Then just ask for the value of twitters when you need it. (Yeah, you said no predicate for some reason, but honestly, it's the right tool for the job here).
The advantage here is that you don't have to maintain a new relationship. If you add a new Twitter account to socialConnections, it automatically becomes part of twitters.
Filtering the set
If there won't be a large number of socialConnections then it might be easier to just filter it in code instead of getting Core Data to do it. Get every socialConnections object and filter the set, something like:
NSEntityDescription *twitterEntity = [NSEntityDescription entityForName:#"Twitter" inManagedObjectContext:[self managedObjectContext]];
NSSet *twitters = [[person valueForKey:#"socialConnections"] objectsPassingTest:^BOOL(id obj, BOOL *stop) {
return [[obj entity] isKindOfEntity:twitterEntity];
}];
That runs through every social connection and gets just the Twitter accounts.

Cross-Store weak relationship with Fetched Properties?

I would like to separate my reference data from my user data in my Core Data model to simplify future updates of my app (and because, I plan to store the database on the cloud and there is no need to store reference data on the cloud as this is part of my application). Therefore, I've been looking for a while for a way to code a cross-store relationship using fetched properties. I have not found any example implementations of this.
I have a Core Data model using 2 configurations :
data model config 1 : UserData (entities relative to user)
data model config 2 : ReferenceData (entities relative to application itself)
I set up 2 different SQLite persistent stores for both config.
UserData config (and store) contains entity "User"
ReferenceData config (and store) contains entities "Type" and "Item".
I would like to create two single-way weak relationships as below :
A "User" has a unique "Type"
A "User" has many "Items"
Here are my questions :
How do I set up my properties?
Do I need 2 properties for each relation (one for storing Unique ID and another to access my fetched results)?
Could this weak relationship be ordered?
Could someone give me an example implementation of this?
As a follow-on to Marcus' answer:
Looking through the forums and docs, I read that I should use the URI Representation of my entity instance instead of objectID. What is the reason behind this?
// Get the URI of my object to reference
NSURL * uriObjectB [[myObjectB objectID] URIRepresentation];
Next, I wonder, how do I store my object B URI (NSURL) in my parent object A as a weak relationship? What attribute type should I use? How do I convert this? I heard about archive... ?
Then, later I should retrieve the managed object the same way (by unconvert/unarchive the URIRepresentation) and get Object from URI
// Get the Object ID from the URI
NSManagedObjectID* idObjectB = [storeCoordinator managedObjectIDForURIRepresentation:[[myManagedObject objectID] URIRepresentation]];
// Get the Managed Object for the idOjectB ...
And last but not least, shouId I declare two properties in my entity A, one for persisting of URI needs and another for retrieving direclty object B?
NSURL * uriObjectB [objectA uriObjectB];
ObjectB * myObjectB = [objectA objectB];
As you can read, I really miss some simple example to implement thes weak relationships ! I would really appreciate some help.
Splitting the data is the right answer by far. Reference data should not be synced with the cloud, especially since iCloud has soft caps on what it will allow an application to sync and store in documents.
To create soft references across to stores (they do not need to be SQLite but it is a good idea for general app performance) you will need to have some kind of unique key that can be referenced from the other side; a good old fashioned foreign key.
From there you can create a fetched property in the model to reference the entity.
While this relationship cannot be ordered directly you can create order via a sort index or if it has a logical sort then you can sort it once you retrieve the data (I use convenience methods for this that return a sorted array instead of a set).
I can build up an example but you really are on the right track. The only fun part is migration. When you detect a migration situation you will need to migrate each store independently before you build up your core data stack. It sounds tricky but it really is not that hard to accomplish.
Example
Imagine you have a UserBar entity in the user store and a RefBar entity in the reference store. The RefBar will then have a fetchedProperty "relationship" with a UserBar thereby creating a ToOne relationship.
UserBar
----------
refBarID : NSInteger
RefBar
--------
identifier : NSInteger
You can then create a fetched property on the RefBar entity in the modeler with a predicate of:
$FETCHED_PROPERTY.refBarID == identifier
Lets name that predicate "userBarFetched"
Now that will return an array so we want to add a convenience method to the RefBar
#class UserBar;
#interface RefBar : NSManagedObject
- (UserBar*)userBar;
#end
#implementation RefBar
- (UserBar*)userBar
{
NSArray *fetched = [self valueForKey:#"userBarFetched"];
return [fetched lastObject];
}
#end
To create a ToMany is the same except your convenience method would return an array and you would sort the array before returning it.
As Heath Borders mentioned, it is possible to add a sort to the NSFetchedProperty if you want but you must do it in code. Personally I have always found it wasteful and don't use that feature. It might be more useful if I could set the sort in the modeler.
Using the ObjectID
I do not recommend using the ObjectID or the URIRepresentation. The ObjectID (and therefore the URIRepresentation of that ObjectID) can and will change. Whenever you migrate a database that value will change. You are far better off creating a non-changing GUID.
The weak relationship
You only need a single value on the M side of the relationship and that stores the foreign identifier. In your object subclass you only need to implement accessors that retrieve the object (or objects).
I would go with just one store.
For storing stuff in the cloud, you will anyway have to serialize the data, either as JSON or SQL statements, or whatever scheme you prefer.
You will need a local copy of the data on the user's device, so he can access it quickly and offline. The cloud store can have only the user entity, while the local store (part of the app) can also have the reference entity.
I have a similar project with a huge reference store (20000 records) with geographic information, and user generated content ("posts"). I use a single store. When I ship the app, the "posts" entity is also defined but empty. When I update the data model I simply re-generate the whole reference store before shipping.
I see absolutely no reason to go for a cross store solution here.

Lazy fetching of objects using FindAllBy , for the first time

When I use criteria queries, the result contains array list of lazy initialized objects. that is, the list has values with handler org.codehaus.groovy.grails.orm.hibernate.proxy.GroovyAwareJavassistLazyInitializer.
This prevent me from doing any array operation (minus, remove etc) in it. When I use, GORM methods, I get array list of actual object types. How can I get the actual objects in criteria query?
The code is listed below.
availableTypes = Type.withCriteria() {
'in'("roleFrom", from)
'in'("roleTo", to)
}
availableTypes (an array list) has one value , but not actual object but value with a handler of GroovyAwareJavassistLazyInitializer
availableTypes (an array list) has values with type Type
availableTypes = Type.findByRoleFrom(from)
---------- Update ----------
I did further troubleshooting, and this is what I found. Probably the above description might be misleading, but I kept it in case it helps.
When using findAllBy for the first time, I get proxy objects rather than the actual instance. Then, I invoke the method through an ajax call, the actual instance is loaded (anything to do with cache loading??). When I refresh the page, it again loads the proxy
def typeFrom = Type.findAllByParty(partyFrom)
there is another use of findAllBy in the same method, which always returns actual instances.
def relFrom = Relation.findAllByParty(partyFrom)
When compared the two classes, the attribute 'party' of class Roles is part of a 1-m relation. like
class Role {
RoleType roleType
LocalDate validFrom
LocalDate validTo
static belongsTo = [party : Party ]
...
}
I know if I do statement like Party.findAll(), the role instances would be proxy till they access. But, when using gorm directly on the class (Role), why I am getting the proxy objects ???
thanks for the help.
thanks.
Turns out are a couple of possible solutions which I came across but didn't try, such as
Overloading the equals method so that the proxy and the domain
object use a primary key instead of the hashCode for equality
Using a join query so that you get actual instances back and not proxies
GrailsHibernateUtil.unwrapProxy(o)
HibernateProxyHelper.getClassWithoutInitializingProxy(object)
One solution that worked for me was to specify lazy loading to be false in the domain object mapping.
History of this problem seems to be discussed here: GRAILS-4614
See also: eager load

Resources