public static IEnumerable <Base> SelectNew(this Base input, string expression, FhirEvaluationContext ctx = null) { var inputNav = input.ToTypedElement(); var result = inputNav.Select(expression, ctx ?? FhirEvaluationContext.CreateDefault()); return(ToFhirValues(result)); }
/// <inheritdoc /> public IReadOnlyCollection <SearchIndexEntry> Extract(ResourceElement resource) { EnsureArg.IsNotNull(resource, nameof(resource)); var poco = resource.ToPoco(); var entries = new List <SearchIndexEntry>(); var context = new FhirEvaluationContext(poco); IEnumerable <SearchParameterInfo> searchParameters = _searchParameterDefinitionManager.GetSearchParameters(resource.InstanceType); foreach (SearchParameterInfo searchParameter in searchParameters) { if (searchParameter.Name == SearchParameterNames.ResourceType) { // We don't index the resource type value. We just use the property on the root document. continue; } if (searchParameter.Type == SearchParamType.Composite) { entries.AddRange(ProcessCompositeSearchParameter(searchParameter, poco, context)); } else { entries.AddRange(ProcessNonCompositeSearchParameter(searchParameter, poco, context)); } } return(entries); }
/// <summary> /// Return <see langword="true"/>if item is in slice. /// </summary> /// <param name="item"></param> /// <returns></returns> public bool IsSlice(Element discriminator, Element item) { var nav = item.ToTypedElement(); EvaluationContext ctx = new FhirEvaluationContext(); ITypedElement[] results = nav.Select(path, ctx).ToArray(); if ((results == null) || (results.Length == 0)) { return(false); } if (results.Length > 1) { throw new NotImplementedException($"SliceOnValueDiscriminator.GetValue: Multiple elements found at path '{this.path}'"); } ITypedElement result = results[0]; Element value = result.Value as Element; if (value == null) { throw new NotImplementedException($"SliceOnValueDiscriminator.GetValue: Value is not an Element '{this.path}'"); } //# TODO: I am using Matches here. SHould I be using IsExactly? return(value.Matches(discriminator)); }
public void TestFhirPathTrace() { var patient = new Hl7.Fhir.Model.Patient() { Id = "pat45", Active = false }; patient.Meta = new Meta() { LastUpdated = new DateTimeOffset(2018, 5, 24, 14, 48, 0, TimeSpan.Zero) }; var nav = patient.ToTypedElement(); EvaluationContext ctx = new FhirEvaluationContext(); var result = nav.Select("Resource.meta.trace('log').lastUpdated", ctx); Assert.IsNotNull(result.FirstOrDefault()); Assert.AreEqual(PartialDateTime.Parse("2018-05-24T14:48:00+00:00"), result.First().Value); bool traced = false; ctx.Tracer = (string name, System.Collections.Generic.IEnumerable <ITypedElement> results) => { System.Diagnostics.Trace.WriteLine($"{name}"); Assert.AreEqual("log", name); foreach (ITypedElement item in results) { var fhirValue = item.Annotation <IFhirValueProvider>(); System.Diagnostics.Trace.WriteLine($"--({fhirValue.FhirValue.GetType().Name}): {item.Value} {fhirValue.FhirValue}"); Assert.AreEqual(patient.Meta, fhirValue.FhirValue); traced = true; } }; result = nav.Select("Resource.meta.trace('log').lastUpdated", ctx); Assert.IsNotNull(result.FirstOrDefault()); Assert.AreEqual(PartialDateTime.Parse("2018-05-24T14:48:00+00:00"), result.First().Value); Assert.IsTrue(traced); traced = false; ctx.Tracer = (string name, System.Collections.Generic.IEnumerable <ITypedElement> results) => { System.Diagnostics.Trace.WriteLine($"{name}"); Assert.IsTrue(name == "id" || name == "log"); foreach (ITypedElement item in results) { var fhirValue = item.Annotation <IFhirValueProvider>(); System.Diagnostics.Trace.WriteLine($"--({fhirValue.FhirValue.GetType().Name}): {item.Value} {fhirValue.FhirValue}"); traced = true; } }; result = nav.Select("Resource.trace('id', id).meta.trace('log', lastUpdated).lastUpdated", ctx); Assert.IsNotNull(result.FirstOrDefault()); Assert.AreEqual(PartialDateTime.Parse("2018-05-24T14:48:00+00:00"), result.First().Value); Assert.IsTrue(traced); }
public LightweightReferenceToElementResolverTests() { ReferenceSearchValueParser referenceSearchValueParser = Mock.TypeWithArguments <ReferenceSearchValueParser>(); _resolver = new LightweightReferenceToElementResolver(referenceSearchValueParser, ModelInfoProvider.Instance); _encounter = Samples.GetJsonSample <Encounter>("Encounter-For-Patient-f001"); _context = new FhirEvaluationContext { ElementResolver = _resolver.Resolve, }; FhirPathCompiler.DefaultSymbolTable.AddFhirExtensions(); }
public void ResolveOnEmptyTest() { // resolve should handle an empty collection as input var symbolTable = new SymbolTable(); symbolTable.AddStandardFP(); symbolTable.AddFhirExtensions(); var compiler = new FhirPathCompiler(symbolTable); var evaluator = compiler.Compile("{}.resolve()"); var result = evaluator(null, FhirEvaluationContext.CreateDefault()); Assert.IsFalse(result.Any()); }
/// <inheritdoc /> public IReadOnlyCollection <SearchIndexEntry> Extract(ResourceElement resource) { EnsureArg.IsNotNull(resource, nameof(resource)); Resource poco = resource.ToPoco(); var entries = new List <SearchIndexEntry>(); var context = new FhirEvaluationContext(poco) { // Allows the indexer to parse the 'resolve' function i.e. CarePlan.subject.where(resolve() is Patient) ElementResolver = _referenceToElementResolver.Resolve, }; IEnumerable <SearchParameterInfo> searchParameters = _searchParameterDefinitionManager.GetSearchParameters(resource.InstanceType); foreach (SearchParameterInfo searchParameter in searchParameters) { if (searchParameter.Code == SearchParameterNames.ResourceType) { // We don't index the resource type value. We just use the property on the root document. continue; } if (searchParameter.Type == SearchParamType.Composite) { entries.AddRange(ProcessCompositeSearchParameter(searchParameter, poco, context)); } else { entries.AddRange(ProcessNonCompositeSearchParameter(searchParameter, poco, context)); } } return(entries); }
private IEnumerable <SearchIndexEntry> ProcessCompositeSearchParameter(SearchParameterInfo searchParameter, Base resource, FhirEvaluationContext context) { Debug.Assert(searchParameter?.Type == SearchParamType.Composite, "The search parameter must be composite."); SearchParameterInfo compositeSearchParameterInfo = searchParameter; IEnumerable <Base> rootObjects = resource.Select(searchParameter.Expression, context); foreach (var rootObject in rootObjects) { int numberOfComponents = searchParameter.Component.Count; bool skip = false; var componentValues = new IReadOnlyList <ISearchValue> [numberOfComponents]; // For each object extracted from the expression, we will need to evaluate each component. for (int i = 0; i < numberOfComponents; i++) { SearchParameterComponentInfo component = searchParameter.Component[i]; // First find the type of the component. SearchParameterInfo componentSearchParameterDefinition = _searchParameterDefinitionManager.GetSearchParameter(component.DefinitionUrl); IReadOnlyList <ISearchValue> extractedComponentValues = ExtractSearchValues( componentSearchParameterDefinition.Url.ToString(), componentSearchParameterDefinition.Type, componentSearchParameterDefinition.TargetResourceTypes, rootObject, component.Expression, context); // Filter out any search value that's not valid as a composite component. extractedComponentValues = extractedComponentValues .Where(sv => sv.IsValidAsCompositeComponent) .ToArray(); if (!extractedComponentValues.Any()) { // One of the components didn't have any value and therefore it will not be indexed. skip = true; break; } componentValues[i] = extractedComponentValues; } if (skip) { continue; } yield return(new SearchIndexEntry(compositeSearchParameterInfo, new CompositeSearchValue(componentValues))); } }
private IReadOnlyList <ISearchValue> ExtractSearchValues( string searchParameterDefinitionUrl, SearchParamType?searchParameterType, IEnumerable <string> allowedReferenceResourceTypes, Base element, string fhirPathExpression, FhirEvaluationContext context) { Debug.Assert(searchParameterType != SearchParamType.Composite, "The search parameter must be non-composite."); var results = new List <ISearchValue>(); // For simple value type, we can parse the expression directly. IEnumerable <Base> extractedValues = Enumerable.Empty <Base>(); try { extractedValues = element.Select(fhirPathExpression, context); } catch (Exception ex) { _logger.LogWarning( ex, "Failed to extract the values using '{FhirPathExpression}' against '{ElementType}'.", fhirPathExpression, element.GetType()); } Debug.Assert(extractedValues != null, "The extracted values should not be null."); // If there is target set, then filter the extracted values to only those types. if (searchParameterType == SearchParamType.Reference && allowedReferenceResourceTypes != null && allowedReferenceResourceTypes.Any()) { List <string> targetResourceTypes = _targetTypesLookup.GetOrAdd(searchParameterDefinitionUrl, _ => { return(allowedReferenceResourceTypes.Select(t => t.ToString()).ToList()); }); // TODO: The expression for reference search parameters in STU3 has issues. // The reference search parameter could be pointing to an element that can be multiple types. For example, // the Appointment.participant.actor can be type of Patient, Practitioner, Related Person, Location, and so on. // Some search parameter could refer to this property but restrict to certain types. For example, // Appointment's location search parameter is returned only when Appointment.participant.actor is Location element. // The STU3 expressions don't have this restriction so everything is being returned. This is addressed in R4 release (see // http://community.fhir.org/t/expression-seems-incorrect-for-reference-search-parameter-thats-only-applicable-to-certain-types/916/2). // Therefore, for now, we will need to compare the reference value itself (which can be internal or external references), and restrict // the values ourselves. extractedValues = extractedValues.Where(ev => ev is ResourceReference rr && rr.Reference != null && targetResourceTypes.Any(trt => rr.Reference.Contains(trt, StringComparison.Ordinal))); } foreach (var extractedValue in extractedValues) { if (!_fhirElementTypeConverterManager.TryGetConverter(extractedValue.GetType(), GetSearchValueTypeForSearchParamType(searchParameterType), out IFhirElementToSearchValueTypeConverter converter)) { _logger.LogWarning( "The FHIR element '{ElementType}' is not supported.", extractedValue.TypeName); continue; } _logger.LogDebug( "The FHIR element '{ElementType}' will be converted using '{ElementTypeConverter}'.", extractedValue.TypeName, converter.GetType().FullName); results.AddRange(converter.ConvertTo(extractedValue) ?? Enumerable.Empty <ISearchValue>()); } return(results); }
private IEnumerable <SearchIndexEntry> ProcessNonCompositeSearchParameter(SearchParameterInfo searchParameter, Base resource, FhirEvaluationContext context) { Debug.Assert(searchParameter?.Type != SearchParamType.Composite, "The search parameter must be non-composite."); SearchParameterInfo searchParameterInfo = searchParameter; foreach (ISearchValue searchValue in ExtractSearchValues( searchParameter.Url.ToString(), searchParameter.Type, searchParameter.TargetResourceTypes, resource, searchParameter.Expression, context)) { yield return(new SearchIndexEntry(searchParameterInfo, searchValue)); } }