Beispiel #1
0
        /// <summary>
        /// Calls the database to retrieve a single dictionary term based on its specific Term ID.
        /// Similar, but not identical, to GetTerm().  Instead of retrieving the term for a specific
        /// dictionary, the term is fetched for a preferred audience.  If no records are available for that audience,
        /// then any other avaiable records are returned instead.
        /// </summary>
        /// <param name="termId">The ID of the Term to be retrieved</param>
        /// <param name="language">The Term's desired language.
        ///     Supported values are:
        ///         en - English
        ///         es - Spanish
        /// </param>
        /// <param name="preferredAudience">Preferred target audieince for the definition.</param>
        /// <param name="version">String identifying which vereion of the API to match.</param>
        /// <returns></returns>
        public DataTable GetTermForAudience(int termId, Language language, AudienceType preferredAudience, String version)
        {
            log.DebugFormat("Enter GetTermForAudience( {0}, {1}, {2}, {3} ).", termId, language, preferredAudience, version);

            DataTable results = null;

            SqlParameter[] parameters = new SqlParameter[] {
                new SqlParameter("@TermID", SqlDbType.Int)
                {
                    Value = termId
                },
                new SqlParameter("@Language", SqlDbType.NVarChar)
                {
                    Value = language.ToString()
                },
                new SqlParameter("@PreferredAudience", SqlDbType.NVarChar)
                {
                    Value = preferredAudience.ToString()
                },
                new SqlParameter("@ApiVers", SqlDbType.NVarChar)
                {
                    Value = version
                },
            };

            using (SqlConnection conn = SqlHelper.CreateConnection(DBConnectionString))
            {
                results = SqlHelper.ExecuteDatatable(conn, CommandType.StoredProcedure, SP_GET_DICTIONARY_TERM_FOR_AUDIENCE, parameters);
            }

            return(results);
        }
        /// <summary>
        /// Get Term deatils based on the input values
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="id">The Id for the term</param>
        /// <param name="requestedFields"> The list of fields that needs to be sent in the response</param>
        /// <returns>An object of GlossaryTerm</returns>
        /// </summary>
        public async Task <GlossaryTerm> GetById(string dictionary, AudienceType audience, string language, long id, string[] requestedFields)
        {
            IGetResponse <GlossaryTerm> response = null;

            try
            {
                string idValue = id + "_" + dictionary + "_" + language + "_" + audience.ToString().ToLower();
                response = await _elasticClient.GetAsync <GlossaryTerm>(new DocumentPath <GlossaryTerm>(idValue),
                                                                        g => g.Index( this._apiOptions.AliasName ).Type("terms"));
            }
            catch (Exception ex)
            {
                String msg = String.Format("Could not search dictionary '{0}', audience '{1}', language '{2}' and id '{3}.", dictionary, audience, language, id);
                _logger.LogError(msg, ex);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = String.Format("Invalid response when searching for dictionary '{0}', audience '{1}', language '{2}' and id '{3}.", dictionary, audience, language, id);
                _logger.LogError(msg);
                throw new APIErrorException(500, msg);
            }

            if (null == response.Source)
            {
                string msg = String.Format("Empty response when searching for dictionary '{0}', audience '{1}', language '{2}' and id '{3}.", dictionary, audience, language, id);
                _logger.LogError(msg);
                throw new APIErrorException(200, msg);
            }

            return(response.Source);
        }
Beispiel #3
0
        /// <summary>
        /// Builds the SearchRequest for terms containing with the search text.
        /// </summary>
        /// <param name="index">The index which will be searched against.</param>
        /// <param name="types">The list of document types to search.</param>
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="query">The text to search for.</param>
        /// <param name="size">The number of records to retrieve.</param>
        private SearchRequest BuildContainsRequest(Indices index, Types types, string dictionary, string language, AudienceType audience, string query, int size)
        {
            /*
             * Create a query similar to the following.  The bool subquery is written with an overloaded version
             * of operator && which supplies the `{"bool" :  {"must": [` portion for you.
             * This is somewhat explained here: https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/bool-queries.html.
             *
             * curl -XPOST http://SERVER_NAME/glossaryv1/terms/_search -H 'Content-Type: application/x-ndjson'   -d '{
             *   "query": {
             *     "bool" : {
             *       "must" :
             *         [{"term" : { "language" : "es" }},
             *         {"term" : { "dictionary" : "Cancer.gov" }},
             *         {"term": { "audience": "Patient"}},
             *         {"match_phrase":{"term_name._autocomplete":"cutáneo"}}
             *         ],
             *          "must_not" :  {"prefix" : {"term_name" : "cutáneo"}}
             *       }
             *     }
             * ,"sort": ["term_name"]
             * , "_source": ["term_id", "term_name"]
             * , "from": 0
             * , "size": 10
             * }'
             */
            SearchRequest request = new SearchRequest(index, types)
            {
                Query = new TermQuery {
                    Field = "language", Value = language.ToString()
                } &&
                new TermQuery {
                    Field = "audience", Value = audience.ToString()
                } &&
                new TermQuery {
                    Field = "dictionary", Value = dictionary.ToString()
                } &&
                new MatchPhraseQuery {
                    Field = "term_name._autocomplete", Query = query.ToString()
                } &&
                !new PrefixQuery {
                    Field = "term_name", Value = query.ToString()
                },
                Sort = new List <ISort>
                {
                    new SortField {
                        Field = "term_name"
                    }
                },
                Source = new SourceFilter
                {
                    Includes = new string[] { "term_id", "term_name" }
                },
                Size = size
            };

            return(request);
        }
Beispiel #4
0
        public Audience tag_not(HashSet <string> values)
        {
            if (allAudience != null)
            {
                allAudience = null;
            }

            AudienceTarget audienceTarget = AudienceTarget.tag_not(values);

            allAudience = null;
            if (dictionary == null)
            {
                dictionary = new Dictionary <string, HashSet <string> >();
            }

            Dictionary <string, HashSet <string> > dictionary1 = dictionary;
            AudienceType audienceType = audienceTarget.audienceType;
            string       key1         = audienceType.ToString();

            if (dictionary1.ContainsKey(key1))
            {
                Dictionary <string, HashSet <string> > dictionary2 = dictionary;
                audienceType = audienceTarget.audienceType;
                string           index     = audienceType.ToString();
                HashSet <string> stringSet = dictionary2[index];
                foreach (string str in values)
                {
                    stringSet.Add(str);
                }
            }
            else
            {
                Dictionary <string, HashSet <string> > dictionary2 = dictionary;
                audienceType = audienceTarget.audienceType;
                string           key2      = audienceType.ToString();
                HashSet <string> stringSet = values;
                dictionary2.Add(key2, stringSet);
            }

            return(Check());
        }
        /// <summary>
        /// Common code for building the results data structure from Search and Expand.
        /// </summary>
        /// <param name="results"></param>
        /// <returns></returns>
        private SearchReturn BuildSearchResultsStructure(SearchResults results, Language language, AudienceType audience, int offset)
        {
            List <String> messages = new List <string>();

            int resultCount = results.MatchCount;

            // Report the count in a human-readable format.
            String message = String.Format("Found {0} results.", resultCount);

            log.Debug(message);
            messages.Add(message);

            // Retrieve results.  We already know the number of results, so let's preset the
            // list to the size we know we're going to need.  (Use the number of rows in the results
            // since MatchCount/resultCount is conceivably much larger than we need and might even be int.MaxValue.)
            List <DictionarySearchResultEntry> foundTerms = new List <DictionarySearchResultEntry>(results.Data.Rows.Count);

            foreach (DataRow row in results.Data.Rows)
            {
                try
                {
                    int    id        = row.Field <int>("termID");
                    string matchName = row.Field <string>("TermName");
                    string detail    = row.Field <string>("object");
                    detail = RewriteMediaFileLocations(detail);
                    foundTerms.Add(new DictionarySearchResultEntry(id, matchName, detail));
                }
                catch (Exception ex)
                {
                    log.Debug("Error retrieving search results.", ex);
                }
            }

            // Populate return metadata structure
            SearchReturnMeta meta = new SearchReturnMeta()
            {
                Language    = language.ToString(),
                Audience    = audience.ToString(),
                Offset      = offset,
                ResultCount = resultCount,
                Messages    = messages.ToArray()
            };


            // Combine meta and results to create the final return object.
            SearchReturn srchReturn = new SearchReturn()
            {
                Result = foundTerms.ToArray(),
                Meta   = meta
            };

            return(srchReturn);
        }
        /// <summary>
        /// Infrastructure for turning the DataTable returned for one of the GetTerm family of query
        /// methods into a TermReturn object.
        /// </summary>
        /// <param name="termId">The ID of the Term which was retrieved</param>
        /// <param name="language">The Term's desired language.
        ///     Supported values are:
        ///         en - English
        ///         es - Spanish
        /// </param>
        /// <param name="audience">The Term's desired audience.
        ///     Supported values are:
        ///         Patient
        ///         HealthProfessional
        /// </param>
        /// <returns>A data structure containing both meta data about the request and a string containing a JSON representation
        /// of the particular definition identified by the inputs to the method.
        /// </returns>
        private TermReturn GetTermCommon(DataTable dtTerm, int termId, Language language, AudienceType audience)
        {
            List <String> messages = new List <string>();

            String term = string.Empty;

            // Normal, found 1 match.
            int resultCount = dtTerm.Rows.Count;

            if (resultCount == 1)
            {
                log.Debug("Found 1 result.");
                messages.Add("OK");
                term = dtTerm.Rows[0].Field <string>("object");
                term = RewriteMediaFileLocations(term);
            }
            // "Normal", found no matches.
            else if (resultCount == 0)
            {
                log.Debug("Found 0 results.");
                messages.Add("No result found.");
                term = string.Empty;
            }
            // "Odd" case. With multiple matches, return the first one.
            else // result count must be greater than 1
            {
                log.WarnFormat("Expected to find one result for term {0}, found {1} instead.", termId, resultCount);
                messages.Add("OK");
                term = dtTerm.Rows[0].Field <string>("object");
                term = RewriteMediaFileLocations(term);
            }


            // Build up the return data structure.
            TermReturn trmReturn = new TermReturn();

            TermReturnMeta meta = new TermReturnMeta();

            meta.Language = language.ToString();
            meta.Audience = audience.ToString();
            meta.Messages = messages.ToArray();

            trmReturn.Meta = meta;
            trmReturn.Term = term;

            return(trmReturn);
        }
        /// <summary>
        /// Get the total number of terms available in the version of a dictionary matching a specific audience and language.
        /// </summary>
        /// <param name="dictionary">The specific dictionary to retrieve from.</param>
        /// <param name="audience">The target audience.</param>
        /// <param name="language">Language (English - en; Spanish - es).</param>
        /// <returns>The number of terms available.</returns>
        public async Task <long> GetCount(string dictionary, AudienceType audience, string language)
        {
            // Set up the CountRequest to send to elasticsearch.
            Indices      index   = Indices.Index(new string[] { this._apiOptions.AliasName });
            Types        types   = Types.Type(new string[] { "terms" });
            CountRequest request = new CountRequest(index, types)
            {
                Query = new TermQuery {
                    Field = "language", Value = language.ToString()
                } &&
                new TermQuery {
                    Field = "audience", Value = audience.ToString()
                } &&
                new TermQuery {
                    Field = "dictionary", Value = dictionary.ToString()
                }
            };

            ICountResponse response = null;

            try
            {
                response = await _elasticClient.CountAsync <GlossaryTerm>(request);
            }
            catch (Exception ex)
            {
                String msg = $"Could not get a count for dictionary '{dictionary}', audience '{audience}', language '{language}'";
                _logger.LogError($"Error getting count on index: '{this._apiOptions.AliasName}'.");
                _logger.LogError(msg, ex);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = $"Invalid response when searching for dictionary '{dictionary}', audience '{audience}', language '{language}'";
                _logger.LogError(msg);
                throw new APIErrorException(500, "errors occured");
            }

            return(response.Count);
        }
Beispiel #8
0
        /// <summary>
        /// Search for Terms based on the search criteria.
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="query">The search query</param>
        /// <param name="matchType">Defines if the search should begin with or contain the key word</param>
        /// <param name="size">Defines the size of the search</param>
        /// <param name="from">Defines the Offset for search</param>
        /// <param name="requestedFields"> The list of fields that needs to be sent in the response</param>
        /// <returns>A list of GlossaryTerm</returns>
        /// </summary>
        public async Task <List <GlossaryTerm> > Expand(string dictionary, AudienceType audience, string language, string query, string matchType, int size, int from, string[] requestedFields)
        {
            //     // Temporary Solution till we have Elastic Search
            //     List<GlossaryTerm> glossaryTermList = new List<GlossaryTerm>();
            //     glossaryTermList.Add(GenerateSampleTerm(requestedFields));
            //     glossaryTermList.Add(GenerateSampleTerm(requestedFields));

            //     return glossaryTermList;

            // Set up the SearchRequest to send to elasticsearch.
            Indices       index   = Indices.Index(new string[] { this._apiOptions.AliasName });
            Types         types   = Types.Type(new string[] { "terms" });
            SearchRequest request = new SearchRequest(index, types)
            {
                Query = new BoolQuery
                {
                    Must = new QueryContainer[]
                    {
                        new TermQuery {
                            Field = "language", Value = language.ToString()
                        } &&
                        new TermQuery {
                            Field = "audience", Value = audience.ToString()
                        } &&
                        new TermQuery {
                            Field = "dictionary", Value = dictionary.ToString()
                        } &&
                        new TermQuery {
                            Field = "first_letter", Value = query.ToString()
                        }
                    }
                },
                Sort = new List <ISort>
                {
                    new SortField {
                        Field = "term_name"
                    }
                },
                Size = size,
                From = from,
                // Source = new SourceFilter
                // {
                //     Includes = requestedFields
                // },
            };

            // The below 3 lines of code help to debug the query that is used for ES.
            // Delete this 3 lines after code starts working.
            var stream = new System.IO.MemoryStream();

            _elasticClient.Serializer.Serialize(request, stream);
            var jsonQuery = System.Text.Encoding.UTF8.GetString(stream.ToArray());
            // End of Debug code

            ISearchResponse <GlossaryTerm> response = null;

            try
            {
                response = await _elasticClient.SearchAsync <GlossaryTerm>(request);
            }
            catch (Exception ex)
            {
                String msg = String.Format("Could not search dictionary '{0}', audience '{1}', language '{2}', query '{3}', matchType '{4}', size '{5}', from '{6}'.", dictionary, audience, language, query, matchType, size, from);
                _logger.LogError(msg, ex);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = String.Format("Invalid response when searching for dictionary '{0}', audience '{1}', language '{2}', query '{3}', matchType '{4}', size '{5}', from '{6}'.", dictionary, audience, language, query, matchType, size, from);
                _logger.LogError(msg);
                throw new APIErrorException(500, "errors occured");
            }
            // If ES returns terms matching the params, return them.
            // List<GlossaryTerm> glossaryTermList = new List<GlossaryTerm>();
            // if (response.Total > 0)
            // {
            //     foreach(GlossaryTerm term in response.Documents)
            // }

            // return glossaryTermList;
            List <GlossaryTerm> glossaryTermList = new List <GlossaryTerm>();

            foreach (GlossaryTerm gt in response.Documents)
            {
                glossaryTermList.Add(gt);
            }
            // Temporary Solution till we have Elastic Search
            // List<GlossaryTerm> glossaryTermList = new List<GlossaryTerm>();
            // glossaryTermList.Add(GenerateSampleTerm(requestedFields));
            // glossaryTermList.Add(GenerateSampleTerm(requestedFields));

            return(glossaryTermList);
        }
Beispiel #9
0
        /// <summary>
        /// Calls the database to search for terms matching searchText. Results are sorted by the matched term name or alias.
        /// </summary>
        /// <param name="searchText">text to search for.</param>
        /// <param name="includeTypes">A filter for the types of name aliases to include.  Multiple values are separated by the pipe character (|).
        /// If no filter is supplied, the result </param>
        /// <param name="offset">Offset into the list of matches for the first result to return.</param>
        /// <param name="numResults">The maximum number of results to return. Must be at least 10.</param>
        /// <param name="dictionary">The dictionary to retreive the Term from.
        ///     Valid values are
        ///        Term - Dictionary of Cancer Terms
        ///        drug - Drug Dictionary
        ///        genetic - Dictionary of Genetics Terms
        /// </param>
        /// <param name="language">The Term's desired language.
        ///     Supported values are:
        ///         en - English
        ///         es - Spanish
        /// </param>
        /// <param name="version">String identifying which vereion of the JSON structure to retrieve.</param>
        /// <returns>DataTable containing a list of matching records.  Results are sorted by the matching term name.</returns>
        public SearchResults Expand(String searchText, String[] includeTypes, int offset, int numResults, DictionaryType dictionary, Language language, AudienceType audience, String version)
        {
            log.DebugFormat("Enter Expand( {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7} ).", searchText, includeTypes, offset, numResults, dictionary, language, audience, version);

            DataTable results;

            // Set up table parameter for specific types to include.
            DataTable includeFilter = new DataTable("includes");

            includeFilter.Columns.Add("NameType");
            Array.ForEach(includeTypes, typeName => includeFilter.Rows.Add(typeName));

            SqlParameter matchCountParam = new SqlParameter("@matchCount", SqlDbType.Int)
            {
                Direction = ParameterDirection.Output
            };
            int matchCount;

            SqlParameter[] parameters = new SqlParameter[] {
                new SqlParameter("@searchText", SqlDbType.NVarChar)
                {
                    Value = searchText
                },
                new SqlParameter("@IncludeTypes", SqlDbType.Structured)
                {
                    Value = includeFilter
                },
                new SqlParameter("@offset", SqlDbType.Int)
                {
                    Value = offset
                },
                new SqlParameter("@maxResults", SqlDbType.Int)
                {
                    Value = numResults
                },

                new SqlParameter("@Dictionary", SqlDbType.NVarChar)
                {
                    Value = dictionary.ToString()
                },
                new SqlParameter("@Language", SqlDbType.NVarChar)
                {
                    Value = language.ToString()
                },
                new SqlParameter("@Audience", SqlDbType.NVarChar)
                {
                    Value = audience.ToString()
                },
                new SqlParameter("@ApiVers", SqlDbType.NVarChar)
                {
                    Value = version
                },
                matchCountParam
            };

            using (SqlConnection conn = SqlHelper.CreateConnection(DBConnectionString))
            {
                results = SqlHelper.ExecuteDatatable(conn, CommandType.StoredProcedure, SP_EXPAND_DICTIONARY, parameters);

                // There's some unresolved weirdness with matchCountParam.Value coming back as NULL even though
                // the value is set unconditionally.  This appears to have been due to retrieving the value
                // after the connection had been closed. But, since that's not definite, check that the parameter
                // value is not null (or DBNull) and if so, log an error and retrieve a value that will allow
                // execution to continue.
                if (DBNull.Value.Equals(matchCountParam.Value) || matchCountParam.Value == null)
                {
                    log.Warn("Expand() encountered null when attempting to retrieve the @matchCount parameter.");
                    matchCount = int.MaxValue;
                }
                else
                {
                    matchCount = (int)matchCountParam.Value;
                }
            }

            return(new SearchResults(results, matchCount));
        }
Beispiel #10
0
        /// <summary>
        /// Calls the database to search for terms matching searchText. This method is intended for use with autosuggest
        /// and returns a maximum of 10 results
        /// </summary>
        /// <param name="searchText">text to search for.</param>
        /// <param name="searchType">The type of search to perform.
        ///     Valid values are:
        ///         Begins - Search for terms beginning with searchText.
        ///         Contains - Search for terms containing searchText.
        ///         Magic - Search for terms beginning with searchText, followed by those containing searchText.
        /// </param>
        /// <param name="numResults">Maximum number of results to return.</param>
        /// <param name="dictionary">The dictionary to retreive the Term from.
        ///     Valid values are
        ///        Term - Dictionary of Cancer Terms
        ///        drug - Drug Dictionary
        ///        genetic - Dictionary of Genetics Terms
        /// </param>
        /// <param name="language">The Term's desired language.
        ///     Supported values are:
        ///         en - English
        ///         es - Spanish
        /// </param>
        /// <param name="audience">The desired target audience - Patient or Health professional</param>
        /// <param name="version">String identifying which vereion of the JSON structure to retrieve.</param>
        /// <returns>DataTable containing a list of matching records.</returns>
        public SuggestionResults SearchSuggest(String searchText, SearchType searchType, int numResults, DictionaryType dictionary, Language language, AudienceType audience, String version)
        {
            log.DebugFormat("Enter SearchSuggest( {0}, {1}, {2}, {3}, {4}, {5}, {6} ).", searchText, searchType, numResults, dictionary, language, audience, version);

            DataTable results = null;

            SqlParameter matchCountParam = new SqlParameter("@matchCount", SqlDbType.Int)
            {
                Direction = ParameterDirection.Output
            };
            int matchCount;

            SqlParameter[] parameters = new SqlParameter[] {
                new SqlParameter("@searchText", SqlDbType.NVarChar)
                {
                    Value = searchText
                },
                new SqlParameter("@searchType", SqlDbType.NVarChar)
                {
                    Value = searchType.ToString()
                },
                new SqlParameter("@maxResults", SqlDbType.Int)
                {
                    Value = numResults
                },

                new SqlParameter("@Dictionary", SqlDbType.NVarChar)
                {
                    Value = dictionary.ToString()
                },
                new SqlParameter("@Language", SqlDbType.NVarChar)
                {
                    Value = language.ToString()
                },
                new SqlParameter("@Audience", SqlDbType.NVarChar)
                {
                    Value = audience.ToString()
                },
                new SqlParameter("@ApiVers", SqlDbType.NVarChar)
                {
                    Value = version
                },
                matchCountParam
            };

            using (SqlConnection conn = SqlHelper.CreateConnection(DBConnectionString))
            {
                results = SqlHelper.ExecuteDatatable(conn, CommandType.StoredProcedure, SP_SEARCH_SUGGEST_DICTIONARY, parameters);

                // There's some unresolved weirdness with matchCountParam.Value coming back as NULL even though
                // the value is set unconditionally.  This appears to have been due to retrieving the value
                // after the connection had been closed. But, since that's not definite, check that the parameter
                // value is not null (or DBNull) and if so, log an error and retrieve a value that will allow
                // execution to continue.
                if (DBNull.Value.Equals(matchCountParam.Value) || matchCountParam.Value == null)
                {
                    log.Warn("SearchSuggest() encountered null when attempting to retrieve the @matchCount parameter.");
                    matchCount = int.MaxValue;
                }
                else
                {
                    matchCount = (int)matchCountParam.Value;
                }
            }

            return(new SuggestionResults(results, matchCount));
        }
Beispiel #11
0
        /// <summary>
        /// Performs a search for terms with names matching searchText. Results are sorted by the matching term name.
        /// </summary>
        /// <param name="searchText">text to search for.</param>
        /// <param name="searchType">The type of search to perform.
        ///     Valid values are:
        ///         Begins - Search for terms beginning with searchText.
        ///         Contains - Search for terms containing searchText.
        ///         Magic - Search for terms beginning with searchText, followed by those containing searchText.
        /// </param>
        /// <param name="offset">Offset into the list of matches for the first result to return.</param>
        /// <param name="numResults">The maximum number of results to return. Must be at least 10.</param>
        /// <param name="dictionary">The dictionary to retreive the Term from.
        ///     Valid values are
        ///        Term - Dictionary of Cancer Terms
        ///        drug - Drug Dictionary
        ///        genetic - Dictionary of Genetics Terms
        /// </param>
        /// <param name="language">The Term's desired language.
        ///     Supported values are:
        ///         en - English
        ///         es - Spanish
        /// </param>
        /// <param name="audience">The desired target audience - Patient or Health professional</param>
        /// <param name="version">String identifying which vereion of the JSON structure to retrieve.</param>
        /// <returns>DataTable containing a list of matching records.  Results are sorted by the matching term name.</returns>
        public SearchResults Search(String searchText, SearchType searchType, int offset, int numResults, DictionaryType dictionary, Language language, AudienceType audience, String version)
        {
            log.DebugFormat("Enter Search( {0}, {1}, {2}, {3}, {4}, {5}, {6} ).", searchText, offset, numResults, dictionary, language, audience, version);

            DataTable results = null;

            switch (searchType)
            {
            case SearchType.Begins:
                searchText += "%";
                break;

            case SearchType.Contains:
                searchText = "%" + searchText + "%";
                break;

            case SearchType.Exact:
                break;

            default:
            {
                String message = String.Format("Unsupport search type '{0}'.", searchType);
                log.Error(message);
                throw new ArgumentException(message);
            }
            }

            SqlParameter matchCountParam = new SqlParameter("@matchCount", SqlDbType.Int)
            {
                Direction = ParameterDirection.Output
            };
            int matchCount;

            SqlParameter[] parameters = new SqlParameter[] {
                new SqlParameter("@searchText", SqlDbType.NVarChar)
                {
                    Value = searchText
                },
                new SqlParameter("@offset", SqlDbType.Int)
                {
                    Value = offset
                },
                new SqlParameter("@maxResults", SqlDbType.Int)
                {
                    Value = numResults
                },

                new SqlParameter("@Dictionary", SqlDbType.NVarChar)
                {
                    Value = dictionary.ToString()
                },
                new SqlParameter("@Language", SqlDbType.NVarChar)
                {
                    Value = language.ToString()
                },
                new SqlParameter("@Audience", SqlDbType.NVarChar)
                {
                    Value = audience.ToString()
                },
                new SqlParameter("@ApiVers", SqlDbType.NVarChar)
                {
                    Value = version
                },
                matchCountParam
            };

            using (SqlConnection conn = SqlHelper.CreateConnection(DBConnectionString))
            {
                results = SqlHelper.ExecuteDatatable(conn, CommandType.StoredProcedure, SP_SEARCH_DICTIONARY, parameters);

                // There's some unresolved weirdness with matchCountParam.Value coming back as NULL even though
                // the value is set unconditionally.  This appears to have been due to retrieving the value
                // after the connection had been closed. But, since that's not definite, check that the parameter
                // value is not null (or DBNull) and if so, log an error and attempt to mretrieve a value that
                // will allow execution to continue.
                if (DBNull.Value.Equals(matchCountParam.Value) || matchCountParam.Value == null)
                {
                    log.Warn("Search() encountered null when attempting to retrieve the @matchCount parameter.");
                    matchCount = int.MaxValue;
                }
                else
                {
                    matchCount = (int)matchCountParam.Value;
                }
            }

            return(new SearchResults(results, matchCount));
        }
        /// <summary>
        /// Get Term details based on the input values
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="id">The Id for the term</param>
        /// <returns>An object of GlossaryTerm</returns>
        /// </summary>
        public async Task <GlossaryTerm> GetById(string dictionary, AudienceType audience, string language, long id)
        {
            IGetResponse <GlossaryTerm> response = null;

            try
            {
                string idValue = $"{id}_{dictionary?.ToLower()}_{language?.ToLower()}_{audience.ToString().ToLower()}";
                response = await _elasticClient.GetAsync <GlossaryTerm>(new DocumentPath <GlossaryTerm>(idValue),
                                                                        g => g.Index( this._apiOptions.AliasName ).Type("terms"));
            }
            catch (Exception ex)
            {
                String msg = $"Could not search dictionary '{dictionary}', audience '{audience}', language '{language}' and id '{id}.";
                _logger.LogError($"Error searching index: '{this._apiOptions.AliasName}'.");
                _logger.LogError(ex, msg);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = $"Invalid response when searching for dictionary  '{dictionary}', audience '{audience}', language '{language}' and id '{id}.";
                _logger.LogError(msg);
                throw new APIErrorException(500, msg);
            }

            if (null == response.Source)
            {
                string msg = $"No match for dictionary '{dictionary}', audience '{audience}', language '{language}' and id '{id}.";
                _logger.LogDebug(msg);
                throw new APIErrorException(404, msg);
            }

            return(response.Source);
        }
        /// <summary>
        /// Get all Terms starting with the character passed.
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="expandCharacter">The character to search the query</param>
        /// <param name="size">Defines the size of the search</param>
        /// <param name="from">Defines the Offset for search</param>
        /// <param name="includeAdditionalInfo">If true, the RelatedResources and Media fields will be populated. Else, they will be empty.</param>
        /// <returns>A GlossaryTermResults object containing the desired records.</returns>
        /// </summary>
        public async Task <GlossaryTermResults> Expand(string dictionary, AudienceType audience, string language, string expandCharacter, int size, int from, bool includeAdditionalInfo)
        {
            // Elasticsearch knows how to figure out what the ElasticSearch name is for
            // a given field when given a PropertyInfo.
            Field[] requestedESFields = (includeAdditionalInfo ? ALL_FIELDS : DEFAULT_FIELDS)
                                        .Select(pi => new Field(pi))
                                        .ToArray();

            // Set up the SearchRequest to send to elasticsearch.
            Indices       index   = Indices.Index(new string[] { this._apiOptions.AliasName });
            Types         types   = Types.Type(new string[] { "terms" });
            SearchRequest request = new SearchRequest(index, types)
            {
                Query = new TermQuery {
                    Field = "language", Value = language.ToString()
                } &&
                new TermQuery {
                    Field = "audience", Value = audience.ToString()
                } &&
                new TermQuery {
                    Field = "dictionary", Value = dictionary.ToString()
                } &&
                new TermQuery {
                    Field = "first_letter", Value = expandCharacter.ToString()
                }
                ,
                Sort = new List <ISort>
                {
                    new SortField {
                        Field = "term_name"
                    }
                },
                Size   = size,
                From   = from,
                Source = new SourceFilter
                {
                    Includes = requestedESFields
                }
            };

            ISearchResponse <GlossaryTerm> response = null;

            try
            {
                response = await _elasticClient.SearchAsync <GlossaryTerm>(request);
            }
            catch (Exception ex)
            {
                String msg = $"Could not search dictionary '{dictionary}', audience '{audience}', language '{language}', character '{expandCharacter}', size '{size}', from '{from}'.";
                _logger.LogError($"Error searching index: '{this._apiOptions.AliasName}'.");
                _logger.LogError(msg, ex);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = $"Invalid response when searching for '{dictionary}', audience '{audience}', language '{language}', character '{expandCharacter}', size '{size}', from '{from}'.";
                _logger.LogError(msg);
                throw new APIErrorException(500, "errors occured");
            }

            GlossaryTermResults glossaryTermResults = new GlossaryTermResults();

            if (response.Total > 0)
            {
                // Build the array of glossary terms for the returned results.
                List <GlossaryTerm> termResults = new List <GlossaryTerm>();
                foreach (GlossaryTerm res in response.Documents)
                {
                    termResults.Add(res);
                }

                glossaryTermResults.Results = termResults.ToArray();

                // Add the metadata for the returned results
                glossaryTermResults.Meta = new ResultsMetadata()
                {
                    TotalResults = (int)response.Total,
                    From         = from
                };
            }
            else if (response.Total == 0)
            {
                // Add the defualt value of empty GlossaryTerm list.
                glossaryTermResults.Results = new GlossaryTerm[] {};

                // Add the metadata for the returned results
                glossaryTermResults.Meta = new ResultsMetadata()
                {
                    TotalResults = (int)response.Total,
                    From         = from
                };
            }

            return(glossaryTermResults);
        }
        /// <summary>
        /// Search for Term based on the pretty URL name passed.
        /// <param name="dictionary">The value for dictionary.</param>
        /// <param name="audience">Patient or Healthcare provider</param>
        /// <param name="language">The language in which the details needs to be fetched</param>
        /// <param name="prettyUrlName">The pretty url name to search for</param>
        /// <returns>An object of GlossaryTerm</returns>
        /// </summary>
        public async Task <GlossaryTerm> GetByName(string dictionary, AudienceType audience, string language, string prettyUrlName)
        {
            // Set up the SearchRequest to send to elasticsearch.
            Indices       index   = Indices.Index(new string[] { this._apiOptions.AliasName });
            Types         types   = Types.Type(new string[] { "terms" });
            SearchRequest request = new SearchRequest(index, types)
            {
                Query = new TermQuery {
                    Field = "language", Value = language.ToString()
                } &&
                new TermQuery {
                    Field = "audience", Value = audience.ToString()
                } &&
                new TermQuery {
                    Field = "dictionary", Value = dictionary.ToString()
                } &&
                new TermQuery {
                    Field = "pretty_url_name", Value = prettyUrlName.ToString()
                }
                ,
                Sort = new List <ISort>
                {
                    new SortField {
                        Field = "term_name"
                    }
                }
            };

            ISearchResponse <GlossaryTerm> response = null;

            try
            {
                response = await _elasticClient.SearchAsync <GlossaryTerm>(request);
            }
            catch (Exception ex)
            {
                String msg = $"Could not search dictionary '{dictionary}', audience '{audience}', language '{language}', pretty URL name '{prettyUrlName}'.";
                _logger.LogError($"Error searching index: '{this._apiOptions.AliasName}'.");
                _logger.LogError(ex, msg);
                throw new APIErrorException(500, msg);
            }

            if (!response.IsValid)
            {
                String msg = $"Invalid response when searching for dictionary '{dictionary}', audience '{audience}', language '{language}', pretty URL name '{prettyUrlName}'.";
                _logger.LogError(msg);
                throw new APIErrorException(500, "errors occured");
            }

            GlossaryTerm glossaryTerm = new GlossaryTerm();

            // If there is only one term in the response, then the search by pretty URL name was successful.
            if (response.Total == 1)
            {
                glossaryTerm = response.Documents.First();
            }
            else if (response.Total == 0)
            {
                string msg = $"No match for dictionary '{dictionary}', audience '{audience}', language '{language}', pretty URL name '{prettyUrlName}'.";
                _logger.LogDebug(msg);
                throw new APIErrorException(404, msg);
            }
            else
            {
                string msg = $"Incorrect response when searching for dictionary '{dictionary}', audience '{audience}', language '{language}', pretty URL name '{prettyUrlName}'.";
                _logger.LogError(msg);
                throw new APIErrorException(500, "Errors have occured.");
            }

            return(glossaryTerm);
        }