【问题标题】:Spring Data Elasticsearch query by JSON structure基于 JSON 结构的 Spring Data Elasticsearch 查询
【发布时间】:2019-02-20 19:19:11
【问题描述】:

我使用的是 spring data elasticsearch,当我使用 @Query 注解时,将代码关联到实际的 JSON elasticsearch 查询要容易得多,如此链接参考中的示例:

https://www.programcreek.com/java-api-examples/index.php?api=org.springframework.data.elasticsearch.annotations.Query

我想知道是否有一种方法可以通过没有注释的 elasticsearch java 库通过完整的 JSON 主体进行查询。 IE。在方法实现或其他东西中。这将帮助我解析响应中的突出显示等。

感谢您提供任何信息。

来自 cmets 的澄清:我正在使用 spring-data-elasticsearch 3.0.10.RELEASE 和 Elasticsearch 6。由于 spring-data-elasticsearch 似乎还不支持 RestHighLevelClient,我正在使用 TransportClient client = new PreBuiltTransportClient (弹性搜索设置);创建ElasticsearchTemplate时的方法:return new ElasticsearchTemplate(client());

【问题讨论】:

  • 有多种方法可以从 Java 访问 Elasticsearch,但一种方法是使用标准 HTTP 客户端和 Jest 库。它还取决于您所指的 Elasticsearch 版本。 github.com/searchbox-io/Jest
  • @tom 我已更新问题以包括技术版本和说明。一个更好的答案将展示如何使用 spring-data-elasticsearch 拥有的客户端和包含的库来执行此操作,但也欢迎使用其他库(如 Jest)的任何示例!

标签: java elasticsearch spring-data-elasticsearch elasticsearch-java-api


【解决方案1】:

这是另一种方法,但不使用传输客户端。

将这些依赖项添加到您的pom.xml

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.19</version>
    <scope>compile</scope>
</dependency>

然后这样做:

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

Client client = new Client();
final WebResource r = client.resource("http://localhost:9200").path("/myindex/_search");
String requestJson = "{\"query\" : {\"match\" : {\"type\" : \"book\"} }}";
ClientResponse response = r.post(ClientResponse.class, requestJson);
String json = response.getEntity(String.class);

Gson gson = new GsonBuilder().setPrettyPrinting()
        .create();
Map map = gson.fromJson(json, Map.class);
System.out.println(gson.toJson(map));

// to convert to SearchResponse:
JsonXContentParser xContentParser = new JsonXContentParser(NamedXContentRegistry.EMPTY,
            new JsonFactory().createParser(json));
SearchResponse searchResponse = SearchResponse.fromXContent(xContentParser);

示例输出:

{
    "took": 9.0,
    "timed_out": false,
    "_shards": {
        "total": 5.0,
        "successful": 5.0,
        "failed": 0.0
    },
    "hits": {
        "total": 1.0,
        "max_score": 0.2876821,
        "hits": [
        {
            "_index": "myindex",
            "_type": "mydoc",
            "_id": "AWXp8gZjXyu6lA_2Kpi2",
            "_score": 0.2876821,
            "_source": {
                "title": "foobar",
                "type": "book"
            }
        }
        ]
    }
}

【讨论】:

  • 完美解决方案!感谢您发布。似乎我可能无法将 ElasticSearch 的 SearchResponse 实体与响应一起使用?可能与此有关的一件事是以某种方式将响应序列化为实体......
  • 我刚刚添加了一个如何将响应字符串转换为 SearchResponse 的示例。
  • 如果您喜欢这些答案中的任何一个,请标记它们,谢谢!
【解决方案2】:

我想出了一种方法来做到这一点,但它需要您制作一个位于 Elastic 节点上的脚本。见File-based scripts。它不是非常灵活,但试一试。这是该怎么做。

创建一个名为template_doctype.mustache 的文件并将其复制到$ELASTIC_HOME/config/scripts。这是您可以根据需要定制的脚本。重新启动 Elastic 或等待 60 秒以使其重新加载。

{
    "query" : {
        "match" : {
            "type" : "{{param_type}}"
        }
    }
}

我的pom.xml 依赖项:

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-elasticsearch</artifactId>
        <version>3.0.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.5.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.2</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

(仅供参考,我使用 mvn dependency:tree 发现您的 spring-data-elasticsearch 版本隐式使用了 5.5 版本的 ElasticSearch 库,即使您使用的是 ElasticSearch 6。)

创建一个虚拟索引:

curl -X PUT http://localhost:9200/myindex

创建几个可用于匹配的文档以确保代码有效:

curl -X POST http://localhost:9200/myindex/mydoc -d '{"title":"foobar", "type":"book"}'
curl -X POST http://localhost:9200/myindex/mydoc -d '{"title":"fun", "type":"magazine"}'

尝试运行查询。此代码应返回单个文档:

String clusterName = "my-application";
Settings elasticsearchSettings = Settings.builder().put("cluster.name", clusterName).build();
TransportClient client = new PreBuiltTransportClient(elasticsearchSettings)
        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"),9300));
Map<String, Object> template_params = new HashMap<>();

// Here is where you put parameters to your script.
template_params.put("param_type", "book");
SearchResponse sr = new SearchTemplateRequestBuilder(client)
        .setScript("template_doctype")  // this is where you specify what template to use
        .setScriptType(ScriptType.FILE)
        .setScriptParams(template_params)
        .setRequest(new SearchRequest())
        .get()
        .getResponse();

SearchHit[] results = sr.getHits().getHits();
for(SearchHit hit : results){

    String sourceAsString = hit.getSourceAsString();
    if (sourceAsString != null) {
        Gson gson = new GsonBuilder().setPrettyPrinting()
                .create();
        Map map = gson.fromJson(sourceAsString, Map.class);
        System.out.println( gson.toJson(map));
    }
}

输出:

{
  "title": "foobar",
  "type": "book"
}

【讨论】:

  • 哇。这真的很酷。我不确定如何将其部署到集群以进行发布构建。我在这里找到了更好的文档:elastic.co/guide/en/elasticsearch/client/java-api/5.0/…,看来我也可以设置脚本内联,或者使用他们的示例从代码中设置脚本模板。
  • 太酷了。我想知道为此,您是否需要集群的特殊权限,因为它正在执行管理 API 来存储脚本。对你来说可能不是问题。
猜你喜欢
  • 2018-11-14
  • 2023-02-13
  • 2015-10-25
  • 2016-11-01
  • 2022-12-11
  • 2016-03-28
  • 1970-01-01
  • 2016-05-10
  • 2015-10-02
相关资源
最近更新 更多