Swift 3 Core Data - Cannot Save Relational Value - core-data

I'm getting an error when I simply try to save data. Here is what I'm doing...
1) First I Fetch using a Category object:
let categories: NSFetchRequest<Categories> = Categories.fetchRequest()
categories.predicate = NSPredicate(format: "id==%#", '25')
var category = try managedObjectContext.fetch(categories)[0]
2) Then I set the values on a new Subcategories object:
let subcategory = NSEntityDescription.insertNewObject(forEntityName: "Subcategories", into: managedObjectContext) as! Subcategories
subcategory.text = "Some text..."
subcategory.categories = category
3) Lastly I save:
managedObjectContext.save()
***** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
Category is not nil though because I can print it immediately before saving. I think there is a problem with the managedObjectContext.

I figured it out. The problems was on the parent entity "categories". I had checked a box to set those to "ordered", which for some reason caused this weird error. What specifically was happening was the categories fieldof the object returned from my query was always nil, always.

Related

SwiftUI - CoreData one too many relationship append array

So I'm using CoreData and wish to append an array of activities. The issue is, it will only append one/the latest entry within the view model array. This works as a one too many relationship.
let activity = Activity(context: self.moc)
activity.trip = Trip(context: self.moc)
activity.trip?.destinationName = destinationName
for activityIndex in flightOverViewModel.selectedActivites {
let ind = Activity()
ind.title = activityIndex.title
ind.name = activityIndex.name
ind.longitude = activityIndex.longitude
ind.latitude = activityIndex.latitude
}
try? self.moc.save()
I've tried activity.trip?.activitiesArray.append(ind) but get an error saying 'Cannot use mutating member on immutable value: 'activitiesArray' is a get-only property'.
Any ideas on how I can index and append each value in flightOverViewModel.selectedActivites array to CoreData?
Thank you.

MongoDB: How to search in document array element

I'm sure this is a trivial question for the most of the Mongo users however, I was unlucky in finding the right answer.
I have a collection of documents like
{
_id:"2a1fd96c-73c5-49e1-a8ca-bd03a20c0197",
timestamp:1519725979178,
storeID:"xxx",
unitID: "yyy",
status:[1, 0, 1, 0],
_rev:"1-8019f22bf26b4d6cb99ae5460b3e0c65"
}
I need to find all documents that:
storeID = "xxx" AND unitID = "yyy" AND status[2] = 1
My filter entry that works with Compass
{'status.2': 1,storeID:'xxx',unitID:'yyy'}
However when I am trying to convert it into Js code
Model.find({'status.2': 1,storeID:'xxx',unitID:'yyy'})
Nothing is returned.
After a couple hours of pulling my hair out, I nailed the problem.
The filtering query
{'status.2':1,storeID:'xxx',unitID:'yyy'} and {'status.2':{$eq:1},storeID:'xxx',unitID:'yyy'} was actually okay.
Unfortunatelly, I used .find() along with Model instead of invoking it in collection scope:
let mongoose = require('mongoose'),
Schema = mongoose.Schema,
MyShema = new Schema({/* definition */}),
Model = mongoose.model('MyShema'),
filter = {'status.2':1,storeID:'xxx',unitID:'yyy'};
// BEFORE
let cursor = Model.find(fd); //returns total=0
// AFTER
let cursor = Model.collection.find(fd); //returns total=80
cursor.count()
.then(total=>console.log('total',total))
.catch(error=>console.log(error));
Somehow bizarrely when I removed 'status.2':1 from the filtering options, both instances of the cursor were returning an identical amount of documents.
The reason for that that was so pathetic - the status was declared in Schema as String whereas it should be obviously Array!

NSFetchRequest predicate querying correctly, but resulting NSManagedObject showing incorrect fields [duplicate]

I am using UIManagedDocument with Parent Child context.
In my child context I do the following
Code 1
NSSet *results = [self.event.memberships filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([[evaluatedObject deleted] boolValue] == NO);
}]];
Above code returns the expected results (only Not deleted members for the event).
Code 2
But this code does not. It fetches all records.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deleted == NO"];
NSSet *results = [self.event.memberships filteredSetUsingPredicate:predicate];
It seems confusing. Both should return same results, but predicateWithBlock returns correct results where as predicateWithFormat returns all records.
What are the pros and cons of using predicateWithBlock instead of predicateWithFormat?
The problem is that you have defined an attribute deleted for your entity. That conflicts with the isDeleted method of NSManagedObject, so you should rename that attribute.
The following "experiment" shows that strange things happen if you call your attribute "deleted" (c is a managed object with a custom deleted attribute):
// Set custom "deleted" property to YES:
c.deleted = #YES;
// Use the property, as your Code 1
NSLog(#"%#", [c deleted]);
// Output: 1
// Use Key-Value Coding, as your Code 2
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 0
// Now really delete the object and try again:
[context deleteObject:c];
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 1
Your "Code 1" refers to the property, therefore it returns the expected result. "Code 2" uses Key-Value Coding, and [c valueForKey:#"deleted"] returns YES if the object
actually has been deleted from the context!
So renaming that attribute should solve your problem. Unfortunately the compiler does not
emit warnings if an attribute name conflicts with a built-in method.
Use the formatting placeholder to replace the bool value:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#",
#"deleted", #(NO)];
Your use of the key path is probably ok, but the right-hand side probably doesn't look like "NO" to the parser.

Grouping a dictionary NSFetchRequest by object ID

I need to return a list of objects along with a count of its related objects. It doesn't seem to be possible to do this in a single dictionary fetch request as I am unable to group the fetch results by objectID.
let objectIDExpression = NSExpressionDescription()
objectIDExpression.name = "objectID"
objectIDExpression.expression = NSExpression.expressionForEvaluatedObject()
objectIDExpression.expressionResultType = NSAttributeType.ObjectIDAttributeType
let countExpression = NSExpressionDescription()
countExpression.name = "count"
countExpression.expression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: "entries")])
countExpression.expressionResultType = .Integer32AttributeType
let fetchRequest = NSFetchRequest(entityName: "Tag")
fetchRequest.resultType = .DictionaryResultType
fetchRequest.propertiesToFetch = [objectIDExpression, countExpression]
fetchRequest.propertiesToGroupBy = [objectIDExpression]
var error: NSError?
if let results = self.context.executeFetchRequest(fetchRequest, error: &error) {
println(results)
}
When this request executes it errors with:
'Invalid keypath expression ((<NSExpressionDescription: 0x7f843bf2d470>), name objectID, isOptional 1, isTransient 0, entity (null), renamingIdentifier objectID, validation predicates (
), warnings (
), versionHashModifier (null)
userInfo {
}) passed to setPropertiesToFetch:'
I also tested just passing the "objectID" expression name, but that also fails.
Is there therefore no way to group by object ID?
You can get the required count without using propertiesToGroupBy. CoreData seems to infer the correct scope for the count and uses a sub-SELECT instead (strangely, only if the fetch includes an attribute as well as the objectID and count, see below). For example, I have Tag many-many with Items:
First attempt
I can fetch tagName and the count of items as follows:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Tag"];
NSExpressionDescription *countED = [NSExpressionDescription new];
countED.expression = [NSExpression expressionWithFormat:#"count:(items)"];
countED.name = #"countOfItems";
countED.expressionResultType = NSInteger64AttributeType;
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[#"tagName", countED];
NSArray *results = [self.context executeFetchRequest:fetch error:nil];
NSLog(#"results is %#", results);
which generates the following SQL:
SELECT t0.ZTAGNAME, (SELECT COUNT(t1.Z_3ITEMS) FROM Z_3TAGS t1 WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
Second attempt
Sadly, it seems CoreData gets confused if I try to select the objectID instead of the tagName:
NSExpressionDescription *selfED = [NSExpressionDescription new];
selfED.expression = [NSExpression expressionForEvaluatedObject];
selfED.name = #"self";
selfED.expressionResultType = NSObjectIDAttributeType;
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[selfED, countED];
generates this SQL:
SELECT t0.Z_ENT, t0.Z_PK, COUNT( t1.Z_3ITEMS) FROM ZTAG t0 LEFT OUTER JOIN Z_3TAGS t1 ON t0.Z_PK = t1.Z_8TAGS
which counts all the rows from the outer join (and suggests that you need to group by the objectID, though we know that won't work).
Final attempt
However, include tagName and objectID, and all is well again:
fetch.propertiesToFetch = #[selfED, #"tagName", countED];
gives:
SELECT t0.Z_ENT, t0.Z_PK, t0.ZTAGNAME, (SELECT COUNT(t1.Z_3ITEMS) FROM Z_3TAGS t1 WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
which seems to do the trick. (Sorry for reverting to Objective-C, and for using different entity/attribute names, but I'm sure you get the picture).
Aside
One other curiosity I discovered is that the second attempt above can also be made to work by counting an attribute of the relationship, rather than the relationship itself:
countED.expression = [NSExpression expressionWithFormat:#"count:(items.itemName)"];
fetch.propertiesToFetch = #[selfED, countED];
gives:
SELECT t0.Z_ENT, t0.Z_PK, (SELECT COUNT(t2.ZITEMNAME) FROM Z_3TAGS t1 JOIN ZITEMS t2 ON t1.Z_3ITEMS = t2.Z_PK WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
which will (I think) give the correct counts provided itemName is not nil.
I played with this for a bit, sure there had to be some way to tell core-data to group by the primary key.
I couldn't figure it out, though I believe it to be possible.
The best I could do was add another unique attribute "uuid" (which I use for all of my entities anyway, for various reasons). You can do this easily enough with NSUUID, or you can take the permanent object ID URI representation and turn it into a string.
Anyway, I think this gives you what you want, but does so by requiring a separate unique attribute.
fetchRequest.propertiesToGroupBy = #[#"uuid"];
I tried a bunch of alternatives as the group-by property but expressionForEvaluatedObject always barfs, and other attempts fell flat.
I'm sure you know this already. Just in case, though it's at least a workaround, even if you don't use it for anything else, until someone comes around who has actually done this before.
FWIW, here is the SQL...
CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, COUNT( t1.Z_1ENTRIES), t0.ZUUID
FROM ZTAG t0 LEFT OUTER JOIN Z_1TAGS t1 ON t0.Z_PK = t1.Z_2TAGS
GROUP BY t0.ZUUID
Surely, there has to be a way to tell it to substitute t0.Z_PK in the group-by clause. I would image that should be an easy special case for expressionForEvaluatedObject or "objectID" or "self" or "self.objectID"
Good luck, and please report back if you solve it. I'd be very interested.
It is perhaps easier to use a NSFetchedResultsController. You can set the sectionNameKeyPath to group and use the resulting NSIndexPaths to construct your dictionary.
That being said, I do not think that it makes any sense to group by objectID because every object ID is by definition unique. So there will be one instance in each group. This is likely why setting propertiesToGroupBy fails.
So, short answer: no.
E.g.
let fetchRequest = NSFetchRequest(entityName: "Tag")
var output = [(NSManagedObjectID, Int)]()
do {
let results = try context.executeFetchRequest(request) as! [Tag]
for tag in results {
output.append((tag.objectID, tag.entries.count))
}
} catch {}
// output contains tuples with objectID and count
If entriesis optional, use tag.entries?.count ?? 0.

Getting "Value does not fall within the expected range" when creating a new discussion with CSOM

I'm using the SharePoint 2013 client object model.
I'm getting a Microsoft.SharePoint.Client.ServerException that says: "Value does not fall within the expected range" when running _context.ExecuteQuery(). The code works as long as I comment out the statement entry["Author"] = authorValue;:
entry["Body"] = post.Body;
entry["Created"] = post.Created;
entry["Modified"] = post.Modified;
FieldUserValue authorValue = new FieldUserValue();
User author = _context.Web.EnsureUser("Mr. X");
_context.Load(author);
_context.ExecuteQuery();
authorValue.LookupId = author.Id;
entry["Author"] = authorValue;
entry.Update();
_context.ExecuteQuery();
"Author" is a valid field name. I also read about increasing "List View Lookup Threshold" to a value of 20 in http://dotnetfollower.com/wordpress/2012/05/sharepoint-value-does-not-fall-within-the-expected-range-exception-in-spfieldmap-getcolumnnumber/. But that value is already set to 5000.
I also checked the value of entry["Author"] without setting it explicitly (entry["Author"] = authorValue commented out) by adding this two statements:
_context.Load(entry);
_context.ExecuteQuery();
It is of type Microsoft.SharePoint.Client.FieldUserValue and it's LookupId property value is equal to authorValue.LookupId.
What did I miss?

Resources