SwiftUI CoreData not in Set of relationships - swift

I am having a CoreData structure. It is working fine for me. Now I am trying to do an interesting FetchRequest, however I am struggeling to find the correct predicate.
I am having two ententies, Person and Venues. A person can be connected to many venues, but only once to the same venue. And other way around.
I have created a mapping table, which saves the person and venue as relationships. Everything is working fine here. I even implemented the inverse relationship.
In the picture left is my venue. I want to Fetch all persons based on a current venue, where there is NO Mapping created already.
My approach was to use pk_venue, which gives me a set of PersonVenueMapping. However, how can I only use the persons of that Set.
fetch.predicate = NSPredicate(format: "NOT (person IN %#)", venue!.pk_venue[HELP])
I came up with that Predicate. However, how can I access the persons of that set? I would need a array/ set of only the persons, that are connected to that current object.
Edit: Just to give you example.
Person: Max, Leon, Henry
Venue: Venue1, Venue2, Venue 3, Venue 4
PersonVenueMapping: Max <-> Venue1
Now when I select Max, I want Venue2 & Venue3 & Venue4
Thanks in advance

Your model looks very much like a normalised database type model. CoreData is not a database and so some of the requirements of modelling data are more flexible when using it. For instance many to many relationships are possible (and encouraged).
Create your model in this way...
Person
------
name: String
------
assignedVenues: Many relationship to `venue` type
Now every relationship has an inverse property, so...
Venue
------
name: String
------
managers: Many relationship to `Person` type
Now you can go from Max to his assigned venues just by doing...
let selectedPerson = // fetch Max from CoreData
let venues = selectedPerson.assignedVenues
Equally you can go from a venue to find all the people managing it...
let someVenue = // fetch venue from CoreData
let managers = someVenue.managers
To satisfy your question of finding all venues that Max is not managing you can now do a query like...
let selectedPerson = //fetch Max
let predicate = NSPredicate(format:"NONE managers == %#", selectedPerson) // Its been a long time since I did CoreData so forgive me if this syntax isnt correct but the idea is correct. :D
You can now use that predicate to get all venues where max is NOT one of the managers of the venue.
By removing the "mapping" that is required in something like SQLite etc... you can make things much simpler for yourself.

I am confused as to whether you are fetching persons with no mapping to a given venue, or venues with no mapping to a given person. I’ll assume the latter.
Your fetch request will therefore be for the entity Venue. Let’s call the selected Person selectedPerson. The following predicate should return only those Venues that have no mapping to selectedPerson:
NSPredicate(format:”SUBQUERY(pk_venue, $M, $M.person == %#).#count == 0”, selectedPerson)
or in natural language “fetch Venues for which the count of mappings ($M), with person equal to the selected person, is zero”.

Related

CoreData. Fetchrequest all entities with relations

My main Entity is called Series. Series has a one to many relationship to an entity called Rounds, connected by one-to-many (ordered), and Rounds has again an NSOrderedSet with Shots. The Shots entity has an attribute called score, which is an Integer.
What I want, is to get all the scores from the Shots entity, belonging to a specific Series.
let shots = currentSeries.rounds.shots as [Shots]
does not give me all the Shots to iterate through, due to the error messeage: "Value of type 'NSOrderedSet' has no member: 'shots'". Somehow, I need to set a predicate to the "Shots" entity which filters all the Shots that belong to a specific "Series" entity.The Series entity does not have a unique identifier, but I guess it could be possible to use the timestamp attribute to isolate a specific "Series". But again, I want all the "Shots" entities, connected to that specifi "Series".
I could seriously need som help about CoreData mining, or at least som suggestions about how to accomplish my efforts.
One way to get all the Shots for a given Series would be to fetch with a predicate:
let fetch = NSFetchRequest(entityName:"Shots")
let fetch.predicate = NSPredicate(format:"rounds.series == %#", currentSeries)
let currentSeriesShots = try! context.executeFetchRequest(fetch) as! [Shots]
(You should add proper error handling). No need to use a unique identifier - CoreData will use its own (internal) identifier for the currentSeries object to determine which Shots to return.

Selecting columns from different entities

I don't know whether I should be drawing parallels, but unfortunately, that's the only way I can express my issue.
In SQL, suppose we have two tables:
Employee with columns Employee ID, Employee Name, Dept. ID
Deptartment with columns Dept. ID, Dept Name
The Dept ID. in the Employee table is a foreign key with that in the Department table.
Now suppose I want to fetch the following columns:
Employee ID, Employee Name, Department Name
using a SQL such as:
SELECT A.EMPLOYEE_ID, A.EMPLOYEE_NAME, B.DEPT_NAME
FROM EMPLOYEE A, DEPARTMENT B
WHERE A.DEPT_ID = B.DEPT_ID
How would one do this using Core Data in Swift? I guess I'm getting confused by only seeing references to
NSFetchRequest(entityName: entityName)
where the entityName refers to a single entity (table in relational DB lingo)
I'd really appreciate any pointers or examples that can help me clear my doubts.
Thanks
It is certainly possible to create a fetch request that is equivalent to your SQL query. More complex queries can be difficult if not impossible to achieve with a single fetch request. But I recommend trying NOT to draw parallels between CoreData and SQL, at least until you have got to grips with how it works.
To take your example, in the CoreData view of the world, Employee would be an entity with a relationship to another entity, Department. A fetch request based on the Employee entity will return an array of Employee objects, and (assuming you create subclasses of NSManagedObject for each entity) you can access the attributes with simple dot notation:
let employeeName = myEmployeeObject.employeeName
But you can use the same notation to traverse relationships equally easily:
let departmentName = myEmployeeObject.department.departmentName
You don't need to worry about joins, etc; CoreData handles that for you.
Now, suppose you try to do it "the SQL way". You can construct a fetch request based on the Employee entity, but specify "properties to fetch" which likewise traverse the relationship:
let fetch = NSFetchRequest(entity:"Employee")
fetch.propertiesToFetch = ["employeeID", "employeeName", "department.departmentName"]
For this to work, you would need to specify the "resultType" for the fetch request to be DictionaryResultType:
fetch.resultType = .DictionaryResultType
The results from this query would be an array of dictionaries containing the relevant keys and values. But the link with the underlying objects is lost. If you subsequently want to access any details from the relevant Department (or even the Employee), you would have to run a new fetch to get the object itself.

Entity Framework - best practice to get count

I have a Customer table and another Orders table. Each Customer can have many orders (One to many relationship).
I want to get a Customer object and from it get how many orders he has (the actual order data is not relevant at this point). So as I see it I have 2 options:
create a view with another OrdersCount field - and that will be another object in my system.
in my app, when I need the count get the Customer.Orders.Count - but for my understanding that will cause an extra query to run and pull all the orders from the database to that collection.
Is there a correct way to do such thing?
Thanks
You do need a new type, but you don't need to recreate all relevant properties.
from c in context.Customers
// where ...
select new {
Customer = c,
OrderCount = c.Orders.Count()
}
Update code that looks for e.g. the Name property of an item in the result, to look for Customer.Name.

CoreData sort on to-many relationship

I'm writing an iOS app which has store of person records, and needs to display lists them sorted in particular ways. There are a variable number of these orderings, and they are generated on the fly, but I would like them to be stored in the datastore. The SQL way to do this is to have a ListPositions table with a list name, an id into the persons table, and a sort key. Then, to display a particular list, I can select all list ListPositions with a given name, pull in the referenced persons, and sort on the sort key. Trying to do this in CoreDatat, however I run into problems. I am trying to do this using a schema like:
Person:
Name
DOB
etc...
positions -->> ListPosition
ListPosition:
listName
sortKey
person --> Person
Then, I can get all the Persons in a given list with the NSPredicate
[NSPredicate predicateWithFormat:#"ANY positions.listName like %#", someList];
This allows me to dynamically add lists against a large set of Persons. The problem is that I am unable to use the sortKey field of ListPosition to sort the Persons. What NSSortDescriptor will do this? And if it is not possible to sort a fetch on the property of one element of a to-many relationship, what is another way to get multiple, dynamic orderings in coredata? I am displaying the lists with a NSFetchedResultsController, so I can't put the lists together myself in memory. I need to do it with a single NSFetchRequest.
You're right-- following a to-many relationship returns an NSSet, which has no inherent sorting. To get sorted results there are a couple of options:
Assuming that Person/ListPosition is a two-way relationship, do a new fetch request for ListPosition entities. Make the predicate match on the "person" relationship from ListPosition, which would look something like [NSPredicate predicateWithFormat:#"person=%#", myPerson]. Use whatever sort descriptor you need on the fetch request.
Follow the relationship as you're doing, which gives you an NSSet. Then use NSSet's -sortedArrayUsingDescriptors: method to convert that to a sorted array.
I think the best approach in this case would be to fetch on ListPosition entity instead. Add the sort Descriptor for sortKey (it would work in this case because the fetch request is on ListPosition entity) and prefetch the Person associated with the the list name using setRelationshipKeyPathsForPrefetching for "person" on the fetch request.
[NSPredicate predicateWithFormat:#"listName like %#", someList];
If I understand your model correctly, each Person has one ListPosition for each list in which it participates. Let's say we have acsending list by their names, so X people have X list positions with the same listName and sortKey.
I would create entity List, that would contain the sortKey attribute and then use it in sort descriptor.
entity List:
- sortKey : string
- ascending : bool
Create sort descriptor and use it in fetch request:
[NSSortDescriptor sortDescriptorWithKey:chosenList.sortKey ascending:chosenList.ascending];
Then you may have as many Lists as you want and you can easily use its sort key to sort all people.
If you want to store the positions in database (you didn't mention attribute index in your ListPosition, or anything similar), you can create “joint entity”:
entity PersonInList:
- index : integer
- person -> Person
- list –> List
Another idea is having ordered set of Person objects directly in List entity.
Get the ListPosition (it will come as a NSMutableSet). Then do a sort on the Set, like this:
NSMutableSet *positionsSet = [personEntity mutableSetValueForKey:#"positions"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"yourSortKey" ascending:NO];
NSArray *positionsSortedSet = [positionsSet sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
That will give you a sorted out array according to your key.
I usually add an index field (type NSNumber) to an entity. It's very easy to calculate index in adding item. just by
object.index = person.positions.count
so, actually you don't need positions field but positions relationship. connect person entity to ListPosition entity would be enough.

LLBLGEN related array not populating in entity

i'm struggling with LLBLGEN and i guess ORM's in general.
i have created an entity, lets use a library example to explain:
i want to display a book object and also return a list of users who have loaned the book.
so i need to return the book object which contains a list of users.
DTO Book::
int bookId,
string bookName
additionally i wish to return with my book a collection of users who have loaned the book:
List<user> Loans
loans table might look like this:
int id
int userid
int bookid
currently my loans entity has now created this:
DTO Loans
int id
User user // user entity
Book book // book entity
im struggling to understand how this example would work in llblgen. can anyone assist with guidance or point me in the way of a tutorial?
at the moment, when i come up to update my model Book with a new loan associated to a book, im getting stackoverflow errors. i assume this is creating some sort of loop when attempting to update my Book object.
thanks
i noticed when running a profiler on SQL that the sql statement didnt include any join statements onto my relationship entities.
this was because my domain query didnt include prefetch items for my relationships, i added the following to my query:
var query = new DomainSearch<T>
{
SearchText = searchText,
PrefetchItems =
{
new Prefetch(typeof(Users))
}
};
To make sure, you are looking for a list of User entities that have loaned a particular Book entity. Is this the correct use case, or are you looking for a list of User entities that have borrowed the particular book?
Regardless, LLBLGen's support for these cases is great with referencing relationships between entities and using related entities quickly and easily.
Presuming you're looking up a book by unique BookId/ISBN/etc....
// Get the specific book entity
BookEntity book = new BookEntity(bookId);
foreach(UserEntity user in book.users)
{
// do stuff with list of users
}
It's just that simple assuming you've defined your relationships between entities.