/// <summary> /// Add search parameters /// </summary> public static void AddSearchParam <TFhirResource>(String fhirQueryParameter, String hdsiQueryParmeter, QueryParameterRewriteType type) { var resourceType = typeof(TFhirResource).GetResourceType(); var mapConfig = s_map.Map.FirstOrDefault(o => resourceType.HasValue ? resourceType.Value == o.Resource : !o.ResourceSpecified); if (mapConfig == null) { mapConfig = new QueryParameterType() { Resource = resourceType.Value, ResourceSpecified = resourceType.HasValue }; s_map.Map.Add(mapConfig); } // parm config var parmConfig = mapConfig.Map.FirstOrDefault(o => o.FhirQuery == fhirQueryParameter); if (parmConfig == null) { parmConfig = new QueryParameterMapProperty() { FhirQuery = fhirQueryParameter, ModelQuery = hdsiQueryParmeter, FhirType = type }; } else { parmConfig.ModelQuery = hdsiQueryParmeter; parmConfig.FhirType = type; } mapConfig.Map.Add(parmConfig); }
/// <summary> /// Re-writes the FHIR query parameter to HDSI query parameter format /// </summary> /// <returns></returns> public static FhirQuery RewriteFhirQuery <TFhirResource, TModelType>(System.Collections.Specialized.NameValueCollection fhirQuery, out NameValueCollection hdsiQuery) { // Try parse if (fhirQuery == null) { throw new ArgumentNullException(nameof(fhirQuery)); } // Count and offset parameters int count = 0, offset = 0, page = 0; if (!Int32.TryParse(fhirQuery["_count"] ?? "100", out count)) { throw new ArgumentException("_count"); } if (!Int32.TryParse(fhirQuery["_offset"] ?? "0", out offset)) { throw new ArgumentException("_offset"); } if (fhirQuery["_page"] != null && Int32.TryParse(fhirQuery["_page"], out page)) { offset = page * count; } Guid queryId = Guid.Empty; if (fhirQuery["_stateid"] != null) { queryId = Guid.Parse(fhirQuery["_stateid"]); } else { queryId = Guid.NewGuid(); } // Return new query FhirQuery retVal = new FhirQuery() { ActualParameters = new System.Collections.Specialized.NameValueCollection(), Quantity = count, Start = offset, MinimumDegreeMatch = 100, QueryId = queryId, IncludeHistory = false, IncludeContained = false }; hdsiQuery = new NameValueCollection(); var map = s_map.Map.FirstOrDefault(o => o.SourceType == typeof(TFhirResource)); foreach (var kv in fhirQuery.AllKeys) { List <String> value = new List <string>(fhirQuery.GetValues(kv).Length); var parmComponents = kv.Split(':'); // Is the name extension? var parmMap = map?.Map.FirstOrDefault(o => o.FhirName == parmComponents[0]); if (parmMap == null) { parmMap = s_default.Map.FirstOrDefault(o => o.FhirName == parmComponents[0]); } if (parmMap == null && kv == "extension") { parmMap = new QueryParameterMapProperty() { FhirName = "extension", ModelName = "extension", FhirType = "tag" } } ; else if (parmMap == null) { continue; } // Valuse foreach (var v in fhirQuery.GetValues(kv)) { if (String.IsNullOrEmpty(v)) { continue; } // Operands bool chop = false; string opValue = String.Empty; string filterValue = v; if (v.Length > 2) { switch (v.Substring(0, 2)) { case "ap": chop = true; opValue = "~"; break; case "gt": chop = true; opValue = ">"; break; case "ge": chop = true; opValue = ">="; break; case "lt": chop = true; opValue = "<"; break; case "le": chop = true; opValue = "<="; break; case "ne": chop = true; opValue = "!"; break; case "eq": chop = true; break; default: break; } } if (parmComponents.Length > 1) { switch (parmComponents[1]) { case "contains": opValue = "~"; filterValue = $"*{filterValue}*"; break; case "missing": filterValue = "null"; chop = false; break; } } retVal.ActualParameters.Add(kv, filterValue); value.Add(opValue + filterValue.Substring(chop ? 2 : 0)); } if (value.Count(o => !String.IsNullOrEmpty(o)) == 0) { continue; } // Query switch (parmMap.FhirType) { case "identifier": foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); // Might be a URL if (Uri.TryCreate(segs[0], UriKind.Absolute, out Uri data)) { var aa = ApplicationServiceContext.Current.GetService <IAssigningAuthorityRepositoryService>().Get(data); hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelName, aa.DomainName), segs[1]); } else { hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelName, segs[0]), segs[1]); } } else { hdsiQuery.Add(parmMap.ModelName + ".value", itm); } } break; case "concept": foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); string codeSystemUri = segs[0]; CodeSystem codeSystem = null; if (codeSystemUri.StartsWith("urn:oid:")) { codeSystemUri = codeSystemUri.Substring(8); codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <CodeSystem> >().Find(o => o.Oid == codeSystemUri).FirstOrDefault(); } else if (codeSystemUri.StartsWith("urn:") || codeSystemUri.StartsWith("http:")) { codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <CodeSystem> >().Find(o => o.Url == codeSystemUri).FirstOrDefault(); } else { codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <CodeSystem> >().Find(o => o.Name == codeSystemUri).FirstOrDefault(); } s_tracer.TraceInfo("Have translated FHIR domain {0} to {1}", codeSystemUri, codeSystem?.Name); if (codeSystem != null) { hdsiQuery.Add(String.Format("{0}.referenceTerm[{1}].term.mnemonic", parmMap.ModelName, codeSystem.Name), segs[1]); } else { hdsiQuery.Add(String.Format("{0}.mnemonic", parmMap.ModelName), segs[1]); } } else { hdsiQuery.Add(parmMap.ModelName + ".referenceTerm.term.mnemonic", itm); } } break; case "reference": foreach (var itm in value) { if (itm.Contains("/")) { var segs = itm.Split('/'); hdsiQuery.Add(parmMap.ModelName, segs[1]); } else { hdsiQuery.Add(parmMap.ModelName, itm); } } break; case "tag": foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelName, segs[0]), segs[1]); } else { hdsiQuery.Add(parmMap.ModelName, itm); } } break; default: hdsiQuery.Add(parmMap.ModelName, value); break; } } return(retVal); }
/// <summary> /// Re-writes the FHIR query parameter to HDSI query parameter format /// </summary> /// <returns></returns> public static FhirQuery RewriteFhirQuery(Type fhirType, Type modelType, System.Collections.Specialized.NameValueCollection fhirQuery, out NameValueCollection hdsiQuery) { // Try parse if (fhirQuery == null) { throw new ArgumentNullException(nameof(fhirQuery)); } // Count and offset parameters int count = 0, offset = 0, page = 0; if (!Int32.TryParse(fhirQuery["_count"] ?? "25", out count)) { throw new ArgumentException("_count"); } if (!Int32.TryParse(fhirQuery["_offset"] ?? "0", out offset)) { throw new ArgumentException("_offset"); } if (fhirQuery["_page"] != null && Int32.TryParse(fhirQuery["_page"], out page)) { offset = page * count; } Guid queryId = Guid.Empty; if (fhirQuery["_stateid"] != null) { queryId = Guid.Parse(fhirQuery["_stateid"]); } else if (fhirQuery["_total"] == "accurate") // to get an accurate total we have to persist query state { queryId = Guid.NewGuid(); } else { queryId = Guid.Empty; } // Return new query FhirQuery retVal = new FhirQuery() { ActualParameters = new System.Collections.Specialized.NameValueCollection(), Quantity = count, Start = offset, MinimumDegreeMatch = 100, QueryId = queryId, IncludeHistory = false, IncludeContained = false }; hdsiQuery = new NameValueCollection(); var resourceType = fhirType.GetResourceType(); var map = s_map.Map.FirstOrDefault(o => resourceType.HasValue ? resourceType.Value == o.Resource : !o.ResourceSpecified); foreach (var kv in fhirQuery.AllKeys) { List <String> value = new List <string>(fhirQuery.GetValues(kv).Length); var parmComponents = kv.Split(':'); // Is the name extension? var parmMap = map?.Map.FirstOrDefault(o => o.FhirQuery == parmComponents[0]); if (parmMap == null) { parmMap = s_default.Map.FirstOrDefault(o => o.FhirQuery == parmComponents[0]); } if (parmMap == null && kv == "extension") { parmMap = new QueryParameterMapProperty() { FhirQuery = "extension", ModelQuery = "extension", FhirType = QueryParameterRewriteType.Tag } } ; else if (parmMap == null) { continue; } // Valuse foreach (var v in fhirQuery.GetValues(kv)) { if (String.IsNullOrEmpty(v)) { continue; } // Operands bool chop = false; string opValue = String.Empty; string filterValue = v; if (v.Length > 2) { switch (v.Substring(0, 2)) { case "ap": chop = true; opValue = "~"; break; case "gt": chop = true; opValue = ">"; break; case "ge": chop = true; opValue = ">="; break; case "lt": chop = true; opValue = "<"; break; case "le": chop = true; opValue = "<="; break; case "ne": chop = true; opValue = "!"; break; case "eq": chop = true; break; default: break; } } if (parmComponents.Length > 1) { switch (parmComponents[1]) { case "fuzzy": case "approx": opValue = ""; filterValue = $":(approx|'{filterValue}')"; break; case "contains": opValue = "~"; filterValue = $"*{filterValue}*"; break; case "exact": opValue = ""; break; case "missing": filterValue = "null"; chop = false; break; default: switch (parmMap.FhirType) { case QueryParameterRewriteType.String: // Default string matching is wonky in FHIR but meh we can mimic it at least opValue = "~"; filterValue = $"*{filterValue.Replace(' ', '?')}*"; break; } break; } } retVal.ActualParameters.Add(kv, filterValue); value.Add(opValue + filterValue.Substring(chop ? 2 : 0)); } if (value.Count(o => !String.IsNullOrEmpty(o)) == 0) { continue; } // Apply a function if (!String.IsNullOrEmpty(parmMap.Function)) { value = value.Select(o => parmMap.Function.Replace("$1", o)).ToList(); } // Query switch (parmMap.FhirType) { case QueryParameterRewriteType.Identifier: foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); // Might be a URL if (Uri.TryCreate(segs[0], UriKind.Absolute, out Uri data)) { var aa = ApplicationServiceContext.Current.GetService <IAssigningAuthorityRepositoryService>().Get(data); if (aa == null) { throw new FhirException(System.Net.HttpStatusCode.BadRequest, OperationOutcome.IssueType.NotFound, $"No authority for {data} found"); } hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelQuery, aa.DomainName), segs[1]); } else { hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelQuery, segs[0]), segs[1]); } } else { hdsiQuery.Add(parmMap.ModelQuery + ".value", itm); } } break; case QueryParameterRewriteType.Indicator: var mq = NameValueCollection.ParseQueryString(parmMap.ModelQuery); foreach (var itm in mq) { hdsiQuery.Add(itm.Key, itm.Value); } break; case QueryParameterRewriteType.Concept: foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); string codeSystemUri = segs[0]; Core.Model.DataTypes.CodeSystem codeSystem = null; if (codeSystemUri.StartsWith("urn:oid:")) { codeSystemUri = codeSystemUri.Substring(8); codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <Core.Model.DataTypes.CodeSystem> >().Find(o => o.Oid == codeSystemUri).FirstOrDefault(); } else if (codeSystemUri.StartsWith("urn:") || codeSystemUri.StartsWith("http:")) { codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <Core.Model.DataTypes.CodeSystem> >().Find(o => o.Url == codeSystemUri).FirstOrDefault(); } else { codeSystem = ApplicationServiceContext.Current.GetService <IRepositoryService <Core.Model.DataTypes.CodeSystem> >().Find(o => o.Name == codeSystemUri).FirstOrDefault(); } s_tracer.TraceInfo("Have translated FHIR domain {0} to {1}", codeSystemUri, codeSystem?.Name); if (codeSystem != null) { hdsiQuery.Add(String.Format("{0}.referenceTerm[{1}].term.mnemonic", parmMap.ModelQuery, codeSystem.Name), segs[1]); } else { hdsiQuery.Add(String.Format("{0}.mnemonic", parmMap.ModelQuery), segs[1]); } } else { hdsiQuery.Add(parmMap.ModelQuery + ".referenceTerm.term.mnemonic", itm); } } break; case QueryParameterRewriteType.Reference: foreach (var itm in value) { if (itm.Contains("/")) { var segs = itm.Split('/'); hdsiQuery.Add(parmMap.ModelQuery, segs[1]); } else { hdsiQuery.Add(parmMap.ModelQuery, itm); } } break; case QueryParameterRewriteType.Tag: foreach (var itm in value) { if (itm.Contains("|")) { var segs = itm.Split('|'); hdsiQuery.Add(String.Format("{0}[{1}].value", parmMap.ModelQuery, segs[0]), segs[1]); } else { hdsiQuery.Add(parmMap.ModelQuery, itm); } } break; default: hdsiQuery.Add(parmMap.ModelQuery, value); break; } } return(retVal); }