public void GivenSearchParametersWhenSearchServicesGatewayMethodIsCalledThenItReturnsResultsMatchingNameAndDescription()
        {
            // arrange
            var services   = EntityHelpers.CreateServices(10);
            var searchTerm = Randomm.Text();

            searchTerm = " " + searchTerm;//15 Feb 2021 - Change made so we only search for whole words in the service description! - So we add a space.

            services.First().Name += searchTerm;
            services[1].Description += searchTerm;
            var expectedData = new List <Service>();

            expectedData.Add(services.First());
            expectedData.Add(services[1]);
            var requestParams = new SearchServicesRequest();

            requestParams.Search = searchTerm;
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.SaveChanges();

            // act
            var gatewayResult = _classUnderTest.SearchServices(requestParams);
            var fullMatches   = gatewayResult.FullMatchServices;

            // assert
            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            fullMatches.Count.Should().Be(2);
        }
        public void GivenSearchParametersWhenSearchServicesGatewayMethodIsCalledThenItReturnsMatchingRecordsWithOrgNameHigherThanServiceName()
        {
            // arrange
            var services   = EntityHelpers.CreateServices(10);
            var searchTerm = Randomm.Text();

            services.First().Name += searchTerm;
            services[1].Organization.Name += searchTerm;
            var expectedData = new List <Service>();

            expectedData.Add(services.First());
            expectedData.Add(services[1]);
            var requestParams = new SearchServicesRequest();

            requestParams.Search = searchTerm;
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.SaveChanges();

            // act
            var gatewayResult = _classUnderTest.SearchServices(requestParams);
            var fullMatches   = gatewayResult.FullMatchServices;

            // assert
            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            fullMatches.Count.Should().Be(2);
            fullMatches[0].Name.Should().Be(services[1].Name);
            fullMatches[1].Name.Should().Be(services[0].Name);
        }
Exemple #3
0
        public void GivenAUrlEncodedSearchTermGatewayIsCalledWithDecodedTerm() // implementation of this test fixes a front-end bug, where front-end app accidentally encodes the url twice before calling an API - I don't we should be 'fixing' this on back-end API.
        {
            var expectedServices = Randomm.SSGatewayResult();

            _mockServicesGateway.Setup(g => g.SearchServices(It.IsAny <SearchServicesRequest>())).Returns(expectedServices); // dummy setup - irrelevant for the test
            var searchTerm       = Randomm.Text();
            var urlencodedSearch = searchTerm.Replace(" ", "%2520");
            var reqParams        = new SearchServicesRequest();

            reqParams.Search = urlencodedSearch;
            _classUnderTest.ExecuteGet(reqParams);
            _mockServicesGateway.Verify(g => g.SearchServices(It.Is <SearchServicesRequest>(p => p.Search == searchTerm)), Times.Once);
        }
        public GetServiceResponseList ExecuteGet(SearchServicesRequest requestParams)
        {
            requestParams.Search = UrlHelper.DecodeParams(requestParams.Search);
            var postcodeIsGiven = !string.IsNullOrEmpty(requestParams.PostCode);

            var gatewayResponse = _servicesGateway.SearchServices(requestParams);
            var fullMServices   = gatewayResponse.FullMatchServices.ToResponseServices();
            var splitMServices  = gatewayResponse.SplitMatchServices.ToResponseServices();


            var metadata = new Metadata();

            metadata.PostCode = postcodeIsGiven ? requestParams.PostCode : null;

            if (postcodeIsGiven)
            {
                try
                {
                    //Get the postcode's coordinates
                    Coordinate?postcodeCoord = _addressesGateway.GetPostcodeCoordinates(requestParams.PostCode);

                    if (postcodeCoord.HasValue)
                    {
                        metadata.PostCodeLatitude  = postcodeCoord.Value.Latitude;
                        metadata.PostCodeLongitude = postcodeCoord.Value.Longitude;

                        fullMServices.CalculateServiceLocationDistances(postcodeCoord.Value);
                        splitMServices.CalculateServiceLocationDistances(postcodeCoord.Value);
                    }
                    else
                    {
                        metadata.Error = "Postcode coordinates not found.";
                    }
                }
                catch (Exception ex)
                {
                    metadata.Error = ex.Message;
                }
            }

            if (postcodeIsGiven)       // And metadata error is empty
            {
                fullMServices.Sort();  // Sort by minimum service location's distance
                splitMServices.Sort(); // IComparator<Service> is defined on the object iteself
            }

            var usecaseResponse = ServiceFactory.SearchServiceUsecaseResponse(fullMServices, splitMServices, metadata);

            return(usecaseResponse);
        }
Exemple #5
0
 public IActionResult SearchServices([FromQuery] SearchServicesRequest requestParams)
 {
     try
     {
         var usecaseResult = _servicesUseCase.ExecuteGet(requestParams);
         return(Ok(usecaseResult));
     }
     catch (Exception ex) when(ex.InnerException != null)
     {
         return(StatusCode(500, new ErrorResponse(ex.Message, ex.InnerException.Message)));
     }
     catch (Exception ex)
     {
         return(StatusCode(500, new ErrorResponse(ex.Message)));
     }
 }
        public void GivenMultipleTaxonomyIdSearchParametersWhenSearchServicesGatewayMethodIsCalledThenItReturnsMatchingTaxonomyIdResults()
        {
            var taxonomy1 = EntityHelpers.CreateTaxonomy();
            var taxonomy2 = EntityHelpers.CreateTaxonomy();

            taxonomy1.Vocabulary = "demographic";
            taxonomy2.Vocabulary = "category";
            var services         = EntityHelpers.CreateServices();
            var serviceToFind1   = EntityHelpers.CreateService();
            var serviceToFind2   = EntityHelpers.CreateService();
            var serviceTaxonomy1 = EntityHelpers.CreateServiceTaxonomy();
            var serviceTaxonomy2 = EntityHelpers.CreateServiceTaxonomy();
            var serviceTaxonomy3 = EntityHelpers.CreateServiceTaxonomy();
            var serviceTaxonomy4 = EntityHelpers.CreateServiceTaxonomy();

            serviceTaxonomy1.Service  = serviceToFind1;
            serviceTaxonomy1.Taxonomy = taxonomy1;
            serviceTaxonomy2.Service  = serviceToFind1;
            serviceTaxonomy2.Taxonomy = taxonomy2;
            serviceTaxonomy3.Service  = serviceToFind2;
            serviceTaxonomy3.Taxonomy = taxonomy1;
            serviceTaxonomy4.Service  = serviceToFind2;
            serviceTaxonomy4.Taxonomy = taxonomy2;
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.Services.Add(serviceToFind1);
            DatabaseContext.Services.Add(serviceToFind2);
            DatabaseContext.ServiceTaxonomies.Add(serviceTaxonomy1);
            DatabaseContext.ServiceTaxonomies.Add(serviceTaxonomy2);
            DatabaseContext.ServiceTaxonomies.Add(serviceTaxonomy3);
            DatabaseContext.ServiceTaxonomies.Add(serviceTaxonomy4);
            DatabaseContext.SaveChanges();
            var requestParams = new SearchServicesRequest();

            requestParams.TaxonomyIds = new List <int> {
                taxonomy1.Id, taxonomy2.Id
            };
            var expectedData = new List <Service>();

            expectedData.Add(serviceToFind1);
            expectedData.Add(serviceToFind2);
            var gatewayResult = _classUnderTest.SearchServices(requestParams);
            var fullMatches   = gatewayResult.FullMatchServices;

            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            fullMatches.Count.Should().Be(expectedData.Count);
        }
        [TestCase(TestName = "Given user provided search term consisting of multiple words, When services get filtered in SearchService method, Then the returned services are categorized into Full user input match Or Split match.")] // done so to ensure the less relevant services are in the separate collection
        public void SearchServiceGatewaySeparatesOutFullMatchResultsFromSplitMatch()
        {
            // arrange
            var word1           = Randomm.Word();
            var word2           = Randomm.Word();
            var userSearchInput = $"{word1} {word2}";

            var request = new SearchServicesRequest()
            {
                Search = userSearchInput
            };

            var services       = new List <Service>();
            var serviceToFind1 = EntityHelpers.CreateService();                 // full match

            serviceToFind1.Name += userSearchInput;
            var serviceToFind2 = EntityHelpers.CreateService();                 // split match 1

            serviceToFind2.Name += word1;
            var serviceToFind3 = EntityHelpers.CreateService();                 // split match 2

            serviceToFind3.Name += word2;

            services.Add(serviceToFind1);
            services.Add(serviceToFind2);
            services.Add(serviceToFind3);
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.SaveChanges();

            // act
            var gatewayResult = _classUnderTest.SearchServices(request);
            var fullMatches   = gatewayResult.FullMatchServices;
            var splitMatches  = gatewayResult.SplitMatchServices;

            // assert
            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            splitMatches.Should().NotBeNull();

            fullMatches.Should().Contain(s => s.Name.Contains(userSearchInput, StringComparison.OrdinalIgnoreCase));
            fullMatches.Should().HaveCount(1);

            splitMatches.Should().Contain(s => s.Name.Contains(word1, StringComparison.OrdinalIgnoreCase));
            splitMatches.Should().Contain(s => s.Name.Contains(word2, StringComparison.OrdinalIgnoreCase));
            splitMatches.Should().NotContain(s => s.Name.Contains(userSearchInput, StringComparison.OrdinalIgnoreCase));
            splitMatches.Should().HaveCount(2);
        }
        public void WhenDoingPartialTextSearchMatchesWordsOf3orLessCharactersLongGetIgnored()  //it's too short - these will be searched and found inside other words in DB
        {
            // arrange
            var shortWordList = new List <string> {
                "and", "a", "an", "the", "bfg", "42"
            };
            var shortWord = shortWordList.RandomItem();

            var word            = Randomm.Word().Replace(shortWord, "test");// have to ensure the shortword is not contained in the actual word for the sake of test
            var userSearchInput = $"{shortWord} {word}";

            var request = new SearchServicesRequest()
            {
                Search = userSearchInput
            };

            var services = EntityHelpers.CreateServices(5).ToList();             //dummy services

            services.ForEach(s => s.Name = s.Name.Replace(word, "ssj"));         //make sure they don't match the search word
            //assuming there's no full match. due to full match containing a shortword, the assertion at the bottom wouldn't be able to test what's needed.
            var serviceToFind = EntityHelpers.CreateService();                   // word 1 match

            serviceToFind.Name  = serviceToFind.Name.Replace(shortWord, "test"); //ensuring random hash does not contain shortword. for the assertion bellow to work as intended, the service name's hash part should not contain shortword.
            serviceToFind.Name += word;

            var serviceToNotFind = EntityHelpers.CreateService();                   // shortword no match. this ensures that the test can fail if the implementation is wrong or not present.

            serviceToNotFind.Name  = serviceToNotFind.Name.Replace(word, "1234");   // make sure the mismatching service does not contain a desired search term
            serviceToNotFind.Name += shortWord;

            services.Add(serviceToFind);
            services.Add(serviceToNotFind);
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.SaveChanges();

            // act
            var gatewayResult = _classUnderTest.SearchServices(request);
            var splitMatches  = gatewayResult.SplitMatchServices;

            // assert
            gatewayResult.Should().NotBeNull();
            splitMatches.Should().NotBeNull();

            splitMatches.Should().NotContain(s => s.Name.Contains(shortWord, StringComparison.OrdinalIgnoreCase));
            splitMatches.Should().HaveCount(1);
        }
        public void UponFilteringServicesByAMultiWordInputTheReturnedResultsIncludePartialMatches()
        {
            // arrange
            var word1           = Randomm.Word();
            var word2           = Randomm.Word();
            var userSearchInput = $"{word1} {word2}";

            var request = new SearchServicesRequest()
            {
                Search = userSearchInput
            };

            var services       = EntityHelpers.CreateServices(5).ToList();      // dummy services
            var serviceToFind1 = EntityHelpers.CreateService();                 // full match

            serviceToFind1.Name += userSearchInput;
            var serviceToFind2 = EntityHelpers.CreateService();                 // word 1 match

            serviceToFind2.Name += word1;
            var serviceToFind3 = EntityHelpers.CreateService();                 // word 2 match

            serviceToFind3.Name += word2;

            services.Add(serviceToFind1);
            services.Add(serviceToFind2);
            services.Add(serviceToFind3);
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.SaveChanges();

            // act
            var gatewayResult    = _classUnderTest.SearchServices(request);
            var fullMatches      = gatewayResult.FullMatchServices;
            var splitMatches     = gatewayResult.SplitMatchServices;
            var returnedServices = fullMatches.Concat(splitMatches).ToList();

            // assert
            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            splitMatches.Should().NotBeNull();

            returnedServices.Should().Contain(s => s.Name.Contains(userSearchInput, StringComparison.OrdinalIgnoreCase));
            returnedServices.Should().Contain(s => s.Name.Contains(word1, StringComparison.OrdinalIgnoreCase));
            returnedServices.Should().Contain(s => s.Name.Contains(word2, StringComparison.OrdinalIgnoreCase));
            returnedServices.Should().HaveCount(3);
        }
        public void GivenSearchParametersWhenSearchServicesGatewayMethodIsCalledThenItReturnsMatchingSynonymGroupResults()
        {
            // arrange
            var synonymGroup1 = EntityHelpers.CreateSynonymGroupWithWords(5);
            var synonymGroup2 = EntityHelpers.CreateSynonymGroupWithWords(3);

            synonymGroup2.SynonymWords.ToList()[1].Word = synonymGroup1.SynonymWords.ToList()[1].Word;
            var services = EntityHelpers.CreateServices();

            services.ForEach(s => s.Name = "irrelevant");
            var serviceToFind1 = EntityHelpers.CreateService();
            var serviceToFind2 = EntityHelpers.CreateService();
            var searchTerm     = synonymGroup1.SynonymWords.ToList()[1].Word;
            var requestParams  = new SearchServicesRequest();

            requestParams.Search = searchTerm;
            serviceToFind1.Name += synonymGroup1.SynonymWords.ToList()[4].Word;
            serviceToFind2.Name += synonymGroup2.SynonymWords.ToList()[2].Word;
            DatabaseContext.SynonymGroups.Add(synonymGroup1);
            DatabaseContext.SynonymGroups.Add(synonymGroup2);
            DatabaseContext.Services.AddRange(services);
            DatabaseContext.Services.Add(serviceToFind1);
            DatabaseContext.Services.Add(serviceToFind2);
            DatabaseContext.SaveChanges();
            var expectedData = new List <Service>();

            expectedData.Add(serviceToFind1);
            expectedData.Add(serviceToFind2);

            // act
            var gatewayResult    = _classUnderTest.SearchServices(requestParams);
            var fullMatches      = gatewayResult.FullMatchServices;
            var splitMatches     = gatewayResult.SplitMatchServices;
            var returnedServices = fullMatches.Concat(splitMatches).ToList();

            // assert
            gatewayResult.Should().NotBeNull();
            fullMatches.Should().NotBeNull();
            splitMatches.Should().NotBeNull();

            returnedServices.Count.Should().Be(expectedData.Count);
        }
        public void WholeAndSplitUserInputMatchesAreReturnedInCorrectRank()
        {
            // arrange
            var searchWord1    = Randomm.Word();                         // word that won't match any results, however 1 of its synonyms from synonym group will
            var searchWord2    = Randomm.Word();                         // same as above, but the matching synonym will be in another synonym group
            var irrelevantWord = Randomm.Word();                         // a control word, that won't match anything, nor its synonyms will.

            var userInput = $"{searchWord1} "
                            + $"{irrelevantWord} "
                            + $"{searchWord2}";

            var request = new SearchServicesRequest();

            request.Search = userInput;

            var bridgeSyn1Word = Utility.SuperSetOfString(searchWord1);     // A superset word of search word 1 that will relate to a synonym word inside synonym group 1 - this word has no match in the DB
            var bridgeSyn2Word = Utility.SuperSetOfString(searchWord2);     // A superset word of search word 2 that will relate to a synonym word inside synonym group 2 - this word has no match in the DB


            var synWord1 = Randomm.Word();                            // synonym within the same synonym group (1) as a word related to search word 1 - this word has a match in the DB
            var synWord2 = Randomm.Word();                            // synonym within the same synonym group (1) as a word related to search word 1 - this word has a match in the DB
            var synWord3 = Randomm.Word();                            // synonym within the same synonym group (2) as a word related to search word 2 - this word has a match in the DB


            var synonymGroup1 = EntityHelpers.CreateSynonymGroupWithWords();            // relevant group with dummy synonym words
            var synonymGroup2 = EntityHelpers.CreateSynonymGroupWithWords();            // relevant group with dummy synonym words
            var dummySynGroup = EntityHelpers.CreateSynonymGroupWithWords();            // dummy synonym group that should not be picked up

            var bridgeSynonym1 = EntityHelpers.SynWord(synonymGroup1, bridgeSyn1Word);  // synonym that has no match in DB, however it bridges user input search word with the synonym group 1
            var bridgeSynonym2 = EntityHelpers.SynWord(synonymGroup2, bridgeSyn2Word);

            var matchSynonym1 = EntityHelpers.SynWord(synonymGroup1, synWord1);         // creating a synonym word object to insert that will have a match, creating a link with synonym group 1
            var matchSynonym2 = EntityHelpers.SynWord(synonymGroup1, synWord2);
            var matchSynonym3 = EntityHelpers.SynWord(synonymGroup2, synWord3);


            synonymGroup1.SynonymWords.Add(bridgeSynonym1);                  // added bridge synonym to the synonym group
            synonymGroup2.SynonymWords.Add(bridgeSynonym2);

            synonymGroup1.SynonymWords.Add(matchSynonym1);                   // added match synonym into a synonym group
            synonymGroup1.SynonymWords.Add(matchSynonym2);
            synonymGroup2.SynonymWords.Add(matchSynonym3);


            var services = EntityHelpers.CreateServices(5);                               // creating list of dummy services that should not be found

            var matchService1 = EntityHelpers.CreateService();                            // service that is intended to be found through the synonym of synonym group 1
            var matchService2 = EntityHelpers.CreateService();                            // service that is intended to be found through the synonym of synonym group 1
            var matchService3 = EntityHelpers.CreateService();                            // service that is intended to be found through the synonym of synonym group 2
            var matchService4 = EntityHelpers.CreateService();                            // service that is intended to be found through the main search term

            matchService1.Name += searchWord2;                                            // creating a link between a service and a match synonym 1
                                                                                          // creating a link between a service and a match synonym 2
            matchService2.Description += " " + synWord2;                                  //15 Feb 2021 - Change made so we only search for whole words in the service description! - So we add a space.

            matchService3.Organization.Name += synWord3;                                  // creating a link between a service and a match synonym 3
            matchService4.Organization.Name += searchWord1;                               // creating a link between a service and a main search word

            services.AddMany(matchService1, matchService2, matchService3, matchService4); // include match services into a to be inserted services collection

            DatabaseContext.SynonymGroups.AddRange(synonymGroup1);                        // adding synonym groups containing synonym words into a database
            DatabaseContext.SynonymGroups.AddRange(synonymGroup2);
            DatabaseContext.SynonymGroups.AddRange(dummySynGroup);
            DatabaseContext.Services.AddRange(services);                     // adding services into a database

            DatabaseContext.SaveChanges();

            // act
            var gatewayResult = _classUnderTest.SearchServices(request);
            var splitMatches  = gatewayResult.SplitMatchServices;
            var fullMatches   = gatewayResult.FullMatchServices;

            // assert
            splitMatches.Should().HaveCount(4);
            fullMatches.Should().HaveCount(0);
            splitMatches[0].Name.Should().Be(matchService4.Name);
            splitMatches[1].Name.Should().Be(matchService1.Name);
            splitMatches[2].Name.Should().Be(matchService3.Name);
            splitMatches[3].Name.Should().Be(matchService2.Name);
        }
Exemple #12
0
        public SearchServiceGatewayResult SearchServices(SearchServicesRequest requestParams)
        {
            var baseQuery = _context.Services
                            .Include(s => s.Image)
                            .Include(s => s.Organization)
                            .Include(s => s.ServiceLocations)
                            .Include(s => s.ServiceTaxonomies)
                            .ThenInclude(st => st.Taxonomy)
                            .Where(s => s.Organization.Status.ToLower() == "published")
                            .AsEnumerable();

            IEnumerable <Service> fullMatchServicesQuery  = baseQuery,
                                  splitMatchServicesQuery = baseQuery;

            List <ServiceEntity> fullMatchServices  = new List <ServiceEntity>(),
                                 splitMatchServices = new List <ServiceEntity>();

            var synonyms = new HashSet <string>();

            var demographicTaxonomies = _context.Taxonomies
                                        .Where(t => t.Vocabulary == "demographic" && requestParams.TaxonomyIds.Any(ti => ti == t.Id))
                                        .Select(t => t.Id).ToList();

            var categoryTaxonomies = _context.Taxonomies
                                     .Where(t => t.Vocabulary == "category" && requestParams.TaxonomyIds.Any(ti => ti == t.Id))
                                     .Select(t => t.Id).ToList();

            if (demographicTaxonomies != null && demographicTaxonomies.Count != 0)
            {
                fullMatchServicesQuery = fullMatchServicesQuery
                                         .Where(s => s.ServiceTaxonomies
                                                .Any(st => demographicTaxonomies.Contains(st.TaxonomyId)));
            }

            if (categoryTaxonomies != null && categoryTaxonomies.Count != 0)
            {
                fullMatchServicesQuery = fullMatchServicesQuery
                                         .Where(s => s.ServiceTaxonomies
                                                .Any(st => categoryTaxonomies.Contains(st.TaxonomyId)));
            }

            if (!string.IsNullOrWhiteSpace(requestParams.Search))
            {
                var searchInputText = requestParams.Search.ToLower();
                var splitWords      = searchInputText
                                      .Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();

                var moreThan1SearchInputWord = splitWords.Count > 1;       // fuzzy search won't execute if only 1 word entered

                splitWords = splitWords.Where(k => k.Length > 3).ToList(); // filter short words

                var matchedSynonyms = _context.SynonymWords                // This variable is not being used! Does it need to be here?
                                      .Include(sw => sw.Group)
                                      .ThenInclude(sg => sg.SynonymWords)
                                      .Where(x => x.Word.ToLower().Contains(searchInputText))
                                      .Select(sw => sw.Group.SynonymWords.Select(sw => synonyms.Add(sw.Word.ToLower())) // hard to track side effects >_<
                                              .ToList())
                                      .ToList();

                // Filter on service organisation name
                Predicate <Service> containsUserInput  = service => service.Organization.Name.ToLower().Contains(searchInputText);
                Predicate <Service> containsAnySynonym = service => synonyms.Any(sn => service.Organization.Name.ToLower().Contains(sn));
                ApplyFullTextFilter(containsUserInput,
                                    containsAnySynonym,
                                    synonyms,
                                    fullMatchServicesQuery,
                                    fullMatchServices);

                // Filter on service name
                containsUserInput  = service => service.Name.ToLower().Contains(searchInputText);
                containsAnySynonym = service => synonyms.Any(sn => service.Name.ToLower().Contains(sn));
                ApplyFullTextFilter(containsUserInput,
                                    containsAnySynonym,
                                    synonyms,
                                    fullMatchServicesQuery,
                                    fullMatchServices);

                // Filter on service description
                containsUserInput  = service => service.Description.ContainsWord(searchInputText);
                containsAnySynonym = service => synonyms.AnyWord(service.Description);
                ApplyFullTextFilter(containsUserInput,
                                    containsAnySynonym,
                                    synonyms,
                                    fullMatchServicesQuery,
                                    fullMatchServices);

                // More than one word in search input
                if (moreThan1SearchInputWord)
                {
                    var splitWordSynonyms = _context.SynonymWords
                                            .Include(sw => sw.Group)
                                            .ThenInclude(sg => sg.SynonymWords)
                                            .AsEnumerable()
                                            .Where(sw => splitWords.Any(spw => sw.Word.ToLower().Contains(spw)))
                                            .SelectMany(sw => sw.Group.SynonymWords)
                                            .Select(sw => sw.Word.ToLower())
                                            .ToHashSet();

                    // Filter words on service organisation name
                    Predicate <Service> containsSplitUserInput = service => splitWords.Any(spw => service.Organization.Name.ToLower().Contains(spw));

                    ApplySplitMatchFilter(containsSplitUserInput,
                                          splitMatchServicesQuery,
                                          demographicTaxonomies,
                                          categoryTaxonomies,
                                          splitMatchServices,
                                          fullMatchServices);

                    // Filter words on service name
                    containsSplitUserInput = service => splitWords.Any(spw => service.Name.ToLower().Contains(spw));

                    ApplySplitMatchFilter(containsSplitUserInput,
                                          splitMatchServicesQuery,
                                          demographicTaxonomies,
                                          categoryTaxonomies,
                                          splitMatchServices,
                                          fullMatchServices);

                    // Filter words on service description
                    containsSplitUserInput = service => splitWords.AnyWord(service.Description);
                    ApplySplitMatchFilter(containsSplitUserInput,
                                          splitMatchServicesQuery,
                                          demographicTaxonomies,
                                          categoryTaxonomies,
                                          splitMatchServices,
                                          fullMatchServices);

                    // Filter synonyms on organisation name
                    Predicate <Service> containsAnySplitInputSynonym = service => splitWordSynonyms.Any(spwsn => service.Organization.Name.ToLower().Contains(spwsn));
                    ApplySplitSynonymsMatchFilter(containsAnySplitInputSynonym,
                                                  splitMatchServicesQuery,
                                                  splitWordSynonyms,
                                                  demographicTaxonomies,
                                                  categoryTaxonomies,
                                                  splitMatchServices,
                                                  fullMatchServices);

                    // Filter synonyms on service name
                    containsAnySplitInputSynonym = service => splitWordSynonyms.Any(spwsn => service.Name.ToLower().Contains(spwsn));
                    ApplySplitSynonymsMatchFilter(containsAnySplitInputSynonym,
                                                  splitMatchServicesQuery,
                                                  splitWordSynonyms,
                                                  demographicTaxonomies,
                                                  categoryTaxonomies,
                                                  splitMatchServices,
                                                  fullMatchServices);

                    // Filter synonyms on service description
                    containsAnySplitInputSynonym = service => splitWordSynonyms.AnyWord(service.Description);
                    ApplySplitSynonymsMatchFilter(containsAnySplitInputSynonym,
                                                  splitMatchServicesQuery,
                                                  splitWordSynonyms,
                                                  demographicTaxonomies,
                                                  categoryTaxonomies,
                                                  splitMatchServices,
                                                  fullMatchServices);
                }
            }
            else
            {
                AddToCollection(fullMatchServices, fullMatchServicesQuery.Select(s => s.ToDomain()).ToList());
            }

            return(new SearchServiceGatewayResult(fullMatchServices, splitMatchServices));
        }