Exemplo n.º 1
0
        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));
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 3
0
        /// <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));
        }
Exemplo n.º 4
0
        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);
        }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 8
0
        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)));
            }
        }
Exemplo n.º 9
0
        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);
        }
Exemplo n.º 10
0
        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));
            }
        }