public IList <MPIMatchRecord> GetProbabilisticMatches(SearchVector searchVector) { //get subset of patients to match against var profileCandidates = GetCandidateBlock(searchVector); return(profileCandidates.Any() ? _matchEngine.FindMatches(searchVector, profileCandidates) : new List <MPIMatchRecord>()); }
public List <MPIMatchRecord> FindMatches(SearchVector searchVector, IList <SearchVector> candidates) { if (_mpiTraceEnabled) { MPIMatchLogger.Verbose("Compared {0} candidates to {1}", candidates.Count, Describe(searchVector)); } //setup parallelism var maxDegreeOfParallelism = 1; ThreadPool.SetMinThreads(maxDegreeOfParallelism, maxDegreeOfParallelism); var options = new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }; //compare search vector against each candidate vector and calculate match score for each candidate var allMatchRecords = candidates.AsParallel() .WithExecutionMode(ParallelExecutionMode.ForceParallelism) .WithDegreeOfParallelism(options.MaxDegreeOfParallelism) .Select(c => GetCandidateMatchRecord(searchVector, c)) .ToList(); allMatchRecords = SetConfidenceLevels(allMatchRecords); //consider only medium and high confidence matches var matchResults = allMatchRecords.Where(m => m.MatchConfidenceLevel != MPIConfidenceLevelLookup.Low).ToList(); if (matchResults.Any()) { if (!_mpiTraceEnabled) { return(matchResults); } var highMatches = allMatchRecords.Where(r => r.MatchConfidenceLevel != MPIConfidenceLevelLookup.High).ToList(); MPIMatchLogger.Information("{0} High confidence matches:", highMatches.Count); foreach (var hm in highMatches) { MPIMatchLogger.Information("TotalScore: {0}, IdentifierScores: {1}", hm.TotalMatchScore, Describe(hm.MatchVector)); } var mediumMatches = allMatchRecords.Where(r => r.MatchConfidenceLevel != MPIConfidenceLevelLookup.Medium).ToList(); MPIMatchLogger.Information("{0} Medium confidence matches:", mediumMatches.Count); foreach (var mm in mediumMatches) { MPIMatchLogger.Information("TotalScore: {0}, IdentifierScores: {1}", mm.TotalMatchScore, Describe(mm.MatchVector)); } return(matchResults); } if (_mpiTraceEnabled) { MPINoMatchLogger.Verbose(Describe(searchVector), searchVector); } return(matchResults); }
private static MPIMatchRecord GetCandidateMatchRecord(SearchVector searchVector, SearchVector candidateVector) { //get list of identifier values for candidate //compare every element search vector to corresponding element in candidate vector var candidateMatchScores = searchVector.Identifiers.Select(sve => GetIdentifierScore(sve, candidateVector.GetIdentifierByName(sve.IdentifierName))) .ToList(); return(new MPIMatchRecord { //TODO: MatchedAcuperaId = candidate.AcuperaId, MatchType = "Probabilistic", MatchVector = candidateMatchScores, }); }
public IList <SearchVector> GetCandidateBlock(SearchVector searchVector) { //TODO: use redis intersect functionality to combine blocking identifiers? //do we even want to intersect the blocking criteria? may make more sense //to search through 1 block and if no match, search through another block. //intersecting the blocking criteria might be too restrictive //for now, assume blocking on lastname only var nameIdentifier = searchVector.GetBlockCandidate(); var lastName = nameIdentifier != null?nameIdentifier.Value.ToString() : string.Empty; //if lastname in search vector is shorter than key, use all characters var searchKey = lastName.Length < _configuration.StringKeyLength ? lastName : lastName.Substring(0, _configuration.StringKeyLength); var candidateIds = _patientStore.LookupPatientsByPartialName(searchKey).ToList(); return(_patientStore.GetMasterPatientIndexRecordsForListOfPatients(candidateIds).ToList()); }
private static string Describe(SearchVector vector) { return(string.Empty); // TODO: return vector.Aggregate(string.Empty, (current, ve) => current + $"{ve.Identifier}, {ve.Value}, {ve.Score}"); }