public void Should_Use_Phrase_Suggester_If_Is_Phrase_And_First_Search_Does_Not_Return_Anything_And_Suggested_Phrase_Is_Not_Null()
        {
            const string wrongPhrase   = "web from";
            const string correctPhrase = "web form";
            var          correctPhraseSearchResults = new List <SearchResult> {
                new SearchResult {
                    Id = 123
                }, new SearchResult {
                    Id = 345
                }
            };
            var topPhrase = new RankedPhrase
            {
                Rank    = correctPhraseSearchResults.Count,
                Content = correctPhrase,
                Results = correctPhraseSearchResults
            };

            _siteSearchService.Setup(y => y.GetRawResults(wrongPhrase, false)).Returns(new List <SearchResult>());//return empty list
            _phraseSuggester.Setup(x => x.GetTopPhraseAndResults(wrongPhrase)).Returns(topPhrase);

            _controller = GetController(true);

            var          result = _controller.GetResult(wrongPhrase, null, TopKeywords) as PartialViewResult;
            SiteSearchVm vm     = (SiteSearchVm)result.Model;

            _phraseSuggester.Verify(x => x.GetTopPhraseAndResults(wrongPhrase), Times.Once);
            Assert.AreEqual(vm.SpellCheckerSuggestionWord, topPhrase.Content);
            Assert.AreEqual(vm.SearchedTerm, wrongPhrase);
        }
        public ActionResult GetResult(string keywords, bool?isstrict, string topSearchKeywords)
        {
            bool isStrictQuery = isstrict ?? false;
            //TODO: use Input Sanitizer instead
            var  keywordsCollection        = InputSanitiser.ExtractWordsFromInput(keywords).ToList(); //sanitise input
            bool isPhrase                  = keywordsCollection.Count > 1;
            var  sanitisedKeywordsAsString = string.Join(" ", keywordsCollection);
            var  didYouMean                = sanitisedKeywordsAsString;

            //First search exact word
            var results = _siteSearchService.GetRawResults(sanitisedKeywordsAsString, false).ToList();

            //If no reasults and q is not strict - search fuzzy but display the corrected word
            if (!results.Any() && !isStrictQuery && keywordsCollection.Any())
            {
                if (isPhrase)
                {
                    RankedPhrase topPhrase = PhraseSuggester.GetTopPhraseAndResults(sanitisedKeywordsAsString);
                    if (topPhrase != null)
                    {
                        didYouMean = topPhrase.Content;
                        results    = topPhrase.Results.ToList();
                    }
                }
                else
                {
                    didYouMean = _spellChecker.Check(sanitisedKeywordsAsString);
                    results    = _siteSearchService.GetRawResults(sanitisedKeywordsAsString, true).ToList();
                }
            }

            //Finally if still no results return the partial with top search words suggestions
            if (!results.Any())
            {
                List <string> topSearchedTerms = topSearchKeywords.Split(',').ToList();
                var           vm = new NoResultsFoundVm {
                    SearchedTerm = sanitisedKeywordsAsString, Keywords = topSearchedTerms
                };

                return(PartialView("_NoSearchResultsAndSuggestions", vm));
            }
            else
            {
                var contentResults = _siteSearchService.ConvertSearchResultToPublishedContentWithTemplate(results, ContentCache, UmbracoContext);
                var mappedResults  = _siteSearchService.MapToCustomResults(contentResults);

                var vm = new SiteSearchVm
                {
                    SearchedTerm = sanitisedKeywordsAsString,
                    SpellCheckerSuggestionWord = didYouMean,
                    SearcResults = mappedResults,
                };

                return(PartialView("_SiteSearchResultsAndSuggestionsPartial", vm));
            }
        }
        public RankedPhrase GetTopPhraseAndResults(string keywords)
        {
            var termsInPhrase       = InputSanitiser.ExtractWordsFromInput(keywords).Take(3).ToList(); //get up to 3 real words and sanitised
            var termsTop5Candidates = new List <List <string> >();

            foreach (var word in termsInPhrase)
            {
                //if exact match exists it will be included in the top suggestions list as well as the other 5
                var topSpellCheckerSuggestions = _spellChecker.GetTopSuggestions(word, 4);
                var topSpellCheckerSuggestionsStoWordsCleaned = InputSanitiser.GetRealWordsOnly(topSpellCheckerSuggestions);
                termsTop5Candidates.Add(topSpellCheckerSuggestionsStoWordsCleaned);
            }

            //The inutual null is used so that the first cross-join will have something to build on (with strings, null + s = s)
            IEnumerable <string> combinations = new List <string> {
                null
            };

            foreach (var list in termsTop5Candidates)
            {
                // cross join the current result with each member of the next list
                combinations = combinations.SelectMany(o => list.Select(s => o + " " + s));
            }

            //for reference: https://stackoverflow.com/questions/12251874/when-to-use-a-parallel-foreach-loop-instead-of-a-regular-foreach
            var phraseseAndResults = new ConcurrentBag <RankedPhrase>();

            Parallel.ForEach(combinations, currentCombination =>
            {
                var trimmedItem        = currentCombination.Trim(); //trim white space
                var searchResultsItems = _siteSearchService.GetRawResults(trimmedItem, false).ToList();
                var phrase             = new RankedPhrase
                {
                    Rank    = searchResultsItems.Count,
                    Results = searchResultsItems,
                    Content = trimmedItem
                };
                phraseseAndResults.Add(phrase);
            });

            return(phraseseAndResults.OrderByDescending(x => x.Rank).FirstOrDefault());
        }