我在C#中使用elasticsearch.net库,并尝试查询与指定过滤器匹配的对象。
我希望查询返回对象,其中对象的Names集合中至少存在来自过滤器的输入名称之一。
问题是此查询的结果总是使我命中0次,即使我确定数据库中确实存在与指定过滤器匹配的数据,我也想找出我的查询出了什么问题…
该模型:
public class A { public int AId { get; set; } public IEnumerable<string> Names { get; set; } }
过滤对象:
public class Filter { public IEnumerable<string> NamesToSearch { get; set; } }
查询数据的方法:
public async Task<IEnumerable<A>> GetFilteredData(Filter filter) { var query = await _elasticClient.SearchAsync<A>(x => x.Query(q => q.Terms(a => a.Names, filter.NamesToSearch)) .Fields(a => a.AId, a => a.Names)); return query.Hits .Select(x => new A { AId = x.Fields.FieldValues<A, int>(a => a.AId)[0] }) .ToList(); }
我也尝试过以下查询,但都没有产生预期的结果:
var query = await _elasticClient.SearchAsync<A>(x => x.Query(q => q.Nested(n => n.Filter(f => f.Terms(y => y.Names, filter.NamesToSearch)))) .Fields(a => a.AId, a => a.Names));
适用于我的解决方案:
以使用ElasticSearch.net1.7.1进行实际编译,并且是类型安全的(没有按字符串引用字段名称),最后得到了以下扩展方法,对于我的场景而言,这就像一个魅力:
public static QueryContainer MatchAnyTerm<T>(this QueryDescriptor<T> descriptor, Expression<Func<T, object>> field, object[] values) where T : class, new() { var queryContainer = new QueryContainer(); foreach (var value in values) { queryContainer |= descriptor.Term(t => t.OnField(field).Value(value)); } return queryContainer; }
和用法:
var query = await _elasticClient.SearchAsync<A>(x => x.Query(q => q.Bool(b => b.Should(s => s.MatchAnyTerm(a => a.Names, filter.NamesToSearch.ToArray())) .Fields(a => a.AId, a => a.Names));
我认为您的问题是您尝试将整个数组传递给查询。相反,您应该将其视为OR表达式。
以下是您应使用的原始查询:
{ "query": { "bool": { "should": [ { "term": {"names": "test" } }, { "term": {"names": "xyz" } } ] } } }
并通过C#代码实现这一目标。首先,我定义了辅助函数:
private static QueryContainer TermAny<T>(QueryContainerDescriptor<T> descriptor, Field field, object[] values) where T : class { QueryContainer q = new QueryContainer(); foreach (var value in values) { q |= descriptor.Term(t => t.Field(field).Value(value)); } return q; }
现在查询:
string[] values = new[] { "test", "xyz" }; client.Search<A>(x => x.Query( q => q.Bool( b => b.Should(s => TermAny(s, "names", values)))));