core data subquery - ios

I have an an entity containing two optional to-many relationships (childA <<-> parent <->> childB). Each of these two child entities contain an optional string that I am interested in querying on.
Using the same format, I get the results I expect for one, but not the other. I understand that means I don't understand the tools I'm working with; and hoped for some insight. This is what the two queries look like:
childA.#count != 0 AND (0 == SUBQUERY(childA, $a, $a.string != NIL).#count)
childB.#count != 0 AND (0 == SUBQUERY(childB, $a, $a.string != NIL).#count)
I would expect to get back results from non-nil instances of both childA and childB only if each entity instances' string is also nil. My question is, why would one give the results that I expect; while the other does not?
Clarification:
I'm trying to solve the general problem where I'm searching for one of two things. I'm either searching for a default value in an attribute. When the attribute is optional, I'm additionally searching for a nil attribute. The problem is further compounded when optional relationships' should only be considered when the are populated. Without the relationship count != 0, I get back all parents with a nil relationship. In one case, this is the desired behavior. In another case, this appears to diminish the returned parent count (to 0 results).
For the optional attribute case, the query might look like:
parent.#count != 0 AND (parent.gender == -1) OR (parent.gender == NIL)
Where there are optional relationships in the key-paths, the query takes the form exemplified in the first example.
Again, I have gotten the results I have expected with all but one case, where there doesn't seem to be anything unique to it's relationships nor attribute characteristics. Or I should say, there's nothing unique about this exception in data model structure or query format...

Maybe you mixed up == and != in the second clause, and it should be
childA.#count != 0 AND (SUBQUERY(childA, $a, $a.string != NIL).#count != 0)
It would be clearer what you want to achieve if you could formulate the query in plain English first.
BTW, you can use expressionForSubquery:usingIteratorVariable:predicate: of class NSExpression to build the subquery for you. You might get useful error reporting more easily then.

I understand the problem now.
In my case, it's usually logical correct to first filter out NSSets with 0 count. But, in the problematic case, it's logically correct to return the results of both NSSets with 0 count, and NSSets with > 0 count where the attribute is nil (when optional), or the attribute is set to its default value. In other words, in the problematic case, the left condition needs to be removed, resulting in the following format:
(0 == SUBQUERY(childA, $a, $a.string != NIL).#count)
It seems I'll need to have the managed objects indicate which scenario is appropriate on a case by case basis...yuk!

Related

Swift - iOS NSPredicate with optional NSDecimalNumber

I'm trying to use the following NSPredicate with an NSDecimalNumber to check if a value is greater than 0.
let addPredicate:NSPredicate = NSPredicate(format:"balance > 0", argumentArray: [])
"balance" is an Optional NSDecimalNumber attribute in a Core Data entity. The result is coming up empty (no errors), even when I try balance < 0, or balance == 0. I can confirm that there are no null balance records. I suspect it is due to the "optional" nature of the attribute. Predicates comparing optional dates and NSNumber values are working with no problems.
Is there a different syntax I should be using to compare an Optional NSDecimalNumber?
The fact that it is optional should not make a difference.
There is a different syntax you can use but yours should work too.
You can try the object syntax:
NSPredicate(format:"balance > %#", argumentArray: [NSDecimalNumber.zero])
But it does exactly the same as your predicate and should return the same result.
If you tried all the options i.e. grater than 0 and lower than zero and equal to zero (the equal to zero should also return entities with nil balance!), then the most simple explanation that there are no objects at all.
I don't think that the predicate is the problem, I would suggest you to check if there is some problem creating/saving new entities or maybe they are deleted ...
Hope it helps.
Have just realised the issue was that I was attempting to use a transient attribute ("balance") in the fetch predicate. So instead I have used a filter on the fetched results and this gives the correct output.
filteredItems = (fetchedResultsController.fetchedObjects?.filter { item in
let searchResult:Bool = (item.balance!.compare(NSDecimalNumber.zero) == .orderedDescending)
return searchResult
})!

Query/double OPTIONAL MATCH issue

I think this is a long question for what's likely a simple answer. However I thought it wise to include the full context in case there's something wrong with my query logic (excuse the formatting if it's off - I've renamed the vars and it may be malformed, I need help with the theory and not the structure)
An organisation can have a sub office
(o:Organisation)-[:sub_office]->(an:Organisation)
Or a head office
(o)-[:head_office]->(ho:Organisation)
Persons in different sub offices can be employees or ex-employee
EX1
(o)-[:employee]->(p:Person{name:'person1'})<-[:ex_employee]-(an)
Persons can be related to other people through the management relationships. These management links can be variable length.
EX2
(o)-[:employee]->(p:Person{name:'person2'})-[:managed]->(p:Person{name:'person3'})<-[:ex_employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person4'})-[:managed]->(p:Person{name:'NOT_RETURNED1'})-[:managed]->(p:Person{name:'person5'})<-[:employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person6'})<-[:managed]-(p:Person{name:'NOT_RETURNED2'})<-[:managed]-(p:Person{name:'person8'})<-[:employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person9'})-[:managed]->(p:Person{name:'NOT_RETURNED4'})-[:managed]->(p:Person{name:'NOT_RETURNED5'})<-[:managed]-(p:Person{name:'person11'})<-[:employee]-(an)
....
I'm querying:
-organisation,
-sub office,
-how they're related
These are all working fine (I think...)
The issues I'm having is with returning Persons associated with the orgs (employees or ex employees) and their relationships to the organisation but only if they are connected to the other organisation directly (as in EX1) or through a managed chain (all of EX2 - I've tried to make it clearer by marking the Persons who won't be returned by the query as name 'NOT_RETURNED')
I've created the following:
MATCH (queryOrganisation:Organisation{name:'BigCorp'})-[orgRel]-(relatedOrganisation:Organisation)
WITH queryOrganisation, orgRel, relatedOrganisation
MATCH (queryOrganisation)-[employmentRel]->(queryPerson:Person)
OPTIONAL MATCH (queryPerson)<-[relatedOrgRel]-(relatedOrganisation)
OPTIONAL MATCH (queryPerson)-[:managed*1..]-(relatedPerson:Person)<-[relatedOrgRel]-(relatedOrganisation)
WITH queryOrganisation, orgRel, relatedOrganisation, employmentRel, queryPerson, relatedOrgRel, relatedPerson
WHERE NOT queryOrganisation.name = relatedOrganisation.name
RETURN ID(queryOrganisation) as queryOrganisationID,
ID(startNode(orgRel))as startNodeId, type(orgRel)as orgRel, ID(endNode(orgRel))as endNodeId,
ID(relatedOrganisation)as relatedOrganisationId, relatedOrganisation.name as relatedOrganisationName
COLLECT({
queryPerson:{endpoint:{ID:ID(queryPerson)}, endpointrelationship: type(employmentRel)},
relatedPerson:{endpoint:{ID:coalesce(ID(relatedPerson),ID(queryPerson))}, endpointrelationship:type(relatedOrgRel)}
}) as rels
I would have expected all the collected results to look like:
{
"startEmp":{
"ID":2715,
"startrelationship":"employee"
},
"relatedEmp":{
"ID":2722,
"endrelationship":"ex employee"
}
}
However the directly connected node results (same node ID) appear like:
{
"startEmp":{
"ID":2716,
"startrelationship":"employee"
},
"relatedEmp":{
"ID":2716,
"endrelationship":null
}
}
Why is that null appearing for type(relatedOrgRel)? Am I misunderstanding whats happening in the OPTIONAL MATCH and the relatedOrgRel gets overwritten by null during the second OPTIONAL MATCH? If so, how can I remedy?
Thanks
No, the OPTIONAL MATCHes cannot overwrite variables that are already defined.
I think the cause of the problems is when your second OPTIONAL MATCH doesn't match anything, but this is partially covered up by the COALESCE used in the collecting of persons in your return hides some of the conseque:
...
relatedPerson:{endpoint:{ID:coalesce(ID(relatedPerson),ID(queryPerson))}, endpointrelationship:type(relatedOrgRel)}
...
If relatedPerson is null, as it will be if your second OPTIONAL MATCH fails, then you're falling back to the id of queryPerson, but since you're not using a COALESCE for relatedOrgRel, this will still be null. You'll need a COALESCE here, or otherwise you'll need to figure out a better way to deal with the null variables in your OPTIONAL MATCHES in cases where they fail.

Comparing 2 columns in Hyperion (Oracle)

I have two columns in a query.
I have 2 fields (Field 1 and Field 2). What I want to do is:
if Field 1 is blank then use Field 2, but
if it is not blank then I want it to populate it with Field 1.
I am sure it can be done in a computed column but have been unsuccessful with the logic (so far).
"Blank" can mean a lot of things. You can try checking for the most common ones.
In a results set, this will check a few things.
if( Field_1 == null || Field_1.replace(/\s/gi,'') == '' || Field_1 == NaN )
{
Field_2;
}
else
{
Field_1;
}
First is whether your field truly is blank, and contains no data.
Hyperion results-set computed items like "null" to be all-lowercase.
Second is whether you do have data, but it's invisible. Usually,
that's empty strings, but it can also be other whitespace, so we'll
see if replacing all whitespace (not changing the data; it's just
for comparison-sake) with empty strings would yield an empty string.
/\s/gi is regex ... it means "whitespace" global (ie: every match),
case-insensitive.
Lastly (and, I've never had this be the case personally, but I've been told it can happen), check to see if there's a Not-a-Number (NaN) response. This is sort of an error that can appear in data, counts as value, but which might not display on your screen.
If you're doing this in the Query section, you'll have to write it in SQL, and have to match the syntax to your data source (Oracle, DB2, Sybase, etc), but the concept is the same.

linq query times out

my Linq query times out and the results are never retreived..
using (var repository = PersistenceService.GetRepository<Organisation>(UserId))
{
var organisation = repository.GetAll().FirstOrDefault(x => x.Id == organisationId);
if (organisation != null)
{
isCourseApproverRole = organisation.Contacts.FirstOrDefault(con => con.RoleName == "CourseApprover" &&
con.Individual.Id == individualId) != null;
}
}
When I try doing all this in one query it works fine..
Can some one explain why above query will time out??
Note: organisation.Contacts contain about 18,000 rows for the selected organisation.
It's because of massive lazy loading.
The first command...
var organisation = repository.GetAll().FirstOrDefault(x => x.Id == organisationId);
... pulls an Organisation object into memory. That shouldn't be any problem.
But then you access organisation.Contacts. It doesn't matter which LINQ method you apply to this collection, the whole collection is pulled into memory by lazy loading. The LINQ filter is applied afterwards.
However, though highly inefficient, this still shouldn't cause a timeout. Fetching 18000 records by an indexed search (I assume) shouldn't take more than 30s (I assume) unless something else is terribly wrong (like low resources, bad network).
The part con.Individual.Id == individualId is the culprit. If you would have monitored the executed SQL commands you'd have seen that this causes one query for each Individual until the predicate Id == individualId is matched. These queries run while the query organisation.Contacts is read. No doubt, this causes the timeout.
You could fix this by replacing the predicate by con.IndividualId == individualId (i.e. using the foreign key property). But I really don't understand why you don't do this in one query, which works fine, as you say. By the current approach you fetch large amounts of data, while in the end you only need one boolean value!

multiple line where clauses

I've got a search page with multiple inputs (text fields). These inputs may or may not be empty - depending on what the user is searching for.
In order to accommodate this I create a base searchQuery object that pulls in all the correct relationships, and then for each non-empty input I modify the query using the searchQuery.Where function.
If I place multiple conditions in the WHERE clause I get the following error:
Cannot compare elements of type 'System.Collections.Generic.ICollection`1'. Only primitive types, enumeration types and entity types are supported.
searchQuery = searchQuery.Where(Function(m) (
(absoluteMinimumDate < m.ClassDates.OrderBy(Function(d) d.Value).FirstOrDefault.Value) _
OrElse (Nothing Is m.ClassDates)
)
)
I know that code looks funky, but I was trying to format it so you didn't have to scroll horizontally to see it all
Now, if I remove the ORELSE clause, everything works (but of course I don't get the results I need).
searchQuery = searchQuery.Where(Function(m) (
(absoluteMinimumDate < m.ClassDates.OrderBy(Function(d) d.Value).FirstOrDefault.Value)
)
)
This one works fine
So, what am I doing wrong? How can I make a multi-condition where clause?
Multiple conditions in the Where isn't the problem. m.ClassDates Is Nothing will never be true and doesn't make sense in SQL terms. You can't translate "is the set of ClassDates associated with this record NULL?" into SQL. What you mean is, are there 0 of them.
If there are no attached ClassDate records, m.ClassDates will be an empty list. You want m.ClassDates.Count = 0 OrElse...

Resources