Stream: implementers
Topic: fluentpath
Grahame Grieve (Apr 02 2016 at 04:57):
I have some questions about fluent path based on implementing it
Grahame Grieve (Apr 02 2016 at 04:59):
when does $this apply? can we say that $this is only available in a function when evaluating parameters, and then only as the proximal value in a parameter, and when you encounter it, it's the object on which the function is being evaluated
Grahame Grieve (Apr 02 2016 at 05:00):
but that means that it's only the iterator functions (where, select, all)
Bryn Rhodes (Apr 02 2016 at 05:00):
That was my understanding, it would only be available within the iterators.
Grahame Grieve (Apr 02 2016 at 05:01):
k. I'll implement it so. And I'll try and remember to make a ballot comment that we need to be more specific anbout this
Bryn Rhodes (Apr 02 2016 at 05:01):
I'll make a note as well.
Grahame Grieve (Apr 02 2016 at 05:10):
another thing I haven't figured out - how can is and as be operators?
Grahame Grieve (Apr 02 2016 at 05:10):
I've found that I need special handling for the function parameters for is and as because they are evaluated in the type space, not the instance space.
Grahame Grieve (Apr 02 2016 at 05:11):
but when they'er operators, I don't know how to do that because I process the operands before the operation.
Grahame Grieve (Apr 02 2016 at 05:11):
thoughts?
Bryn Rhodes (Apr 02 2016 at 05:14):
Yeah, they're more like generic operators in that they have a compile-time resolve.
Bryn Rhodes (Apr 02 2016 at 05:15):
What we do in the CQL-to-ELM translator is resolve the type at compile-time and save it in the object model.
Grahame Grieve (Apr 02 2016 at 05:15):
hmm. I hadn't thought of it like that. but that implies that there has to be a compile step
Bryn Rhodes (Apr 02 2016 at 05:16):
Well, either that or your run-time has to have some way to resolve a type.
Bryn Rhodes (Apr 02 2016 at 05:16):
I've implemented it both ways.
Bryn Rhodes (Apr 02 2016 at 05:16):
Do you have a context you could use at run-time to reach into the type model?
Grahame Grieve (Apr 02 2016 at 05:17):
well, it's not so much that I can resolve a type or not, but that I have to know that I'm in the type space not the instance space
Bryn Rhodes (Apr 02 2016 at 05:18):
Yeah I see that's special case code because of the way the FluentPath grammar is built then.
Grahame Grieve (Apr 02 2016 at 05:19):
should we note that?
Bryn Rhodes (Apr 02 2016 at 05:19):
You basically have to say, if this is an "is" or "as", remember that the operand is an type.
Bryn Rhodes (Apr 02 2016 at 05:20):
So, for the "is" and "as" expressions (where they are keywords), we do have a different production rule.
Bryn Rhodes (Apr 02 2016 at 05:20):
typeSpecifier
Bryn Rhodes (Apr 02 2016 at 05:20):
But for ".is()" and ".as()", we don't specify those in the grammar at all, so I'm not sure how we would handle that.
Grahame Grieve (Apr 02 2016 at 05:21):
we say that in the definition of the functions. they were easier for me
Bryn Rhodes (Apr 02 2016 at 05:21):
We could introduce specific rules for just those invocations.
Bryn Rhodes (Apr 02 2016 at 05:21):
Or....
Bryn Rhodes (Apr 02 2016 at 05:21):
we could introduce a typeof keyword...
Bryn Rhodes (Apr 02 2016 at 05:22):
Then Type would be a type and you could declare the .is() and .as() operators to take arguments of type Type.
Bryn Rhodes (Apr 02 2016 at 05:22):
(formatted for clarity)
Grahame Grieve (Apr 02 2016 at 05:23):
typeof is a keyword in the definitions or the language? it seems retrograde to have to specify it in is() and as()
Grahame Grieve (Apr 02 2016 at 05:23):
and I can see how to solve it for me - it's similar to the notion of short cut boolean evaluation
Grahame Grieve (Apr 02 2016 at 05:23):
and I do it a the same point
Bryn Rhodes (Apr 02 2016 at 05:24):
Keyword or method, either way would work.
Grahame Grieve (Apr 02 2016 at 05:24):
well, let's just make sure we ballot about it
Bryn Rhodes (Apr 02 2016 at 05:24):
But if you have a solution, we can just note it as something to add clarifying documentation around.
Bryn Rhodes (Apr 02 2016 at 05:24):
Yep.
Grahame Grieve (Apr 02 2016 at 05:24):
no point wasting cycles solving it now. it might be solved with better doco, or it might need something more. we can decide later
Grahame Grieve (Apr 02 2016 at 05:25):
for now, I know how to implement it, but it is kind of unexpected
Bryn Rhodes (Apr 02 2016 at 05:26):
Agreed.
Grahame Grieve (Apr 03 2016 at 04:30):
I have a problem with '-'. Take this expression:
Grahame Grieve (Apr 03 2016 at 04:31):
Patient.name.given.where(substring($this.length()-3) = 'out')
Grahame Grieve (Apr 03 2016 at 04:31):
here, - is an operator.
Grahame Grieve (Apr 03 2016 at 04:31):
but take this expression:
Grahame Grieve (Apr 03 2016 at 04:31):
Patient.name.given.count() > -3
Grahame Grieve (Apr 03 2016 at 04:32):
- here is part of the constant.
Grahame Grieve (Apr 03 2016 at 04:32):
I don't know how to tell these apart in my lexer
Brian Postlethwaite (Apr 03 2016 at 04:38):
Could be wrong, I didn't do the stuff in the .net implementation, but could have a look over what Ewout did.
3 tokens at the end of the stream ">", "-", "3"
So you have the "-" token inbetwen an operator and a constant, so it should merge it into the const token.
Grahame Grieve (Apr 03 2016 at 04:39):
see, that breaks the separation between the lexer and the grammar
Brian Postlethwaite (Apr 03 2016 at 04:46):
Yes, tokenizer breaks into the words, then the lexer needs to convert the tokens into correct tree for execution. In this case it is merging those 2 tokens into the constant as the substract operator when following a comparison operator.
I haven't gone that close into the grammar to see how that fits into it.
Brian Postlethwaite (Apr 03 2016 at 04:46):
(I have been meaning to do that to review Ewout's implementation though)
Grahame Grieve (Apr 03 2016 at 05:03):
I'll have to figure that out, I guess
Grahame Grieve (Apr 03 2016 at 05:03):
a question:
Grahame Grieve (Apr 03 2016 at 05:03):
'12345'.startsWith('') = false
Grahame Grieve (Apr 03 2016 at 05:03):
true? or false?
Brian Postlethwaite (Apr 03 2016 at 05:10):
empty string?
Brian Postlethwaite (Apr 03 2016 at 05:11):
good question, would also be good question for if the string was null/not found
Brian Postlethwaite (Apr 03 2016 at 05:11):
maybe that's something that should be defined by the startswith operator
Grahame Grieve (Apr 03 2016 at 06:01):
that's why I'm asking. Clearly, if it's empty for some reason, then the string can't start with it. but the empty string is different.
Grahame Grieve (Apr 03 2016 at 06:01):
it's not allowed in fhir btw
Brian Postlethwaite (Apr 03 2016 at 06:02):
but a null is.
Grahame Grieve (Apr 03 2016 at 07:34):
Another problem in the spec: the type datetime should be dateTime
Brian Postlethwaite (Apr 03 2016 at 08:28):
the only date functions are today() and now(), I think we need some date manipulation functions also.
Lots of validations have date relative features. But I guess that can be added later e.g. dateAdd(month, 12)
Bryn Rhodes (Apr 03 2016 at 18:54):
Regarding the unary minus, that should come back from the parser as a unary expression. The Antlr visitor will just do that, are you not using an Antlr generated visitor?
Bryn Rhodes (Apr 03 2016 at 19:00):
Regarding '12345'.startsWith(''), startsWith is usually defined in terms of a substring of the same length, which means startsWith('') would return true.
Bryn Rhodes (Apr 03 2016 at 19:00):
I'll note that we need to clarify the behavior.
Grahame Grieve (Apr 05 2016 at 00:19):
I'm not using an Antlr grammar
Grahame Grieve (Apr 05 2016 at 00:19):
I don't like the way all() is defined.
Grahame Grieve (Apr 05 2016 at 00:20):
it should take a criteria - does all this collection meet this criteria?
Grahame Grieve (Apr 05 2016 at 00:21):
as it is, you have to do select(criteria).all() - but the selection does - or could - drop the cases that don't meet the criteria, so .all() will be true.
Grahame Grieve (Apr 05 2016 at 00:21):
it's a lot easier if it's .all(criteria)
Grahame Grieve (Apr 06 2016 at 23:30):
consider this invariant: entry.where(fullUrl).select(fullUrl+resource.meta.versionId).distinct()
Grahame Grieve (Apr 06 2016 at 23:31):
if versionId is present, it contributes to the assessment of null. but it might not be. So this invariant fails because:
Grahame Grieve (Apr 06 2016 at 23:32):
"The math operators require each operand to be a single element. Both operands must be of the same type, each operator below specifies which types are supported."
Grahame Grieve (Apr 06 2016 at 23:32):
but resource.meta.versionId might not be present
Grahame Grieve (Apr 06 2016 at 23:32):
at present the only way I could approach this is:
Grahame Grieve (Apr 06 2016 at 23:32):
entry.where(fullUrl).select(fullUrl+iif(resource.meta.versionId, resource.meta.versionId, '')).distinct()
Grahame Grieve (Apr 06 2016 at 23:33):
that's painful.
Grahame Grieve (Apr 06 2016 at 23:33):
I think I need to bring back & operator
Grahame Grieve (Apr 07 2016 at 04:56):
I have brought it back for FHIR.
Grahame Grieve (Apr 07 2016 at 04:56):
it's just too finicty to deal with the stupid rules aorund plus that make sense for integers, but not strings
Brian Postlethwaite (Apr 08 2016 at 05:07):
Not sure I follow this last one, plus for integers, not strings?
Is the & the concatenation for strings instead of +?
Grahame Grieve (Apr 11 2016 at 04:19):
in my implementation, you can do string + String
Grahame Grieve (Apr 11 2016 at 04:20):
but if either string is missing, it's an error, according to the rules for +, since they are type safe
Grahame Grieve (Apr 11 2016 at 04:20):
& is not type safe - it simply appends left and right as strings together, and ignores missing parameters
Grahame Grieve (Apr 11 2016 at 04:21):
I have 2 differences between my implementation and the spec after ignoring the little details above in this topic:
- I have the & operator
- I allow a selector on the .all() operation
Grahame Grieve (Apr 11 2016 at 04:22):
hopefully I'll be upgrading both my DSTU2 and Montreal servers to use current fluent path in the next few hours - just doing testing now
Bryn Rhodes (Apr 11 2016 at 17:11):
@Grahame Grieve: "but if either string is missing, it's an error, according to the rules for +, since they are type safe" It's not an error, it's just that the result is empty. In CQL we define a _coalesce_ operator that collapses the .iif() logic you have above. So with that operator it would be:
Bryn Rhodes (Apr 11 2016 at 17:11):
entry.where(fullUrl).select(fullUrl + resource.meta.versionId.coalesce('')).distinct()
Bryn Rhodes (Apr 11 2016 at 17:13):
Having said that, I do like the notion of defining a string concatentation operator that is different than +, and having it treat empty differently would simplify things even further. But I would still propose the need for something like a coalesce operator.
Bryn Rhodes (Apr 11 2016 at 17:13):
I'll note the & operator in the FluentPath comments.
René Spronk (Apr 11 2016 at 17:52):
I've asked Bryn to speak about CQL (using FluentPath) at the upcoming WGM in Montreal. It would be nice to also have a speaker about FluentPath and its implementation aspects. Any volunteers? We need to spread the word about these two topics somewhat, most in the FHIR user comunity won't have heard about them..
Brian Postlethwaite (Apr 12 2016 at 13:55):
Looking to see if there has been any progress on date manipulation operations/functions.
My use cases are for comparing dates 6 or 12 months in past/future, both for validation and defaulting values (in my case with quesitonnaires)
Bryn Rhodes (Apr 12 2016 at 15:21):
The current FluentPath specification does not define date/time arithmetic. CQL defines a complete set though, so we have two options 1) incorporate the date/time arithmetic operations, or 2) use full CQL to express your use case.
Grahame Grieve (Apr 12 2016 at 19:16):
I think that the preference will be 1, but do we need a complete set?
Bryn Rhodes (Apr 12 2016 at 19:45):
We can start with just the date arithmetic definitions to support this particular use case.
Bryn Rhodes (Apr 12 2016 at 19:48):
so +(dateTime, quantity), +(time, quantity), -(dateTime, quantity), -(time, quantity)
Grahame Grieve (Apr 12 2016 at 20:02):
why would we not use + / - for this?
Bryn Rhodes (Apr 12 2016 at 20:21):
Agreed, that is the syntax CQL uses.
Brian Postlethwaite (Apr 12 2016 at 21:01):
Is it documented how to express a const quantity? do I can represent 1d or -6m or +1y
Grahame Grieve (Apr 12 2016 at 21:02):
I don't think it is, but given the complexity, I would define a function....
Brian Postlethwaite (Apr 12 2016 at 21:30):
something like a Quantity resource constructor?
Grahame Grieve (Apr 12 2016 at 21:53):
quantity(value, unit) or quantity(value, unit, system code)
Bryn Rhodes (Apr 12 2016 at 21:54):
CQL defines a quantity selector, but we did not define that in FluentPath. We talked about adding it, but never reached a decision.
Bryn Rhodes (Apr 12 2016 at 21:54):
Nope, I take it back.
Bryn Rhodes (Apr 12 2016 at 21:54):
It's in the grammar, just not documented in the md.
Bryn Rhodes (Apr 12 2016 at 21:55):
The syntax in the grammar is <NUMBER unit?>
Bryn Rhodes (Apr 12 2016 at 21:56):
Where unit is either a singular or plural date time precision (year(s), month(s), day(s), hour(s), minute(s), second(s), millisecond(s)), or a UCUM unit as a string.
Bryn Rhodes (Apr 12 2016 at 21:56):
So you can say:
Bryn Rhodes (Apr 12 2016 at 21:56):
6 months
Bryn Rhodes (Apr 12 2016 at 21:56):
or
Bryn Rhodes (Apr 12 2016 at 21:56):
6 'mo'
Bryn Rhodes (Apr 12 2016 at 21:56):
I'll add a comment to document that in the md.
Grahame Grieve (Apr 12 2016 at 21:57):
but see, this doesn't deal with questions around the units
Bryn Rhodes (Apr 12 2016 at 22:15):
The simplest implementation is to just require that units be respected. A more sophisticated implementation could do conversion where possible.
Bryn Rhodes (Apr 12 2016 at 22:16):
For date/time arithmetic, it's fairly straightforward, and I imagine most implementations would just pass through to an underlying library to perform the computation.
Bryn Rhodes (Apr 12 2016 at 22:16):
For quantity arithmetic, the implementation just needs to ensure that if the values have units, they're the same, or it's an error.
Grahame Grieve (Apr 12 2016 at 22:17):
well, which units? we should use ucum units if we're going to have a simple form
Bryn Rhodes (Apr 12 2016 at 22:17):
Yeah, the expectation is UCUM units.
Grahame Grieve (Apr 12 2016 at 23:13):
that's not what you said above
Grahame Grieve (Apr 12 2016 at 23:14):
and I don't understand how that's not grammatically ambiguous
Bryn Rhodes (Apr 13 2016 at 01:36):
For the date/time precisions specifically, CQL defines keywords that are synonyms for the UCUM date/time precision.
Bryn Rhodes (Apr 13 2016 at 01:42):
As far as ambiguity, there's no other context where an expression is allowed to be followed by a literal like that. It requires lookahead, but I don't think it's ambiguous.
Grahame Grieve (Apr 13 2016 at 06:03):
regrding ambiguity, some ucum codes are ambiguous - e.g. *10
Grahame Grieve (Apr 21 2016 at 13:38):
So, still working on fluent path quallity....
Grahame Grieve (Apr 21 2016 at 13:40):
Lloyd has this series of constraints on Questionnaire Extensions:
Grahame Grieve (Apr 21 2016 at 13:40):
valueInteger = 0 or $parent.required valueInteger <= 1 or $parent.repeats $parent.type!='display' $parent.type!='display' $parent.type='choice' or $parent.type='open-choice' $parent.type='choice' or $parent.type='open-choice' $parent.type!='display' $parent.type='reference' $parent.type='display' $parent.type='reference' $parent.type!='display' $parent.type='reference' $parent.type='integer' or $parent.type='decimal' $parent.type='integer' or $parent.type='decimal'
Grahame Grieve (Apr 21 2016 at 13:41):
these are all illegal, and not implementable under the current spec
Grahame Grieve (Apr 21 2016 at 13:41):
it's not obvious to me how this should be resolved
Lloyd McKenzie (Apr 21 2016 at 13:47):
So the general use-case is the need to be able to make assertions about the context in which an extension is allowed to appear
Bryn Rhodes (Apr 21 2016 at 14:10):
Can they be expressed on the parent node in terms of the children instead of on the children in terms of the parent?
Lloyd McKenzie (Apr 21 2016 at 14:37):
No because their defined on the extension - and are trying to further constrain the type of node they're allowed to appear on.
Bryn Rhodes (Apr 21 2016 at 14:42):
I see, so it's a context invariant. The validation is already doing that with extensions, so it seems like it would work if we had a way to say 'this is a context invariant, evaluate it in terms of the container to which the extension is being applied', rather than 'this is an invariant, evaluate it in terms of the extension'.
Grahame Grieve (Apr 21 2016 at 20:01):
I think that makes sense
Grahame Grieve (Apr 21 2016 at 22:26):
@Bryn Rhodes : according to fluentpath, .length() can only be used for strings. but we have constraints on date lengths - lots of them - and you could imagine lengths being imposed for decimals and integers in some cases. so I think length() should be applicable to primitives, not strings
Grahame Grieve (Apr 21 2016 at 22:27):
and do uris, codes, etc count as strings? avoid that question....
Bryn Rhodes (Apr 22 2016 at 14:26):
@Grahame Grieve So for length constraints on decimals and integers, why not express it as a value constraint? value between 1 and 1000, e.g.
Bryn Rhodes (Apr 22 2016 at 14:27):
for length constraints on date/times, wouldn't it be better to express it in terms of precision? (i.e. what does date.length() < 4 mean?)
Bryn Rhodes (Apr 22 2016 at 14:28):
And for uris, codes, yes, I think those are treated as strings. In the CQL implementation, I'm defining implicit conversions from the FluentPath/CQL primitives to the FHIR types, that should make it totally transparent to the user.
Lloyd McKenzie (Apr 22 2016 at 14:37):
Sometimes you want to enforce a pure length. So "-50" and "100" and "2.3" all have a length of "3". That said, that's a fairly low frequency use-case.
Bryn Rhodes (Apr 22 2016 at 14:50):
I think in that case, you could always do a value.toString().length() = 3
Lloyd McKenzie (Apr 22 2016 at 17:00):
That works for me
Grahame Grieve (Apr 22 2016 at 19:29):
well, ok, we could consider something more direct in terms of dates. Precision of dates is particularly frequent thing in constraints.
Grahame Grieve (Apr 22 2016 at 19:29):
but what would that look like?
Grahame Grieve (Apr 25 2016 at 18:48):
Bryn - I didn't join the ballot pool for FluentPath - can you submit all my comments?
Bryn Rhodes (Apr 26 2016 at 16:46):
Sure, I've tracked them, I'll get them submitted.
Bryn Rhodes (Apr 26 2016 at 16:50):
Regarding the constraints on date precision, within CQL, we would say:
Bryn Rhodes (Apr 26 2016 at 16:50):
month from DatetimeValue is null
Bryn Rhodes (Apr 26 2016 at 16:51):
To enforce that a date/time could only be specified to a year, for example.
Bryn Rhodes (Apr 26 2016 at 16:51):
I imagine the FluentPath for that would look like:
Bryn Rhodes (Apr 26 2016 at 16:51):
DateTimeValue.month().isNull()
Grahame Grieve (Apr 26 2016 at 17:07):
.exists(). but we haven't defined a date time that works like that.
Grahame Grieve (Apr 26 2016 at 17:07):
it's a lot harder to convert to real world constraints like XPath too
Ewout Kramer (Apr 27 2016 at 13:44):
So, I missed most of this thread because I must have misclicked in Zulip and it was all marked as read :-(
That was my understanding, it would only be available within the iterators.
I think what is the matter is that in some functions we're not passing VALUES as parameters (like the length into the substring() function), but we're passing functions as parameters, which are lazyly evaluated at the right moment by the function that is called. So, what happens with all() and any() is not that they are iterators, it's that they are functions that are passed (anonymous) functions (or in FP expressions that are lazily evaluated).
So, internally, in my AST after parsing, these functions receive a parameter that's a lambda expression, with $this as the single parameter. That would be compatible with just allowing $this in these iterators (though currently, $this works everywhere in the expressions on .NET)
Grahame Grieve (Apr 27 2016 at 13:45):
but it's not definable what $this actually is in any other case
Ewout Kramer (Apr 27 2016 at 13:48):
Informally, it's the outcome of the last statement before the "." invocation. Even if we would not allow it everywhere, it's present everywhere. I have assumed that:
Patient.name.substring(length()-4,2) is actually Patient.name.substring($this.length()-4,2). Isn't that equivalent? So you need to have $this available everywhere anyway?
Grahame Grieve (Apr 27 2016 at 13:49):
I could only read Patient.name.substring(length()-4,2) to mean that length refers to the collection of names from Patient.name
Ewout Kramer (Apr 27 2016 at 13:59):
Right. Which in my interpretation means $this refers to the collection of names from Patient.name. Removing $this means removing the possibility for the user to refer back to that resultset. Why would we do that?
Grahame Grieve (Apr 27 2016 at 14:00):
I don't understand. allowing $this implies that there's some other choice
Grahame Grieve (Apr 27 2016 at 14:00):
and, in fact, that's how I found it being used
Grahame Grieve (Apr 27 2016 at 14:00):
trying to refer a step further back
Ewout Kramer (Apr 27 2016 at 14:01):
Yeah I see that's special case code because of the way the FluentPath grammar is built then.
It's turned into a separate AST node type indeed, since it's so special. I was reassured when I saw the .NET LINQ compiler treats these kind of nodes as a special case as well: https://msdn.microsoft.com/en-us/library/system.linq.expressions.typebinaryexpression(v=vs.110).aspx
Ewout Kramer (Apr 27 2016 at 14:02):
(from the doc: TypeBinaryExpression Represents an operation between an expression and a type)
Ewout Kramer (Apr 27 2016 at 14:07):
Grahame Grieve: I don't like the way all() is defined.
Grahame Grieve: it should take a criteria - does all this collection meet this criteria?
Agree. I thought it was defined that way, I even put an example in the text that suggest it does ;-)
Grahame Grieve (Apr 27 2016 at 14:07):
yeah. Bryn defined it differently, but I just treated it as if it took a criteria throughout the FHIR invariants
Ewout Kramer (Apr 27 2016 at 14:08):
Regarding '12345'.startsWith(''), startsWith is usually defined in terms of a substring of the same length, which means startsWith('') would return true.
For what it's worth, the .NET StartsWith() function would return 'true' in this case.
Ewout Kramer (Apr 27 2016 at 14:13):
I think I need to bring back & operator
So, this would the first case where an operator does NOT propagate empty..I worry that this would be a source of confusion, though I do understand why you'd do it. Maybe coalesce() would be a better option.
Grahame Grieve (Apr 27 2016 at 14:13):
that just moves the deck chairs
Grahame Grieve (Apr 27 2016 at 14:14):
but no, it's not about propgating empty - I don't use it on {} and expect to get anything but {}
Ewout Kramer (Apr 27 2016 at 14:16):
But it is about propagating empty. We've defined ALL our operators to propagate empty: 3+{}={}, "adfadfs" + {} = {}. Now we want "asdfadf" + {} = "asdfadf". So the new & would NOT propagate empty anymore. Or would convert empty to '' automatically if you want. I think that is confusing.
Grahame Grieve (Apr 27 2016 at 14:24):
well, ok. but it's absolutely what is needed - to swallow nothing into nothing in the string. You might tihnk it's confusing, but try writing an expression without it - stringing your iif()s together... yuck
Brian Postlethwaite (Apr 27 2016 at 21:48):
Ah the joys of timezones.
We added the today/now expression in fluentpath (great).
My new question is how do we communicate the intended timezone for evaluation?
The expected answer is "the person who is calling the operation"
But what about when the server and client are in different timezones, how is this offset handled? Can we have a standard way to communicate this to the Evaluation engines?
Set of parameters that can be provided to the evaluator for execution.
Ewout Kramer (Apr 28 2016 at 02:29):
Brian can you explain to me what the intended timezone for evaluation is? Is that the preferred timezone the datetimes are expressed in? As longs as each of the instances of dateTime's has a timezone when the hour component is there, what's the use of passing in the "actual" timezone?
Brian Postlethwaite (Apr 28 2016 at 03:30):
the today or now functions.
Brian Postlethwaite (Apr 28 2016 at 03:30):
rest of them, yes you're right.
Brian Postlethwaite (Apr 28 2016 at 03:31):
actually, really only today.
Brian Postlethwaite (Apr 28 2016 at 03:31):
comparisons should be ok where there are all timezones. But when only considering the date part, the timezone is important.
Grahame Grieve (Apr 28 2016 at 06:19):
no I don't follow
Brian Postlethwaite (Apr 28 2016 at 06:22):
consider a fhirpath validation rule that says
Appointment.Start >= today()
Brian Postlethwaite (Apr 28 2016 at 06:23):
today evaluates to 28 April 2016
(here in australia) but in USA this might evaulate to 27 April 2016
and thus rule will fail.
Brian Postlethwaite (Apr 28 2016 at 06:24):
So the zone where the evaluation of the rule occurs makes a difference here.
Brian Postlethwaite (Apr 28 2016 at 06:24):
(This is basically the rule that we have failing)
Grahame Grieve (Apr 28 2016 at 06:25):
this is a question about your pre-conditions. If you're actually running this about Australia, then that's correct.
Brian Postlethwaite (Apr 28 2016 at 06:26):
Our deployment is Server in Melbourne, clients in Adelaide and Perth. So part of the day has issues submitting forms.
Grahame Grieve (Apr 28 2016 at 06:27):
well, you can't make a rule that's so simple as appointment.start > today - the rule itself lacks expressivity
Grahame Grieve (Apr 28 2016 at 06:27):
appointment.start.universal() >= today().universal()
Brian Postlethwaite (Apr 28 2016 at 06:39):
(in the case I'm working with both properties are dates)
Brian Postlethwaite (Apr 28 2016 at 06:39):
In the questionnaire
Grahame Grieve (Apr 28 2016 at 06:41):
so?
Brian Postlethwaite (Apr 28 2016 at 06:41):
The complete rule that I can't express is
today() < date1 < date2 < today().add(6, months)
Grahame Grieve (Apr 28 2016 at 06:41):
why can't you express that?
Brian Postlethwaite (Apr 28 2016 at 06:42):
Can't add the months part at the end, and the timezone issue with evaulating today()
Brian Postlethwaite (Apr 28 2016 at 06:42):
apart from that, is ok.
Grahame Grieve (Apr 28 2016 at 06:42):
well, the general issue is: need date and time functions on the date/time class
Brian Postlethwaite (Apr 28 2016 at 06:43):
yes, and the zone for evaluating today, and I'd be happy
Grahame Grieve (Apr 28 2016 at 06:47):
I don't follow about the timezone for today bit, What's that got to do with the expression?
Brian Postlethwaite (Apr 28 2016 at 06:54):
if the execution is done in a different timezone to the client, then the value may be different
Grahame Grieve (Apr 28 2016 at 06:54):
what's that got to do with the expresssion?
Brian Postlethwaite (Apr 28 2016 at 06:55):
Expression is correct.
Brian Postlethwaite (Apr 28 2016 at 06:56):
evaluation isnt
Elliot Silver (Apr 28 2016 at 18:19):
Interesting. We had this same discussion in IHE yesterday related to XDS submission/queries.
How do I find encounters handled by the night shift (not necessarily during my night, but during the night where it occured)? Documents submitted in the morning? The TZ offset at the querying client isn't necessarily the same as the offset at the server, or the offset at the system that submitted the resource. And, the time zone in effect at any of those today, isn't necessarily the same as the offset in effect when the resource was submitted.
Brian Postlethwaite (Apr 29 2016 at 04:15):
Has also found a bug in the dotnet fluentpath implementation...
We cache the execution tree to reduce the amount of re-parses are required.
And then the first execution turns the today() into the value, and is not exectuted again, even when the value changes - which is worse when implementing the now() function.
Bryn Rhodes (Apr 29 2016 at 04:36):
So, in the CQL definitions of Today() and Now(), we define them to return the same value for a given "evaluation", and we define them to return based on the "evaluation request time", which is left somewhat open, but would typically be tied to the request date time (or an "evaluateAsOf" parameter if you wanted to support evaluation as of a certain point in time).
Bryn Rhodes (Apr 29 2016 at 04:37):
This way the engine can support caching within any given evaluation, but not across them, and the expression result is guaranteed to be deterministic.
Brian Postlethwaite (Apr 30 2016 at 00:30):
Question to others that are implementing and/or testing this.
Should these 2 expressions parse and evaluate correctly?
1 < 2
1 < -2
+1 < +2
Brian Postlethwaite (Apr 30 2016 at 00:31):
The dotnet fluentpath evaluator doesn't handle the inclusion of the negative (or positive)
Brian Postlethwaite (Apr 30 2016 at 00:32):
or this one
-1<2
Brian Postlethwaite (Apr 30 2016 at 00:35):
the notepad++ fhir addin really doesn't like these either.
Bryn Rhodes (Apr 30 2016 at 00:44):
The FluentPath grammar supports them, and the CQL/FluentPath translator I'm working on handles them, but I don't think the other implementations are using the generated Antlr visitors. And IIRC, the initial FhirPath used to treat unary negation as part of the number literal rather than as a separate syntax, so that could be holdover behavior.
Grahame Grieve (Apr 30 2016 at 00:54):
the notepad++ plug-in should accept them. I'll add tests for them. I would have thought that negation is ok but not +
Brian Postlethwaite (Apr 30 2016 at 01:03):
I wouldn't be fussed by the absense of the +
I found it testing an implementation of an experimental dateadd('mm', -6) function with the dotnet client
Grahame Grieve (Apr 30 2016 at 01:10):
fixed in both the java and pascal implemnetations. will release updates to notepad++ and my server soon
Brian Postlethwaite (Apr 30 2016 at 01:12):
Thanks Grahame, I'll look forward to testing it out.
Grahame Grieve (May 02 2016 at 02:25):
@Bryn Rhodes is it too late to add ballot comments on fluentpath?
Lloyd McKenzie (May 02 2016 at 02:46):
Close is tomorrow, so shouldn't be
Bryn Rhodes (May 02 2016 at 03:01):
Hi Grahame, nope, if you get them to me now I can still add them in.
Grahame Grieve (May 02 2016 at 03:04):
2 FHIR specific additions:
- we need a concise way to say 'no modifier extensions except for these ones'. Right now I'm not sure how I'd actually do that one
- we need a .slice(structure, name) operation that allows an author to select a particular slice as defined in the referenced structure definition
Bryn Rhodes (May 02 2016 at 03:26):
Captured.
Grahame Grieve (May 02 2016 at 06:38):
thanks
Brian Postlethwaite (May 02 2016 at 06:44):
I'd also like to propose a date based function "dateadd" that takes 2 parameters
1. datepart (yy, mm, dd, hh, mi, ss) as a string (happy if other literals are used, pulled these from TSQL)
2. value (the number of units to add to that part of the date)
This will then permit the creation of an invariant like:
valueDate < today().dateadd("mm", 6)
To cover a business rule for things like must be reviewed within 6 months.
Grahame Grieve (May 02 2016 at 06:45):
we've got - generally - the need for date operations/operators in the ballot comments
Brian Postlethwaite (May 02 2016 at 06:47):
Just wanted to be sure someone had put in a ballot comment for it.
Brian Postlethwaite (May 02 2016 at 06:47):
The above is what I have put in as an experimental while I wait for an official function.
James Agnew (May 02 2016 at 15:20):
The FP for Patient:deceased
seems decidedly wrong: Patient.deceased.exists()
since this returns true
if the value of Patient.deceased is false
What's the right way to report this? Tracker?
James Agnew (May 02 2016 at 18:09):
Hmm, I could swear I posted in this topic already. What's going on here? Oh well, take two.. From the latest SVN trunk:
Patient:deceased specifies the path Patient.deceased.exists()
which seems wrong, since it will evaluate to true
if the patient has a populated value of false
Practitioner:phone specifies the path Practitioner.telecom.where(system='email') or Practitioner.practitionerRole.telecom.where(system='email')
, which evaluates to a boolean instead of two separate expressions. Should that or
be a |
like other "joined" expressions use?
James Agnew (May 02 2016 at 18:19):
oh... now both posts are here. sorry, Zulip is being flaky on me.
Grahame Grieve (May 02 2016 at 18:24):
"The FP for Patient:deceased " - what FP? invariant? index?
Grahame Grieve (May 02 2016 at 18:24):
yes the second should be |
James Agnew (May 02 2016 at 20:01):
Oh sorry, for deceased
search parameter
James Agnew (May 02 2016 at 20:05):
The problem of course being that with the way that FP is written, a search for Patient?deceased=true
will match any patient that has a value for Patient.deceased, whether that value is true or false.
Grahame Grieve (May 02 2016 at 20:36):
yes that is wrong
James Agnew (May 02 2016 at 21:59):
Would these require a tracker item, or can I just fix them?
Grahame Grieve (May 02 2016 at 22:28):
I think you can just fix them and use tracker....
Grahame Grieve (May 02 2016 at 22:30):
nah can't find it
Bryn Rhodes (May 02 2016 at 22:45):
@Brian Postlethwaite the comment we have so far is to add +(DateTime, Quantity), -(DateTime, Quantity), +(Time, Quantity) and -(Time, Quantity).
Bryn Rhodes (May 02 2016 at 22:46):
So for your example that would look like valueDate < today().add(6 'mo')
Brian Postlethwaite (May 02 2016 at 23:08):
as long as the quantity can be defined/described to cover each of the components in the date, then all good.
Brian Postlethwaite (May 02 2016 at 23:09):
(You can tag me as someone interested in that ballot item if there's a way to do that too)
Grahame Grieve (May 07 2016 at 09:19):
sub-topic: reflection in fluent path
Grahame Grieve (May 07 2016 at 09:20):
one of the issues in the fluent path ballot is that we need to be able to access the definitions of the element in an expression
Grahame Grieve (May 07 2016 at 09:21):
in FHIR, the idea is that you'd do something like:
Observation.value.definition().max - .definition() would return a handle to an ElementDefinition, which you can navigate like any other object.
Grahame Grieve (May 07 2016 at 09:21):
I'm not sure what you'd get from .definition in non-FHIR contexts
Grahame Grieve (May 07 2016 at 09:23):
but the really interesting thing you want to do here is reason from the semantics in the definition - say, Snomed CT mappings. And to do this in a cross-profile way. E.g. you could define a base (explicit) profile that defines a constraint, and have the constraint refer to a definition that's found in a dictionary rather than an explicit profile (see the HSPC lab profile for an example of this)
Grahame Grieve (May 07 2016 at 09:24):
so the natural question that follows is, .definition() refers to the base definition of the spec, but that's not the really useful definition to get hold of. I want the one of the definitions in one of the applicable profiles... but how do I ask for that?
Ewout Kramer (May 08 2016 at 13:33):
Maybe the "as" operator should take a url, so you can "cast" to a specific profile....
Ewout Kramer (May 08 2016 at 13:34):
By the way, I see this in a lot of examples:
Patient.name.empty().not()
There is however a short-cut in the current version of FP:
Patient.name.exists()
Grahame Grieve (May 08 2016 at 13:35):
where do these come from? I thought I want through and replaced all of those
Ewout Kramer (May 08 2016 at 13:36):
I saw them in an example you showed yesterday, but I also see others use this on the Zulip chats... (so it was not specifically about the fp's in the spec)
Grahame Grieve (May 08 2016 at 13:36):
ok
Josh Mandel (May 12 2016 at 15:22):
From http://hl7-fhir.github.io/fluentpath
Patient.contained.all($this is Patient implies age > 10)
Is age
a thing?
Josh Mandel (May 12 2016 at 15:28):
repeat(projection: expression) : collection
Does repeat here mean something like "recurse" (i.e. repeat the procedure on its own output)?
Grahame Grieve (May 12 2016 at 15:30):
age is not a thing. where did you get that from?
Grahame Grieve (May 12 2016 at 15:30):
and yes, that's what it means
Josh Mandel (May 12 2016 at 15:30):
I got that from the URL I cited before my quotation :p
Josh Mandel (May 12 2016 at 15:31):
Does fluentpath express data math enough to say "Patients over 20 years old"? I assume yes but I couldn't figure out how.
Josh Mandel (May 12 2016 at 15:31):
now()
seems useful... but without date subtraction I can't put it together.
Grahame Grieve (May 12 2016 at 15:32):
not right now - ballot comment that we need date operations, and then the answer will be yes
Josh Mandel (May 12 2016 at 15:32):
adding.
Ewout Kramer (May 13 2016 at 00:26):
This is an example Bryn added, so I guess this is something that's there in the CQM, but not in FHIR. Remember, the examples are not necessarily FHIR anymore ;-)
Josh Mandel (May 13 2016 at 01:53):
That's not a great answer for whatever documentation is published in the FHIR build .
Ewout Kramer (May 13 2016 at 02:38):
Yeah. I have to get used to that myself too. But that's also the reason for the name change (no more FHIR in the name).
As to publishing, what's the alternative? We've thought a bit about that at HIMSS...an external website with a publishing cycle not connected to the FHIR spec would be one option.
Brian Postlethwaite (May 13 2016 at 04:28):
we already noted the date addition logic
Brian Postlethwaite (May 13 2016 at 04:31):
Sorry, that was simple manipulation, not diffs of dates (which can return a timespan)
Grahame Grieve (May 13 2016 at 07:53):
well, we have to sort out the publication issue
Lloyd McKenzie (May 29 2016 at 22:51):
Is the XHTML accessable from within fluentpath? I'm trying to figure out how to write a constraint that says "narrative must be a single string node with no mark-up"
Grahame Grieve (May 29 2016 at 23:04):
we haven't said anything about this, but there's no object model for xhtml, and I'm not keen to make one up. This constraint would be easy if we said the xhtml was a string, and you could use a regex (.matches())
Lloyd McKenzie (May 29 2016 at 23:33):
So a regex that prohibited tags greater-than? That might work here. It's not going to work well for other use-cases we'd talked about, for example "this element needs to appear in the third row of the first table". There are lots of use-cases for wanting to have fairly strict control over what the narrative looks like. I'd hate to have that go away in the transition from xpath to fluentpath. When you say there's no object model, why not? We have a schema. My impression was that fluentpath would work for anything you could express by schema.
Grahame Grieve (May 29 2016 at 23:50):
schema is not quite the same as object model because of... you guessed it.... mixed content
Lloyd McKenzie (May 29 2016 at 23:58):
Mixed content isn't really an issue. That's just a "text node". My guess is what really causes grief is there are some parts that are non-ordered - you can specify repeating properties interspersed with other properties.
Bryn Rhodes (Jun 06 2016 at 15:43):
Following up on the discussion about reflection in FluentPath, I think the use of .as() to get at the profile information is the right approach, but what structure should be returned in non-FHIR contexts? The documentation says FluentPath works on a "MOF-equivalent type system", so in general we would need to expose a structure that provided that information, yes?
Bryn Rhodes (Jun 06 2016 at 15:45):
CQL has a generic "model info" structure that supports this, it's just a simple exposure of classes, could we use that in the general case and then for FHIR specifically, provide a way to access the elementDefinition using the proposed .definition() operator?
Brian Postlethwaite (Jun 20 2016 at 05:38):
Looking to check if my understanding of using fluentpath with extensions is correct (and I'm not missing something)
Patient.extension.where(url='http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName').valueString
Is how I should read the Mothers maiden name for a patient in a fluentpath expression (for a custom search parameter)
Grahame Grieve (Jun 20 2016 at 06:19):
yes, but there's a short cut in the FHIR appendix:
Grahame Grieve (Jun 20 2016 at 06:19):
Patient.extension('http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName').value
Grahame Grieve (Jun 20 2016 at 06:20):
actually, your first bit was wrong - it's value, not valueString
Brian Postlethwaite (Jun 20 2016 at 06:22):
And it will work out that it needs to skip the valueString part, and go to @value
xpath fhir:extension[@url='...']/fhir:valueString/@value
Brian Postlethwaite (Jun 20 2016 at 06:22):
Is that defined in fluentpath, or the localized fhir version?
That's pretty fhir specific.
Grahame Grieve (Jun 20 2016 at 06:23):
yes, the @value is implicit. The String itself is the value. Think about the json alternative...
Brian Postlethwaite (Jun 20 2016 at 06:32):
Still seems kinda fhir specific. (happy with that, but appears like a fhir localization)
Grahame Grieve (Jun 20 2016 at 06:48):
which bit? the extension() bit is for sure
Grahame Grieve (Jun 20 2016 at 06:48):
it's documented as a FHIR specific thing
Brian Postlethwaite (Jun 20 2016 at 06:49):
Thanks, will recheck.
Brian Postlethwaite (Jun 20 2016 at 13:31):
does this mean that extension is a reserved word in the fluentpath for fhir?
and thus the syntax/format that I used wouldn't work (as extension is a reserved word)?
Grahame Grieve (Jun 20 2016 at 22:30):
.extension() is different to .extension
Jason Walonoski (Jun 30 2016 at 12:47):
Question about the fluentpath invariant sdf-12.
Currently it is written as snapshot.element.base implies baseType
Believe it should say snapshot.element.select(base) implies baseType
Can any of the fluentpath experts chime in?
Bryn Rhodes (Jun 30 2016 at 13:29):
Those should both work to enforce that invariant, if there are base elements, then there must be a baseType. The first one will give a list of base elements, the second one will give a list of list of base elements, either way, interpreting that list as a boolean is effectively an exists.
Brian Postlethwaite (Jul 05 2016 at 12:55):
The fluentpath expression today().toString().length() = 10
that is in the unit tests uses toString on a date. The toString defined here http://hl7-fhir.github.io/fluentpath.html#1.32.4.4
doesn't say what to do with dates.
Grahame Grieve (Jul 05 2016 at 12:58):
they have an innate string representation
Brian Postlethwaite (Jul 05 2016 at 13:03):
given the others were explicitly called out what to do for them, should it also be explicitly stated?
Brian Postlethwaite (Jul 05 2016 at 13:04):
(Yes, I've updated it to return the string representation too)
nicola (RIO/SS) (Sep 12 2016 at 23:06):
One more query language with implementation: https://stedolan.github.io/jq/manual/.
nicola (RIO/SS) (Sep 12 2016 at 23:17):
curl http://spark.furore.com/fhir/Patient?_format=json | jq '.entry[].resource| {lastName: .name[0].family[0], firstName: .name[0].given[0], id: .id}'
Last updated: Apr 12 2022 at 19:14 UTC