如何将预先制作的Query对象发送给NEST?

时间:2017-08-11 18:01:31

标签: c# elasticsearch nest

我正在尝试编写一个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
    }
  }
}

必须更改映射以在任何类型字段上进行任何类型的排序。

搜索查询的格式化将对象初始化函数与更通用的对象创建相结合,以提供我正在寻找的抽象类型。

我认为我试图解决的问题已经解决了。

1 个答案:

答案 0 :(得分:0)

使用"Raw" query in NEST,您只能提供请求的查询部分,即它不能包含"sort"或将提供给{{1}的JSON对象的任何其他顶级属性API端点。

我认为您在此之后可能会使用低级客户端发送搜索请求,但仍会返回_search的强类型响应,因为使用高级客户端进行返回搜索请求。

低级客户端通过SearchResponse<T>属性在高级客户端上公开;假设进入的.LowLevel将按照JSON.Net的预期进行序列化,您可以

Object query