Is it possible to use group_concat with coredata? - ios

I am really trying hard to use coredata with a new IOS application.
A real challenge is that since my background is SQL, I keep thinking in terms of SQLite rather than coredata. I've solved most hurdles, but this one stumps me -- I really want to create a SQLite view that returns a group_concat.
Do I need to recreate this by coding on the results in objective-c, or is there a way to create a view or issue direct SQL using coredata objects?
EDIT: Added data model and desired results
Table_A; columns -- key, name (note -- key is not unique)
Sample data:
K1, N1
K1, N2
K1, N3
K2, N1
K2, N2
Desired results:
K1, N1;N2;N3
K2, N1;N2
In SQLite this would be SELECT key, GROUP_CONCAT(name) from Table_A GROUP BY key

If you set your fetch request to return results as dictionaries, you have a lot of control over grouping.
http://mattconnolly.wordpress.com/2012/06/21/ios-core-data-group-by-and-count-results/ and Core Data Fetching Properties with Group By Count both contain good examples. The second link shows how to walk down a relationship's keypath for grouping.
EDIT:
After seeing revised question, I tinkered with this a bit. Source code is on Github: https://github.com/halmueller/CoreDataGroupFetch
I couldn't get a solution that would work in one single query. This is the best I could come up with. It fetches all of the unique values for the "key" field, then iterates over those keys and fetches all objects matching that "key". In the second fetch, you might want to fetch NSManagedObjects instead of dictionaries, depending on what you're going to do.
NSFetchRequest* uniqueKeysFetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"KeyedName"];
uniqueKeysFetchRequest.propertiesToFetch = #[#"key"];
uniqueKeysFetchRequest.resultType = NSDictionaryResultType;
uniqueKeysFetchRequest.returnsDistinctResults = YES;
NSError* error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:uniqueKeysFetchRequest
error:&error];
NSLog(#"uniqueKeysFetchRequest: %#", results);
NSLog(#"distinct values for \"key\": %#", [results valueForKeyPath:#"#distinctUnionOfObjects.key"]);
for (NSString *thisKey in [results valueForKey:#"key"]) {
NSFetchRequest *oneKeyFetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"KeyedName"];
NSString *predicateString = [NSString stringWithFormat:#"key LIKE '%#'", thisKey];
oneKeyFetchRequest.predicate = [NSPredicate predicateWithFormat:predicateString];
oneKeyFetchRequest.resultType = NSDictionaryResultType;
oneKeyFetchRequest.propertiesToFetch = #[#"name"];
NSLog(#"%#: %#", thisKey, [self.managedObjectContext executeFetchRequest:oneKeyFetchRequest error:&error]);
}
This yields
results from uniqueKeysFetchRequest: (
{
key = K1;
},
{
key = K2;
}
)
distinct values for "key": (
K1,
K2
)
K1: (
{
name = N2;
},
{
name = N1;
},
{
name = N3;
}
)
K2: (
{
name = N2;
},
{
name = N1;
}
)
I also tried
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"KeyedName"];
fetchRequest.propertiesToFetch = #[#"key", #"name"];
fetchRequest.propertiesToGroupBy = #[#"key", #"name"];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.returnsDistinctResults = YES;
NSError* error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest
error:&error];
NSLog(#"withKeypathStrings: %#", results);
NSLog(#"distinct values for \"key\": %#", [results valueForKeyPath:#"#distinctUnionOfObjects.key"]);
which might be closer to what you want:
withKeypathStrings: (
{
key = K1;
name = N1;
},
{
key = K1;
name = N2;
},
{
key = K1;
name = N3;
},
{
key = K2;
name = N1;
},
{
key = K2;
name = N2;
}
)
distinct values for "key": (
K2,
K1
)

You can implement group_concat with Core Data using NSExpression(forFunction: "count:") or other functions with numbers https://developer.apple.com/documentation/foundation/nsexpression/1413747-init
But there is no way to make string concat like "name1, name2, name3..." because Core Data doesn't support custom NSExpression functions

Related

Core Data, Get Sum Of Certain Boolean Value Attribute

I'm logging a Core Data attribute "passed" (Boolean value)
for (Circuit *object in self.distributionBoard.circuits) {
NSLog(#"Core Data Value = %d", object.passed);
}
This logs fine. What's the most efficient way to count the number of times the saved boolean value == 1?
Using NSFetchReques or NSExpression did not yield the desired result so far. Looked here: Core Data sum of all instances attribute and similar, with the usual searches
Since your property is a boolean, you can make it a lot simpler than the methods described in that answer. Use a predicate to match the value of passed and then get the count of the result instead of the fetched objects. Something like:
NSFetchRequest<Event *> *fetchRequest = MyEntity.fetchRequest;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"passed = true"];
NSError *error = nil;
NSUInteger count = [self.managedObjectContext countForFetchRequest:fetchRequest error:&error];
Then count has the number of instances where passed is true.

Parsing Json Output correctly

I am trying to correctly target the elements within the Json Output and I am getting closer but I presume there is a easy and obvious way I am missing.
My Json looks like this with a upper level event.
JSON SNIPPET UPDATED
chat = (
(
{
Key = senderId;
Value = {
Type = 0;
Value = "eu-west-1:91afbc3f-890a-4160-8903-688bf0e9efe8";
};
},
{
Key = chatId;
Value = {
Type = 0;
Value = "eu-west-1:be6457ce-bac1-412d-9307-e375e52e22ff";
};
},
{
Key = timestamp;
Value = {
Type = 1;
Value = 1430431197;
};
},
//Continued
I am targeting this level using
NSArray *chat = array[#"chat"];
for ( NSDictionary *theCourse in chat )
{
NSLog(#"---- %#", theCourse);
// I tried the following to target the values
//NSLog(#"chatId: %#", [theCourse valueForKey:#"Key"]);
//NSLog(#"timestamp: %#", theCourse[#"senderId"] );
}
}
I need to parse the value data for each key which if I was using an array would do like [theCourse valueForKey:#"Key"] but I think I may not be going deep enough?
As you would expect, [theCourse valueForKey:#"Key"] gives me the Key values but I need the associate values of those keys.
You can create an easier dictionary:
NSArray *chat = array[#"chat"][0];
NSMutableDictionary* newDict = [NSMutableDictionary dictionary];
for (NSDictionary* d in chat)
[newDict setValue:d[#"Value"][#"Value"] forKey:d[#"Key"]];
Now you can use the newDict.
NSLog(#"chatId: %#", [newDict valueForKey:#"chatId"]);

Count of child in Coredata

I need to find count of children based on logic.
I have table A, it has two relationship B and C. Now i need to find count of B and C.
Count = No of B * NO of C.
Data:
A1
{
{
B1a
},
{
C1a,
C1b
}
},
A2:
{
{
B2a,
B2b
},
{
C2a,
C2b
}
}
Total Count = 6
i have tried with following
NSEntityDescription *entity = [NSEntityDescription entityForName:#"A" inManagedObjectContext:context];
NSArray *allObjects = [context executeFetchRequest:fetchRequest error:&fetchError];
NSInteger totalCount= 0;
for(A *a in allObjects)
{
NSInteger countOfB = [a.B count];
NSInteger countOfc = [a.C count];
totalCount = totalCount + (countOfB * countOfc);
}
This is work fine. But when i have 10000 records it is taking more time. Please suggest me if any alternative ways.
Don't do the multiplication on demand. Each time an A instance has the relationship to B or C changed, calculate the product and store it in a new attribute on A. Now to fetch your total count you can use only a single fetch (returning dictionary type) and then #sum (using the array with a collection operator) and none of the objects need to actually be loaded into memory.
Consider using KVO to monitor for relationship changes and trigger the update to your product.

How to get data from unrelated entities using Core Data?

I have these three entities which are not related. How can i get the salary of an employee. I want something like this :
Select C.salary FROM Employee A, Department B, Salaries C Where A.id=B.empid AND B.id=C.deptid AND A.id=12
i need to do the same above operation in core data.
As Martin suggested, the easy way is to set up the needed relationships and traverse them.
But if you don't have the permission to alter the model, you need to work and filter managed in memory object you own once retrieved. In other words, you need to set up NSFetchRequests with the correct NSPredicates.
For example, to retrieve the department for a given employee.
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:#"Department"];
request.predicate = [NSPredicate predicateWithFormat:#"empid", #(12)];
NSError *error = nil;
NSArray *departments = [moc executeFetchRequest:request error:&error];
if(departments) {
// do a NSLog for departments here to see what you have
// here you can access deptid for each department you retrieve
// and with that value run another request against Salaries
// e.g. NSManagedObject* department = [departments lastObject];
// NSNumber* deptid = [department valueForKey:#"deptid"]
} else {
// handle error here
}
Other part is left as excersise ;)

Parsing json returned by foursquare for iPhone gives Unrecognised leading character

I'm trying to get the nearby places using the foursquare api.
Here's the json data that is returned from
NSDictionary *results = [jsonString JSONValue];
NSLog(#"%#", results);
(
{
code = 200;
errorDetail = "This endpoint will stop returning groups in the future. Please use a current version, see http://bit.ly/lZx3NU.";
errorType = deprecated;
},
{
groups = (
{
items = (
{
categories = (
{
icon = "https://foursquare.com/img/categories/parks_outdoors/default.png";
id = 4bf58dd8d48988d163941735;
name = Park;
parents = (
"Great Outdoors"
);
pluralName = Parks;
primary = 1;
shortName = Park;
}
);
Then I try to get the list of the groups in an array with
NSArray *groups = [ (NSDictionary *)results objectForKey:#"groups"];
This returns the following error
2011-11-05 11:42:12.907 XperienzApp[1972:207] No of results returned: 0 Results : (null)
2011-11-05 11:42:13.225 XperienzApp[1972:207] -JSONValue failed. Error trace is: (
"Error Domain=org.brautaset.JSON.ErrorDomain Code=3 \"Unrecognised leading character\" UserInfo=0x5849cd0 {NSLocalizedDescription=Unrecognised leading character}"
)
2011-11-05 11:42:13.225 XperienzApp[1972:207] No of results returned: 0 Results : (null)
How should I parse this?
Edit:
I tried the suggested technique, this gives me an array
id groups = [[(NSDictionary *)results objectForKey:#"response"] objectForKey:#"groups"];
if ([results count] > 1){
NSLog(#"groups class %#\ngroups %# %d", groups, [groups class], [groups count]);
The log output is of the form:
{
categories = (
{
icon = "https://foursquare.com/img/categories/nightlife/danceparty.png";
id = 4bf58dd8d48988d11f941735;
name = Nightclub;
parents = (
"Nightlife Spots"
);
pluralName = Nightclubs;
primary = 1;
shortName = Nightclub;
}
);
contact = {
};
hereNow = {
count = 0;
};
id = 4eb33ba561af0dda8f673c1b;
location = {
address = "144 Willow St 4R";
city = Brooklyn;
crossStreet = Pierrepont;
distance = 462;
lat = "40.696864";
lng = "-73.996409";
postalCode = 11201;
state = NY;
};
name = "Entertainment 720, Ltd.";
stats = {
checkinsCount = 3;
tipCount = 0;
usersCount = 1;
};
verified = 0;
}
);
name = Nearby;
type = nearby;
}
)
groups __NSArrayM 1
This is again not json and is hard to parse, how do I get the output in json.
I'm the iPhone lead at foursquare. I'll try to take a stab at what's going on here.
First of all, I highly recommend you use JSONKit for your parser. It's lightweight and insanely fast: https://github.com/johnezang/JSONKit
It appears that you are parsing the JSON properly and getting the dictionary properly. Then you are logging the parsed object, not the original JSON. The output you are seeing is how Objective-C chooses to serialize the parsed dictionary to text. It is definitely not JSON. Using JSONKit, you could send the JSONString selector to your parsed result and convert it back to JSON and log that.
If you could provide some details on the problem you are trying to solve, I might be able to help you out more. And as Maudicus said, please pay attention to the error you are getting back. You don't want your app to break when we make the change to the API.
If the output below NSLog(#"%#", results); is your log statement. It appears your results variable is an array of dictionary objects.
Try to log the class of results to verify that NSLog(#"%#", [results class]);
If it is an array your groups object is the second object.
if ([results count] > 1)
id groups = [results objectAtIndex:1];
NSLog(#"groups class %#\ngroups %#", [groups class], groups);
Keep doing this until you understand the format of your data
Also the line
errorDetail = "This endpoint will stop returning groups in the future. Please use a current version, see http://bit.ly/lZx3NU.";
should be cause for concern. Check the documentation on foursquare for the current way of getting groups.

Resources