Exemplo n.º 1
0
        JobAdSearchResults IJobAdSearchService.SearchSuggested(Guid?memberId, JobAdSearchQuery searchQuery)
        {
            const string method = "GetSuggestedJobs";

            try
            {
                if (memberId == null)
                {
                    return(new JobAdSearchResults());
                }

                var reader = GetReader();

                var searcher = new Searcher(reader);

                var member    = _membersQuery.GetMember(memberId.Value);
                var candidate = _candidatesQuery.GetCandidate(memberId.Value);
                if (member == null || candidate == null || candidate.ResumeId == null)
                {
                    return(new JobAdSearchResults());
                }

                var resume = _resumesQuery.GetResume(candidate.ResumeId.Value);

                //Get a MLT query based on the candidate's details
                var mlt = new MoreLikeThis(reader, CreateSimilarity());
                mlt.setAnalyzer(_contentAnalyzer);
                mlt.setMaxNumTokensParsed(10000); //increase for long resumes
                mlt.setFieldNames(new[] { FieldName.Content });
                mlt.setMinWordLen(3);             //exclude UK, BBC and the like
                mlt.setMaxQueryTerms(20);
                //mlt.setBoost(true);
                var candidateMltQuery = GetCandidateMltQuery(mlt, candidate, resume, method);

                mlt.setFieldNames(new[] { FieldName.Title });
                var candidateTitleMltQuery = GetCandidateMltQuery(mlt, candidate, resume, method);

                #region Log

                if (EventSource.IsEnabled(Event.Trace))
                {
                    EventSource.Raise(Event.Trace, method, "Candidate MLT Query",
                                      Event.Arg("Query",
                                                candidateMltQuery == null ? "null" : candidateMltQuery.toString()));
                    EventSource.Raise(Event.Trace, method, "Candidate Title MLT Query",
                                      Event.Arg("Query",
                                                candidateTitleMltQuery == null
                                                    ? "null"
                                                    : candidateTitleMltQuery.toString()));
                }

                #endregion

                var mltQueries   = new BooleanQuery(); // Up to 3 MLT queries ORed together
                var titleQueries = new BooleanQuery();
                // (Optionally) Desired Job Title and up to 3 job titles ORed together
                var combinedFilter = new BooleanFilter();
                // Filtering including exclusion of past applications and salary, location, etc.
                var combinedQuery = new BooleanQuery(); // mltQueries and titleQueries ANDed together

                if (candidateMltQuery != null)
                {
                    mltQueries.add(candidateMltQuery, BooleanClause.Occur.SHOULD);
                }

                if (candidateTitleMltQuery != null)
                {
                    mltQueries.add(candidateTitleMltQuery, BooleanClause.Occur.SHOULD);
                }

                //Get a MLT query based on the candidate's past 3 months of applications

                var jobAdIds = (from a in _memberApplicationsQuery.GetApplications(member.Id)
                                where a.CreatedTime >= DateTime.Now.AddMonths(-3)
                                select a.PositionId).ToList();

                if (jobAdIds.Any())
                {
                    mlt.setFieldNames(new[] { FieldName.Content });
                    var applicationsMltQuery = GetApplicationsMltQuery(mlt, jobAdIds);

                    if (applicationsMltQuery != null)
                    {
                        mltQueries.add(applicationsMltQuery, BooleanClause.Occur.SHOULD);
                    }

                    #region Log

                    if (EventSource.IsEnabled(Event.Trace))
                    {
                        EventSource.Raise(Event.Trace, method, "Applications MLT Query",
                                          Event.Arg("Query",
                                                    applicationsMltQuery == null
                                                        ? "null"
                                                        : applicationsMltQuery.toString()));
                    }

                    #endregion

                    // ensure the jobs that have already been applied for are not in the results
                    var idFilter = new SpecialsFilter(SearchFieldName.Id, false,
                                                      jobAdIds.Select(id => id.ToFieldValue()));
                    combinedFilter.add(new FilterClause(idFilter, BooleanClause.Occur.MUST_NOT));

                    #region Log

                    if (EventSource.IsEnabled(Event.Trace))
                    {
                        EventSource.Raise(Event.Trace, method, "Combined Filter #1",
                                          Event.Arg("Query", combinedFilter.toString()));
                    }

                    #endregion
                }

                if (mltQueries.getClauses().Any())
                {
                    combinedQuery.add(new BooleanClause(mltQueries, BooleanClause.Occur.SHOULD));
                }

                // now add in additional weighting data from the candidate's profile
                if (!string.IsNullOrEmpty(candidate.DesiredJobTitle))
                {
                    //Boost the DesiredJobTitle above the rest of the terms
                    var jobTitleExpression =
                        Expression.Parse(Expression.StripOperatorsAndBrackets(candidate.DesiredJobTitle),
                                         ModificationFlags.AllowShingling);

                    // First match on title
                    var boostQuery =
                        _indexer.GetQuery(new JobAdSearchQuery
                    {
                        AdTitle = jobTitleExpression, IncludeSynonyms = true
                    });
                    boostQuery.setBoost(1.5F);
                    titleQueries.add(boostQuery, BooleanClause.Occur.SHOULD);

                    // Also match general content
                    //boostQuery = _indexer.GetLuceneQuery(new JobAdSearchQuery {Keywords = jobTitleExpression, IncludeSynonyms = true});
                    //boostQuery.setBoost(1.3F);
                    //titleQueries.add(boostQuery, BooleanClause.Occur.SHOULD);

                    #region Log

                    if (EventSource.IsEnabled(Event.Trace))
                    {
                        EventSource.Raise(Event.Trace, method, "Combined Query #1",
                                          Event.Arg("Query", combinedQuery.toString()));
                    }

                    #endregion
                }

                if (resume.Jobs != null)
                {
                    foreach (var jobTitle in resume.Jobs.Select(j => j.Title).Take(3))
                    {
                        if (string.IsNullOrEmpty(jobTitle))
                        {
                            continue;
                        }

                        var jobTitleExpression = Expression.Parse(Expression.StripOperatorsAndBrackets(jobTitle),
                                                                  ModificationFlags.AllowShingling);

                        // First match on title
                        var boostQuery =
                            _indexer.GetQuery(new JobAdSearchQuery
                        {
                            AdTitle = jobTitleExpression, IncludeSynonyms = true
                        });
                        boostQuery.setBoost(1.2F);
                        titleQueries.add(boostQuery, BooleanClause.Occur.SHOULD);

                        // Also match general content
                        //boostQuery = _indexer.GetLuceneQuery(new JobAdSearchQuery { Keywords = jobTitleExpression, IncludeSynonyms = true });
                        //boostQuery.setBoost(1.1F);
                        //titleQueries.add(boostQuery, BooleanClause.Occur.SHOULD);

                        // Also match general content
                        //boostQuery = _indexer.GetLuceneQuery(new JobAdSearchQuery { Keywords = jobTitleExpression, IncludeSynonyms = true });
                        //boostQuery.setBoost(1.1F);
                        //combinedQuery.add(boostQuery, BooleanClause.Occur.SHOULD);
                    }
                }

                // now combine the queries

                if (mltQueries.getClauses().Any())
                {
                    combinedQuery.add(new BooleanClause(mltQueries, BooleanClause.Occur.SHOULD));
                }

                if (titleQueries.getClauses().Any())
                {
                    combinedQuery.add(new BooleanClause(titleQueries, BooleanClause.Occur.MUST));
                }

                #region Log

                if (EventSource.IsEnabled(Event.Trace))
                {
                    EventSource.Raise(Event.Trace, method, "Combined Query #2",
                                      Event.Arg("Query", combinedQuery.toString()));
                }

                #endregion

                searchQuery.Salary = candidate.DesiredSalary == null
                    ? null
                    : candidate.DesiredSalary.Clone();
                searchQuery.ExcludeNoSalary         = false;
                searchQuery.Location                = member.Address.Location;
                searchQuery.Distance                = 50;
                searchQuery.JobTypes                = candidate.DesiredJobTypes;
                searchQuery.Relocations             = candidate.RelocationPreference != RelocationPreference.No && candidate.RelocationLocations != null
                    ? candidate.RelocationLocations = candidate.RelocationLocations.ToArray()
                    : null;

                //Add salary, job type, location restriction & (optionally) last run time
                var filter = _indexer.GetFilter(searchQuery, null, null);

                // combine salary, etc. filter with the excluded job ads
                combinedFilter.add(new FilterClause(filter, BooleanClause.Occur.MUST));

                // exclude blocked jobs
                var excludedIds = _jobAdActivityFiltersQuery.GetExcludeJobAdIds(member, searchQuery);
                if (excludedIds != null && excludedIds.Count > 0)
                {
                    combinedFilter.add(new FilterClause(new SpecialsFilter(SearchFieldName.Id, true, excludedIds.Select(id => id.ToFieldValue())), BooleanClause.Occur.MUST));
                }


                #region Log

                if (EventSource.IsEnabled(Event.Trace))
                {
                    EventSource.Raise(Event.Trace, method, "Combined Filter #2",
                                      Event.Arg("Query", combinedFilter.toString()));
                }

                #endregion

                var searchResults = searcher.Search(combinedQuery, combinedFilter, null, null, searchQuery.Skip, searchQuery.Take ?? reader.maxDoc(), false);

                #region Log

                if (EventSource.IsEnabled(Event.Trace))
                {
                    EventSource.Raise(Event.Trace, method, "MLT Results",
                                      Event.Arg("Results Count", searchResults == null ? 0 : searchResults.JobAdIds.Count));
                }

                #endregion

                return(searchResults);
            }
            catch (Exception e)
            {
                #region Log
                EventSource.Raise(Event.Error, method, "Unexpected exception.", e);
                #endregion
                throw;
            }
        }