I have a document structure like the following:
Employer => Positions => RequiredSkills
Employer has a collection of Position
Positions have a collection of RequiredSkill.
Required Skill consists of a skill (string) and a proficiency (enum).
If I use a dynamic index it seems to return company fine, however I want to use an index to populate MVC view models to return to the UI.
I'm really new to Raven so my apologies for doing anything stupid/unnecessary!
I've got the following mapping:
public class PositionSearch : AbstractIndexCreationTask<Employer>
{
public PositionSearch()
{
Map = employers =>
from employer in employers
from position in employer.Positions
select new
{
EmployerId = employer.Id,
EmployerName = employer.Name,
PositionId = position.Id,
PositionTitle = position.Title,
position.Location,
position.Description,
RequiredSkills = position.RequiredSkills
};
StoreAllFields(FieldStorage.Yes);
Index("RequiredSkills_Skill", FieldIndexing.Analyzed);
}
}
However when I try to execute the following query:
var results = session.Query<PositionSearchResultModel, PositionSearch>()
.Customize(x => x.WaitForNonStaleResults())
.Where(x=>x.RequiredSkills.Any(y=>y.Skill == "SkillName"))
.ProjectFromIndexFieldsInto<PositionSearchResultModel>()
.ToList();
I get the following error:
System.ArgumentException:
The field 'RequiredSkills_Skill' is not indexed,
cannot query on fields that are not indexed
Can anyone see what I'm doing wrong or suggest another approach for me please?
Thanks,
James
UPDATE my view model - Thanks :
public class PositionSearchResultModel
{
public PositionSearchResultModel()
{
RequiredSkills = new HashSet<SkillProficiency>();
}
public string EmployerId { get; set; }
public string EmployerName { get; set; }
public string PositionId { get; set; }
public string PositionTitle { get; set; }
public string Location { get; set; }
public string Description { get; set; }
public ICollection<SkillProficiency> RequiredSkills { get; set; }
}
Because you want to do an analyzed search against the skill name, you need to isolate it as a separate index entry.
public class PositionSearch
: AbstractIndexCreationTask<Employer, PositionSearchResultModel>
{
public PositionSearch()
{
Map = employers =>
from employer in employers
from position in employer.Positions
select new
{
EmployerId = employer.Id,
EmployerName = employer.Name,
PositionId = position.Id,
PositionTitle = position.Title,
position.Location,
position.Description,
position.RequiredSkills,
// Isolate the search property into it's own value
SkillsSearch = position.RequiredSkills.Select(x => x.Skill)
};
// you could store all fields if you wanted, but the search field
// doesn't need to be stored so that would be wasteful.
Store(x => x.PositionId, FieldStorage.Yes);
Store(x => x.PositionTitle, FieldStorage.Yes);
Store(x => x.Location, FieldStorage.Yes);
Store(x => x.Description, FieldStorage.Yes);
Store(x => x.RequiredSkills, FieldStorage.Yes);
// Any field you are going to use .Search() on should be analyzed.
Index(x => x.SkillsSearch, FieldIndexing.Analyzed);
}
}
Note that I specified the projection as the result of the index. This is syntactic sugar. It's not wrong to leave it off, but then you have to specify your search field using a string.
You'll also need to add the search field to your results class
public string[] SkillsSearch { get; set; }
It really doesn't matter what type it is. A string array or collection will do just fine. You could also use just a string or an object, because it's only the name that's relevant.
When you query against this index, use the .Search()
method, like this:
var results = session.Query<PositionSearchResultModel, PositionSearch>()
.Customize(x => x.WaitForNonStaleResults()) // only use this in testing
.Search(x=> x.SkillsSearch, "SkillName")
.ProjectFromIndexFieldsInto<PositionSearchResultModel>() // AsProjection would also work
.ToList();
Note that the only reason you have to store so many fields is because you want to project them. If you separated the positions into their own documents, you would have much smaller indexes and much less to project. Keep in mind that when you project, all of the fields in the original document are already there and come directly from the document store, rather than having to be copied into the index. So if your original documents more closely match your desired results, then there's less work to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With