我正在使用Elasticsearch v6和NEST搜索我在我的网站上创建的广告。
这是我的文档(我在其中构建Index):
[ElasticsearchType(Name = "document")]
public class Document
{
public long Id { get; set; }
[Text(Analyzer = "english")]
public string Title { get; set; }
public int Price { get; set; }
public short AdDurationInDays { get; set; }
public DateTime AdStartTime { get; set; }
[Text(Analyzer = "english")]
public string Description { get; set; }
[Text(Index = false)] // Don't want to query this field
public string MainPhoto { get; set; }
public int ParentCategoryId { get; set; }
[Text(Analyzer = "english")]
public string ParentCategoryName { get; set; }
public GeoLocation GeoLocation { get; set; }
}我有一个创建的Elasticsearch客户端,还有一个名为SearchDocuments的方法。我想向这个方法传递一个搜索词(关键字)和一个过滤器列表。搜索文档需要根据搜索项和传入的过滤器构建动态查询。
这是我的搜索方法:
public void SearchDocuments(KeywordMultiMatch Keyword, List<IQueryBuilder> filters)
{
QueryContainer multiMatchQuery = Keyword.GetQuery();
QueryContainer filterQuery = null;
foreach (var filter in filters)
{
var query = filter.GetQuery();
if (query != null)
{
filterQuery &= query;
}
}
var res = Client.Search<Document>(new SearchRequest<Document>
{
Query = new BoolQuery
{
Must = new QueryContainer[] { multiMatchQuery },
Filter = new QueryContainer[] { filterQuery }
}
});
}为了构建动态搜索查询,我创建了一个名为IQueryBuilder的接口。这个接口有一个名为GetQuery()的方法,它返回一个QueryContainer。
public interface IQueryBuilder
{
QueryContainer GetQuery();
}现在,我的搜索术语类(关键字)和所有过滤器实现了这个接口,这是KeywordMultiMatch类,它返回一个MultiMatchQuery:
public class KeywordMultiMatch : IQueryBuilder
{
[StringLength(100, MinimumLength = 2)]
public string keyword { get; set; }
public QueryContainer GetQuery()
{
if (!string.IsNullOrEmpty(keyword))
{
var query = new MultiMatchQuery
{
Name = "Keyword Multimatch",
Fields = Fields<Document>(p => p.Title).And<Document>(p => p.Description).And<Document>(p => p.ParentCategoryName).And<Document>(p => p.ParentCategoryName),
Query = keyword,
Fuzziness = Fuzziness.EditDistance(1)
};
return query;
}
return new MatchAllQuery();
}
}这是我的LocationFilter课程:
public class LocationFilter : IQueryBuilder
{
public short Distance { get; set; }
public GeoLocation GeoLocation { get; set; }
public QueryContainer GetQuery()
{
if (GeoLocation != null && Distance > 0)
{
var query = new GeoDistanceQuery
{
Name = "Location Filter",
Field = Field<Document>(p => p.GeoLocation),
DistanceType = GeoDistanceType.Plane, // plane is faster but less accurate than arc
Location = GeoLocation,
Distance = Distance.ToString() + "km"
};
return query;
}
return null;
}
}这是我的ParentCategoryFilter课程:
public class ParentCategoryFilter : IQueryBuilder
{
public int ParentCategoryId { get; set; }
public QueryContainer GetQuery()
{
if (ParentCategoryId > 0)
{
var query = new TermQuery
{
Name = "Parent Category Filter",
Field = Field<Document>(p => p.ParentCategoryId),
Value = ParentCategoryId
};
return query;
}
return null;
}
}发布于 2018-06-03 12:37:06
抽象单一用途的查询构建器是一个不错的选择。
SearchDocuments的参数应该合并到一个单一的模型中,以简化方法定义。
public class Query {
public IQueryBuilder Must { get; set; }
public List<IQueryBuilder> Filters { get; set; }
}这还应该允许在定义动态搜索查询时具有更大的灵活性,因为现在它并没有与KeywordMultiMatch紧密耦合,在这种情况下,这将是一个实现问题。
public void SearchDocuments(Query args) {
var filterQuery = args.Filters
.Select(buider => buider.GetQuery())
.Where(container => container != null)
.Aggregate((current, next) => current & next);
var result = Client.Search<Document>(new SearchRequest<Document> {
Query = new BoolQuery {
Must = new QueryContainer[] { args.Must.GetQuery() },
Filter = new QueryContainer[] { filterQuery }
}
});
//...
}https://codereview.stackexchange.com/questions/195689
复制相似问题