I'm working on a search page for my application where the user can search for Jobs using a keyword along with some additional "advanced" criteria.
The following document is a simplified version of the "Vacancy" aggregate:
{
"ClientId": "clients/10",
"Salary": {
From: 30000.00,
To: 40000.00
}
"Campaigns": [
{
"Title": "
ASP.NET MVC Web Developer",
"IsActive": true
"Summary": "Some summary",
"Keywords": [
"ASP",
"MVC",
"C#"
]
},
{
"Title": "
ASP.NET MVC Web Developer",
"IsActive": false
"Summary": "Some old summary",
"Keywords": [
"ASP",
]
},
]
}
Each vacancy can have multiple campaigns, but only one campaign can be active at any one time (this rule is enforced by my domain layer). The keyword search should be applied to the "Active" campaign's TItle, Summary and Keywords. The advanced search will allow the user to search using a salary range.
Here's what I'm doing currently:
public class Jobs_Search : AbstractIndexCreationTask<Vacancy, Jobs_Search.ReduceResult>
{
public Jobs_Search()
{
Map = vacancies => from v in vacancies
from c0 in v.Campaigns
let c = (WebsiteCampaign)c0
select new
{
Id = v.Id,
StartDate = c.CampaignPeriod.StartDate,
EndDate = c.CampaignPeriod.EndDate,
Salary = v.Salary.From, // always search against the lower range
Query = new object[] {
c.Title,
c.Description,
c.Summary,
c.Description
}
};
Index(x => x.Query, Raven.Abstractions.Indexing.FieldIndexing.Analyzed);
}
public class ReduceResult
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public decimal Salary { get; set; }
public string Query { get; set; }
public int Id { get; set; }
}
}
public ActionResult Search(string q, decimal? salaryFrom, decimal? salaryTo, int? page)
{
var query = session.Query<Jobs_Search.ReduceResult, Jobs_Search>()
.Where(x => x.StartDate <= DateTime.UtcNow && x.EndDate >= DateTime.UtcNow)
.Search(x => x.Query, q);
if (salaryFrom.HasValue)
query = query.Where(x => x.Salary >= salaryFrom);
if (salaryTo.HasValue)
query = query.Where(x => x.Salary <= salaryTo);
var jobs = query.As<Vacancy>().ToList();
return View(jobs);
}
This is working fine.
What I'm not particularly happy with is that I then have to flatten out my vacancy, finding the active campaign and building my viewmodel.
Something I'm still struggling with is whether I can/should denormalize my data within an index. It seems a good way of encapsulating a query, as with the above example, I always want a Vacancy with it's Active campaign. It would be nice if I could return something like the following from the server:
{
"SalaryFrom": 30000.00,
"SalaryTo": 40000.00
"CampaignTitle": "
ASP.NET MVC Web Developer",
"CampaignSummary": "Some summary",
"Keywords": [
"ASP",
"MVC",
"C#"
}