Core Data fetch request predicate filtering across one-to-one relationship - objective-c

I have the following model graph:
+-----------+ +-----------+
| Container | | Group |
+-----------+ +-----------+
| groups |<-->>| container |
+-----------+ +-----------+
^ ^
| |
+-----------+ +-----------+ +-----------+ +---------+
| Owner | | ToyBox | | ToyType | | Item |
+-----------+ +-----------+ +-----------+ +---------+
| toyBox |<--->| owner | | items |<-->>| toyType |
+-----------+ +-----------+ +-----------+ +---------+
In a UITableView I am to display a list a list of Items. In this case I only want to show the items that belong to a particular owner. To do this I will use NSFetchedResultsController to display the items. This means that I need create an NSFetchRequest with an appropriate NSPredicate to give to the NSFetchedResultsController.
Attempting to use a keypath predicate causes an exception due to the parent entities. This appears to be an Apple bug or decision not to support. A radar has been filed. Additionally, I do not wish to flatten the entities.
So that left me with an attempt to do this with SUBQUERY() as follows:
NSFetchRequest *itemsFetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Item"];
NSPredicate *itemsPredicate = [NSPredicate predicateWithFormat:#"SUBQUERY(toyItem, $g, SUBQUERY($g.container, $c, SUBQUERY($c.owner, $o, $o = %#).#count > 0).#count > 0).#count > 0", ownerObject];
This results in the following exception:
Can't have a non-relationship collection element in a subquerySUBQUERY($c.owner, $o, $o == <MLMOOwner: ...
I realize that because the relationship is one to one between Owner and ToyBox that there isn't a collection returned and this is the problem. So my questions are:
1) Is there a way to force the return of a collection for to-one relationships in the subquery?
2) If not is there another way to go about creating this predicate for the fetch request?

Seems like it should be a lot simpler to do what you want. If you want all the items with a given owner, start with the owner and use a key path to get the associated items. You don't need a predicate. If you've got 10 jars of peanuts and you want to retrieve the peanuts in jar 2, you don't start with the set of all peanuts and then filter them according to their jar, right? You first get jar 2, and then you look at the peanuts it contains. So do this:
NSSet *groups = [ownerObject valueForKeyPath:#"toyBox.groups"];
That gives you all the groups owned by ownerObject. You can't just get all the items using a single key path because the Group entity doesn't have an items attribute. You could make life easier for yourself by giving Group an items accessor, even if it only returns an empty set. That would let you do this:
NSSet *items = [ownerObject valueForKeyPath:#"toyBox.groups.items"];
If you don't want to add an items attribute to Group, you'll have to filter the set groups from the first example to pick out just those objects that are ToyType objects. You can then use the key path #"items" on that set to get the list of items that you want.
You have to be a bit careful with entity inheritance. You've just seen how it can make fetching a little more complicated. It also has implications for the way your data is stored, namely that all instances of sub-entities are stored in the same table. So, if you have a dozen entities derived from Group (such as ToyType), all the instances of all those entities are stored together.

Related

How to make power bi work with many to many?

I have the following model, where the relation tables create a many to many relationship, as usual.
|Table car | |relation car worker|
|Id car |1----*|id car |
|car things| |id worker |*-----
|
|
|table worker | /
|id worker |1----------/
|worker things | \
\_______________
\
|table building | |relation worker building| |
|id building |1----*|id building | |
|building things| |id worker |*---------
When I load this model in Power Bi, it can build a table visualization (and others) containing one of the following:
Option 1:
Car things
Worker things
Option 2:
Worker things
Building things
But it totally fails when I try to put in the table visualization something from the edges of the model:
Car things
Building things
This is the error:
What's going on here? Why the error?
Basically I need to see which cars visit which buildings (and do one or two summarizations)
From what i can understand from your model.. you need to set your cross filter direction to both rather than single so it can filter back up the tables
Here is an example which may help you to understand
For example, for 1 from tableA, it can relate 1 or 2 from tableB, since 1 from Table B can also relate 1 or 2 from tableC, so for 1 from tableA, it can’t determine whether 1 or 2 from tableC should be refer to
*----------------------------*
|Table A Table B Table C |
*----------------------------*
| 1 1 1,2 |
| 1 2 1,2 |
*----------------------------*
You can create a new table with the formula
tableName = CROSSJOIN(tableA,tableB,tableC....tableZ)
I had to create a new table as if I were doing an inner join in SQL (which works just as expected).
Click "modeling", "create table".
For the formula:
NewTable = NATURALINNERJOIN(tb_cars,
NATURALINNERJOIN(tb_rel_car_worker,
NATURALINNERJOIN(tb_workers,
NATURALINNERJOIN(tb_rel_worker_building, tb_buildings))))
Then I start using everything from this table instead of the others.
In order to create metrics that include zero counts grouped by one of the edge tables, I had to leave this table separate:
Partial Table= NATURALINNERJOIN(tb_rel_car_worker,
NATURALINNERJOIN(tb_workers,
NATURALINNERJOIN(tb_rel_worker_building, tb_buildings))))
Then in the relationship editor, I added a relation between Partial Table[id_car] and tb_cars[id_car].
Now the metrics grouped by car can use the technique of adding +0 to the DAX formulas to show also cars whose metric sum zero in charts.

How to represent unique attribute in Z-notation without quantifiers?

Full disclosure, this is for a university course. I don't expect an answer outright, but help would be appreciated.
I need to model an Item entity using Z-notation. This is the description:
Item: Every item has a name and a unique ID which can be used to uniquely describe the item. An item also has a price (positive float) and a category.
Part of the requirement is modelling these entities without quantifiers.
This is what I ended up with, but I'm not sure that it's correct:
Schema for Item
The idea being that the name is some combination of strings, the ID is a tuple of a positive integer and said name, and both the price and the category are mapped with total functions.
The first predicate is to ensure a positive price, the second is to ensure the uniqueness of the ID, i.e. reduce the domain to all integers that are not already assigned. I don't think this is correct, though.
The main issue with your approach is that you try to put information about the whole system (or part of it) into the description of a single item. E.g. you specified the price as a mapping from the id to a float - which is fine in principle - but you do not have such a function for each item.
There are many ways to specify this, I show two approaches:
You have two schemas: E.g. Item and Database
+-- Item -----
| id: ℕ
| name: String
| price: ℝ
| category: String
|----
| price ≥ 0
+----------
+-- Database -----
| items: ℕ +-> Item
|----------
This way you have the ID of each item moved from the item itself. When each item has also a field id, it would be complicated to state without quantifiers the fact that items should map an id to an item with the same id. Or when you just use a set of items it would be complicated to describe without quantifiers that two items must have distinct identifiers.
The uniqueness of the id for each item is guaranteed by items being a function.
Or just use several functions for each aspect of an item:
+-- Items -----
| ids: ℕ
| name: ids --> String
| price: ids --> ℝ
| category: ids --> String
+----------
But stating the fact that all prices must be non-negative without quantifiers would be hard. Maybe by replacing ℝ by { x:ℝ | x≥0}.
A general remark: Do you need compute with your IDs? Maybe you can introduce a type with [ID] instead. The same applies for the category (e.g. [CATEGORY]).
And is the name not just a single string? But I don't think it would be a set of (unordered) strings.

sqlite variable and unknown number of entries in column

I am sure this question has been asked before, but I'm so new to SQL, I can't even combine the correct search terms to find an answer! So, apologies if this is a repetition.
The db I'm creating has to be created at run-time, then the data is entered after creation. Some fields will have a varying number of entries, but the number is unknown at creation time.
I'm struggling to come up with a db design to handle this variation.
As an (anonymised) example, please see below:
| salad_name | salad_type | salad_ingredients | salad_cost |
| apple | fruity | apple | cheap |
| unlikely | meaty | sausages, chorizo | expensive |
| normal | standard | leaves, cucumber, tomatoes | mid |
As you can see, the contents of "salad_ingredients" varies.
My thoughts were:
just enter a single, comma-separated string and separate at run-time. Seems hacky, and couldn't search by salad_ingredients!
have another table, for each salad, such as "apple_ingredients", which could have a varying number of rows for each ingredient. However, I can't do this, because I don't know the salad_name at creation time! :(
Have a separate salad_ingredients table, where each row is a salad_name, and there is an arbitrary number of ingredients fields, say 10, so you could have up to 10 ingredients. Again, seems slightly hacky, as I don't like to unused fields, and what happens if a super-complicated salad comes along?
Is there a solution that I've missed?
Thanks,
Dan
based on my experience the best solution is based on a normalized set of tables
table salads
id
salad_name
salad_type
salad_cost
.
table ingredients
id
name
and
table salad_ingredients
id
id_salad
id_ingredients
where id_salad is the corresponding if from salads
and id_ingredients is the corresponding if from ingredients
using proper join you can get (select) and filter (where) all the values you need

Possible design pattern suggestions?

I was wondering if anyone can suggest a suitable design pattern for achieving the following:
I have a payslip, each slip shows my previous pay and my current pay. Each payslip should not need to duplicate fields, but rather point the current value to the previous value in the next slip.
On top of all this, I also need to be able to retrieve any given payslip at any point in time (preferably O(1)).
Here's a visual to help understand my problem.
[key:"1"] [key:"2"] [key:"3"]
+------+ +------+ +------+
| | | | | |
| Curr | <--- | Prev | | Curr |
| Null | | Curr | <--- | Prev |
| | | | | |
+------+ +------+ +------+
Any help would be greatly appreciated
Give your PaySlip class a PreviousPaySlipKey property, which will be null for the very first pay slip. In your database, this should be a foreign key to the PaySlip's Key property.
This way, if you have a PaySlip, you can find the key of the previous pay slip, and query the database for the payslip with that key.
Disclaimer: I am not a VB programmer, and the syntax below may not apply. Assuming VB has a Map interface, you could use that for your lookups O(1)
Map<Integer, CompositeValue> = some hash based map
and declare a class (assuming VB lets you create classes) CompositeValue as such:
class CompositeValue {
Integer previousKey;
Value value;
}
Now, once you've retrieved a CompositeValue from the Map, you have the means to get it's real value (value), and to retrieve the previous value using previousKey.
Just a thought.
If we are talking DBs here, every payslip should be given a sequential ID (also called auto-increment in some RDBMS). This will be the primary key (so it's automatically indexed). Assuming you retrieved the payslip you wanted, here is how to get previous one from DB:
SELECT TOP 1 * FROM PaySlip WHERE PaySlipID < #PaySlipID ORDER BY PaySlipID Desc
And the next one:
SELECT TOP 1 * FROM PaySlip WHERE PaySlipID > #PaySlipID ORDER BY PaySlipID
You will probably have payslips for multiple employees stored in a single table, so just add to the WHERE condition.
It is counter productive to store references to previous/next payslip on each payslip.

Trying to understanding Core Data relationships or handling duplicates

I'm trying to verify I understand Core Data relationships and/or possibly how to handle duplicates.
In my example below, I have entity Account in a to-many relationship with entity Transaction Date. I'm thinking to-many as there will be multiple dates under the one Account.
Where I'm getting confused is that I only want to have one specific date... meaning, only one date and no duplicates. The intent is then to have entity Transaction Date to have a to-many relation with entity Event. So account XYZ will have trans date of 06/11/2012 and multiple entries for entity Event. Then account XYZ will have trans date of 06/12/2012 and multiple entries for entity Event.
Is the relationship between Account and Trans Date truly a to-many or to-one?
If it is a to-many... how are duplicates handled? How do I keep only one date in entity Trans Date? If my code is adding by entries into Event and Trans Date, is some handling done there? How?
I'm guessing Account to Trans Date should be to-one... but really not sure at this point.
/-----------------------\ /----------------------\ /------------------\
| Account | | Transaction Date | | Event |
|-----------------------| |----------------------| |------------------|
| name | | addDate | | amount |
| balance | | | | |
|-----------------------| |----------------------| |------------------|
| heldByAcct | <-\ | | | |
| | \->> | inAcct | | |
| | | heldByEvent |<-\ | |
\-----------------------/ \----------------------/ \->>| inTrans |
\------------------/
I'm not sure I understood what you are trying to achieve but I'll try to give you some suggestions.
First
If an Account can have only one Transaction Date, I think you should set a one-to-one relationship. In this manner you are sure that one Account has a Transaction Date and viceversa. Behind, Core Data will set up your db so that your Account table will have a field for the ID of the corresponding Transaction Date object.
Second
If you are setting up a checkout mechanism and the Account can have multiple transaction (e.g. only one per day) you need to set up a one-to-many relationship. If you need to check for a single transaction per day, you need to perform the check manually. Set a request against Transaction Date with a predicate for a specific Account and date and see if the count is greater than one. If no, add that Transaction Date.
NSInteger count = [fetchRequest countForFetchRequest:&error]; // query against `Transaction Date`
if(count >= 1) // not allowed
else // allowed
Usually when I need to check for duplicates, I use a guid as an attribute for an entity (e.g. guid of type NSString). So, in your Transaction Date you could use such a mechanism. Then you could set the fetch request with a predicate like the following:
[NSPredicate predicateWithFormat:#"guid == %# AND inAcct == %#", #"12345", [self currentAccount]];
Each time you insert a new Transaction Date you could generate a new guid based on the date (without time) and the specific Account (use your own protocol to do this).
Thinking on it, you could also save and check the date without its time part (category-on-nsdate). I think it will work but you need to try. By means of this you could simply use a predicate without a guid. For example:
[NSPredicate predicateWithFormat:#"addDate == %# AND inAcct == %#", [self currentDate], [self currentAccount]];
Hope it helps.
To model a transaction register, I'd suggest making addDate a property of Event and removing Transaction Date entirely.
It significantly simplifies the work you need to do to manage the object graph, and removes the need to de-duplicate the dates.
If you need to produce a list of unique dates, you can do it on the fly with distinctUnionOfSets as described in the answer to this question:
CoreData get distinct values of Attribute.