我正在尝试编写一个api方法,该方法采用在应用程序级别构建的通用查询,并使用NEST接口将其发送到elasticsearch数据库。
到目前为止,我的例行程序的代码是:
[HttpPost]
[ActionName("Query")]
public IEnumerable<Account> Query([FromBody]Object query)
{
IEnumerable<Account> result = null;
var settings = new ConnectionSettings(
baseAddress
).DefaultIndex("bank").InferMappingFor<Account>(m => m.IndexName("bank").TypeName("account"));
var descriptor = new CreateIndexDescriptor("bank").Mappings(ms => ms.Map<Account>(m => m.AutoMap()));
var client = new ElasticClient(settings);
if (query == null)
{
var response = client.Search<Account>(s => s.Query(q => q.MatchAll()).Pretty().Size(1000));
result = response.Documents;
}
else
{
var response = client.Search<Account>(s => s.Query(q => q.Raw(query.ToString())));
result = response.Documents;
}
return result;
}
我正在使用E https://www.elastic.co/guide/en/elasticsearch/reference/current/_exploring_your_data.html中所示的示例数据库。
我想发送的查询是:
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
]
}
当我为查询发送null时,例程会提供正确的数据,但是当我发送查询值时,我希望它返回任何数据。
我错过了什么?
修改
我要做的是创建一个api调用接口,在该接口中构造查询并将其发送到api调用,将其作为完整查询进行处理,api使用通用查询对象进行泛型调用。
当前代码(有效)是:
public virtual IEnumerable<T> Query(Object query, String index)
{
IEnumerable<T> result = null;
var settings = new ConnectionSettings(baseAddress)
.DefaultIndex(index)
.InferMappingFor<T>(m => m.IndexName(index).TypeName(typeof(T).Name.ToLower()))
;
var descriptor = new CreateIndexDescriptor(index).Mappings(ms => ms.Map<T>(m => m.AutoMap()));
var client = new ElasticClient(settings);
if (query == null)
{
var response = client.Search<T>(s => s.Query(q => q.MatchAll()).Pretty(true).Size(1000));
if (response.IsValid)
{
result = response.Documents;
}
else
{
if (response.ServerError != null && response.ServerError.Error != null)
Logger.Logger.Warning(response.ServerError.Error.Reason);
else if (response.OriginalException != null)
Logger.Logger.Error(response.OriginalException);
else
Logger.Logger.Warning("NEST operation likely timed out.");
}
}
else
{
var type = client.Infer.TypeName(TypeName.From<T>());
var response = client.Search<T>(s => s.Query(q => q.Raw(query.ToString())).Pretty(true).Size(1000));
if (response.IsValid)
{
result = response.Documents;
}
else
{
if (response.ServerError != null && response.ServerError.Error != null)
Logger.Logger.Warning(response.ServerError.Error.Reason);
else if (response.OriginalException != null)
Logger.Logger.Error(response.OriginalException);
else
Logger.Logger.Warning("NEST operation likely timed out.");
}
}
client = null;
return result;
}
查询在另一个应用程序中构建,并发送到Web API 2.x服务。调用代码目前是:
[HttpPost]
public ActionResult Search(String query)
{
var client1 = new ElasticClient();
var matchQuery = new SimpleQueryStringQuery()
{
AllFields = true,
Analyzer = "standard",
Boost = 1.1,
Name = query,
Query = query,
DefaultOperator = Operator.Or,
AnalyzeWildcard = true,
Flags = SimpleQueryStringFlags.And | SimpleQueryStringFlags.Near | SimpleQueryStringFlags.Phrase,
};
String sqsq = client1.Serializer.SerializeToString(matchQuery);
String queryString = String.Format("{{ \"simple_query_string\" : \r\n {0} \r\n}}", sqsq);
var repository = new ElasticRepository();
var studies = repository.Query(queryString);
TempData["Studies"] = studies;
TempData["Query"] = query;
return RedirectToAction("Index");
}
我正在寻找的是一个完全通用的接口,它发送由应用程序创建的查询,并通过直接与数据库交互的Web API 2.x调用发送给它。
我在下面的答案中尝试了这个建议但没有成功:elasticsearch数据库表明查询构造有问题。它只是不会将查询对象发送给它并返回与我已有的NEST解决方案相同的记录。
看来低级的elasticsearch.NET可能是一个更合适的解决方案,但是,我无法将查询构造成发送它所喜欢的。
我愿意接受有关这方面的建议。
更新
我修改了api例程,将泛型查询的低级查询实现为:
public virtual IEnumerable<T> Query(Object query, String index)
{
IEnumerable<T> result = null;
var type = typeof(T).Name.ToLower();
var settings = new ConnectionSettings(baseAddress)
.DefaultIndex(index)
.InferMappingFor<T>(m => m.IndexName(index).TypeName(type))
;
var descriptor = new CreateIndexDescriptor(index).Mappings(ms => ms.Map<T>(m => m.AutoMap()));
var client = new ElasticClient(settings);
if (query == null)
{
var response = client.Search<T>(s => s.Query(q => q.MatchAll()).Pretty(true).Size(1000));
if (response.IsValid)
{
result = response.Documents;
}
else
{
if (response.ServerError != null && response.ServerError.Error != null)
Logger.Logger.Warning(response.ServerError.Error.Reason);
else if (response.OriginalException != null)
Logger.Logger.Error(response.OriginalException);
else
Logger.Logger.Warning("NEST operation likely timed out.");
}
}
else
{
var response = client.LowLevel.Search<SearchResponse<T>>(index, type, query);
if (response.Success)
{
var body = response.Body;
if (body != null)
{
result = body.Documents;
}
else
{
if (response.ServerError != null && response.ServerError.Error != null)
{
Logger.Logger.Error(response.OriginalException);
Logger.Logger.Warning(response.ServerError.Error.Reason);
if (response.ServerError.Error.RootCause != null && response.ServerError.Error.RootCause.Count() > 0)
{
foreach (var cause in response.ServerError.Error.RootCause)
{
Logger.Logger.Warning(String.Format("Root cause {0}: {1}", cause.Index, cause.Reason));
}
}
}
else if (response.OriginalException != null)
Logger.Logger.Error(response.OriginalException);
else
Logger.Logger.Warning("ElasticSearch operation likely timed out.");
}
}
else
{
if (response.ServerError != null && response.ServerError.Error != null)
{
Logger.Logger.Error(response.OriginalException);
Logger.Logger.Warning(response.ServerError.Error.Reason);
if (response.ServerError.Error.RootCause != null && response.ServerError.Error.RootCause.Count() > 0)
{
foreach (var cause in response.ServerError.Error.RootCause)
{
Logger.Logger.Warning(String.Format("Root cause {0}: {1}", cause.Index, cause.Reason));
}
}
}
else if (response.OriginalException != null)
Logger.Logger.Error(response.OriginalException);
else
Logger.Logger.Warning("ElasticSearch operation likely timed out.");
}
}
client = null;
return result;
}
此例程适用于通过Web Api 2.x调用发送的格式正确的查询字符串。
在Web应用程序级别形成并发送到Web API的查询本身如下:
[HttpPost]
public ActionResult Search(String query)
{
var client = new ElasticClient();
var searchQuery = new
{
query = new
{
simple_query_string = new SimpleQueryStringQuery()
{
AllFields = true,
Analyzer = "standard",
Boost = 1.1,
Name = query,
Query = query,
DefaultOperator = Operator.Or,
AnalyzeWildcard = true,
Flags = SimpleQueryStringFlags.And | SimpleQueryStringFlags.Near | SimpleQueryStringFlags.Phrase,
}
},
sort = new List<ISort>() {
new SortField() {
Field = Infer.Field("id"),
Order = SortOrder.Descending
}
}
};
String sqsq = client.Serializer.SerializeToString(searchQuery);
var repository = new ElasticRepository();
var studies = repository.Query(sqsq);
TempData["Studies"] = studies;
TempData["Query"] = query;
return RedirectToAction("Index");
}
此例程仍然无效,因为排序部分导致以下错误:
根本原因:默认情况下,文本字段禁用Fielddata。在[id]上设置fielddata = true,以便通过反转索引来加载内存中的fielddata。请注意,这可能会占用大量内存。或者,也可以使用关键字字段。
解决这个问题的方法是直接向elasticsearch数据库发出命令。这是:
PUT /studies/_mapping/study
{
"properties": {
"id": {
"type": "text",
"fielddata": true
}
}
}
必须更改映射以在任何类型字段上进行任何类型的排序。
搜索查询的格式化将对象初始化函数与更通用的对象创建相结合,以提供我正在寻找的抽象类型。
我认为我试图解决的问题已经解决了。
答案 0 :(得分:0)
使用"Raw" query in NEST,您只能提供请求的查询部分,即它不能包含"sort"
或将提供给{{1}的JSON对象的任何其他顶级属性API端点。
我认为您在此之后可能会使用低级客户端发送搜索请求,但仍会返回_search
的强类型响应,因为使用高级客户端进行返回搜索请求。
低级客户端通过SearchResponse<T>
属性在高级客户端上公开;假设进入的.LowLevel
将按照JSON.Net的预期进行序列化,您可以
Object query