예제 #1
0
        private static List <MPObject> FilterBySearchParameters(List <MPObject> listToFilter, SearchParameters searchParameters)
        {
            List <MPObject> result = listToFilter.ToList();

            if (searchParameters == null)
            {
                return(result);
            }

            if (searchParameters.OnlyRoutes)
            {
                result.RemoveAll(p => !(p is Route));
            }

            if (searchParameters.OnlyAreas)
            {
                result.RemoveAll(p => !(p is Area));
            }

            //Note: searchParameters.SpecificLocation is handled by the GetPossibleQueryAndLocationGroups function

            return(result);
        }
예제 #2
0
        private static MPObject DetermineBestMatch(List <MPObject> allMatches, string searchQuery, SearchParameters searchParameters = null)
        {
            allMatches = FilterBySearchParameters(allMatches, searchParameters);

            if (allMatches.Count == 0)
            {
                return(null);
            }

            //=============== CHOOSE THE BEST MATCH ===============
            //First priority: items where the name matches the search query exactly CASE SENSITIVE
            List <MPObject> filteredItems = allMatches.Where(p => Utilities.StringsEqual(searchQuery, p.Name, false)).ToList();

            //Second priority: items where the name matches the search query exactly (but case-insensitive)
            if (!filteredItems.Any())
            {
                filteredItems = allMatches.Where(p => Utilities.StringsEqual(searchQuery, p.Name)).ToList();
            }

            //Third priority: items where the name matches the FILTERED (no symbols or spaces, case insensitive) search query exactly
            if (!filteredItems.Any())
            {
                filteredItems = allMatches.Where(p => Utilities.StringsEqual(Utilities.FilterStringForMatch(searchQuery), p.NameForMatch)).ToList();
            }

            //[IN THE FUTURE]Fourth priority: items with a levenshtein distance less than 3

            //Fifth priority: items where the name contains the search query CASE SENSITIVE
            if (!filteredItems.Any())
            {
                filteredItems = allMatches.Where(p => Utilities.StringContains(p.Name, searchQuery, false)).ToList();
            }

            //Sixth priority: items where the name contains the search query (case-insensitive)
            if (!filteredItems.Any())
            {
                filteredItems = allMatches.Where(p => Utilities.StringContains(p.Name, searchQuery)).ToList();
            }

            //Seventh priority: items where the name contains the FITLERED search query (case-insensitive)
            if (!filteredItems.Any())
            {
                filteredItems = allMatches.Where(p => Utilities.StringContains(p.NameForMatch, Utilities.FilterStringForMatch(searchQuery))).ToList();
            }

            //=============== RESULTS PROCESSING ===============
            //Remove parents where the child was also matched (eg "Alexisizer" area vs "Alexisizer" route)
            filteredItems.RemoveAll(parent => filteredItems.Find(child => child.ParentIDs.Contains(parent.ID)) != null);

            //=============== RETURN RESULTS ===============
            //Return the most popular filtered item
            if (filteredItems.Any())
            {
                return(FilterByPopularity(filteredItems));
            }

            //Otherwise, if we haven't matched anything above, just return the most popular item out of allMatches
            return(FilterByPopularity(allMatches));
        }
예제 #3
0
        private static List <Tuple <string, string> > GetPossibleQueryAndLocationGroups(string queryText, SearchParameters searchParameters = null)
        {
            List <Tuple <string, string> > result = new List <Tuple <string, string> >();

            if (searchParameters != null && !string.IsNullOrEmpty(searchParameters.SpecificLocation))
            {
                result.Add(new Tuple <string, string>(queryText, searchParameters.SpecificLocation));
            }
            else
            {
                result.Add(new Tuple <string, string>(queryText, "")); //Add the full query as a possible match

                Regex  locationWordsRegex = new Regex(@"(\s+of\s+)|(\s+on\s+)|(\s+at\s+)|(\s+in\s+)");
                string possibleSearchText, possibleLocation;

                if (queryText.Contains(",")) //Location by comma (eg "Send me on my way, Red River Gorge")
                {
                    possibleSearchText = queryText.Split(',')[0].Trim();
                    possibleLocation   = queryText.Split(',')[1].Trim();

                    result.Add(new Tuple <string, string>(possibleSearchText, possibleLocation));
                }
                else if (locationWordsRegex.IsMatch(queryText)) //Location by "location word" (eg "Butterfly Blue in Red River Gorge")
                {
                    foreach (Match match in locationWordsRegex.Matches(queryText))
                    {
                        possibleSearchText = queryText.Split(new string[] { match.Value }, StringSplitOptions.None)[0].Trim();
                        possibleLocation   = queryText.Split(new string[] { match.Value }, StringSplitOptions.None)[1].Trim();
                        result.Add(new Tuple <string, string>(possibleSearchText, possibleLocation));
                    }
                }
            }

            return(result);
        }
예제 #4
0
        private static SearchResult DetermineBestMatch(List <SearchResult> searchResults, string searchQuery, SearchParameters searchParameters = null)
        {
            MPObject bestMatch = DetermineBestMatch(searchResults.Select(p => p.FilteredResult).ToList(), searchQuery, searchParameters);

            return(searchResults.Find(p => p.FilteredResult == bestMatch));
        }
예제 #5
0
        public static SearchResult Search(string queryText, SearchParameters searchParameters = null)
        {
            WriteToConsole($"\tGetting info from MountainProject for \"{queryText}\"");
            Stopwatch    searchStopwatch = Stopwatch.StartNew();
            SearchResult searchResult;

            List <SearchResult>            possibleResults = new List <SearchResult>();
            List <Tuple <string, string> > possibleQueryAndLocationGroups = GetPossibleQueryAndLocationGroups(queryText, searchParameters);

            foreach (Tuple <string, string> group in possibleQueryAndLocationGroups)
            {
                string query    = Utilities.FilterStringForMatch(Utilities.EnforceWordConsistency(group.Item1));
                string location = Utilities.FilterStringForMatch(Utilities.EnforceWordConsistency(group.Item2));

                List <MPObject> possibleMatches = DeepSearch(query, DestAreas);
                possibleMatches = FilterBySearchParameters(possibleMatches, searchParameters);

                MPObject     filteredResult;
                SearchResult possibleResult;
                if (!string.IsNullOrEmpty(location))
                {
                    Dictionary <MPObject, Area> resultsWithLocations = GetMatchingResultLocationPairs(possibleMatches, location);
                    filteredResult = DetermineBestMatch(resultsWithLocations.Keys.ToList(), group.Item1, searchParameters);

                    if (filteredResult == null)
                    {
                        continue;
                    }

                    possibleResult = new SearchResult()
                    {
                        AllResults      = resultsWithLocations.Keys.ToList(),
                        FilteredResult  = filteredResult,
                        RelatedLocation = resultsWithLocations[filteredResult]
                    };
                }
                else
                {
                    filteredResult = DetermineBestMatch(possibleMatches, group.Item1, searchParameters);

                    if (filteredResult == null)
                    {
                        continue;
                    }

                    possibleResult = new SearchResult()
                    {
                        AllResults     = possibleMatches,
                        FilteredResult = filteredResult
                    };
                }

                if (!possibleResult.IsEmpty())
                {
                    possibleResults.Add(possibleResult);
                }
            }

            if (possibleResults.Any())
            {
                searchResult = DetermineBestMatch(possibleResults, queryText, searchParameters);
            }
            else if (searchParameters != null && !string.IsNullOrEmpty(searchParameters.SpecificLocation))
            {
                //If we used searchParameters to find a specific location, but couldn't find a match at that location,
                //we should return an empty result
                searchResult = new SearchResult();
            }
            else
            {
                string          filteredQuery  = Utilities.FilterStringForMatch(queryText);
                List <MPObject> results        = FilterBySearchParameters(DeepSearch(filteredQuery, DestAreas), searchParameters);
                MPObject        filteredResult = DetermineBestMatch(results, queryText, searchParameters);
                searchResult = new SearchResult()
                {
                    AllResults     = results,
                    FilteredResult = filteredResult
                };
            }

            WriteToConsole($"\tFound {searchResult.AllResults.Count} matching results from MountainProject in {searchStopwatch.ElapsedMilliseconds} ms");

            searchResult.TimeTakenMS = searchStopwatch.ElapsedMilliseconds;

            return(searchResult);
        }