From 1a3e3c3b8ee865f7879fec73eb5ee154419e1e1f Mon Sep 17 00:00:00 2001 From: vga91 Date: Mon, 16 Dec 2024 12:16:51 +0100 Subject: [PATCH] [NOID] cleanup and fix tests --- .../database-integration/elasticsearch.adoc | 81 +- full-it/build.gradle | 1 + .../apoc/full/it/es/ElasticSearchTest.java | 429 ++++++++++ .../full/it}/es/ElasticVersionEightTest.java | 23 +- .../full/it}/es/ElasticVersionSevenTest.java | 23 +- full/src/main/java/apoc/es/ElasticSearch.java | 186 +---- .../java/apoc/es/ElasticSearchConfig.java | 6 +- .../java/apoc/es/ElasticSearchHandler.java | 70 +- .../test/java/apoc/es/ElasticSearchTest.java | 786 ------------------ 9 files changed, 594 insertions(+), 1011 deletions(-) create mode 100644 full-it/src/test/java/apoc/full/it/es/ElasticSearchTest.java rename {full/src/test/java/apoc => full-it/src/test/java/apoc/full/it}/es/ElasticVersionEightTest.java (87%) rename {full/src/test/java/apoc => full-it/src/test/java/apoc/full/it}/es/ElasticVersionSevenTest.java (97%) delete mode 100644 full/src/test/java/apoc/es/ElasticSearchTest.java diff --git a/docs/asciidoc/modules/ROOT/pages/database-integration/elasticsearch.adoc b/docs/asciidoc/modules/ROOT/pages/database-integration/elasticsearch.adoc index 71ebd85ec1..b7b8667ca4 100644 --- a/docs/asciidoc/modules/ROOT/pages/database-integration/elasticsearch.adoc +++ b/docs/asciidoc/modules/ROOT/pages/database-integration/elasticsearch.adoc @@ -25,7 +25,7 @@ include::example$generated-documentation/apoc.es.delete.adoc[] [NOTE] ==== -It is currently not possible to query Elastic 8 via certificate, +It is currently not possible to query Elastic 8 via certificate, but only disabling ssl with the configuration `"xpack.security.http.ssl.enabled=false"`, using the basic authentication via the header config (see `config parameter` below) or (not recommended) disabling security via `xpack.security.enabled=false` ==== @@ -74,7 +74,7 @@ Here an example: [source,cypher] ---- // It's important to create an index to improve performance -CREATE INDEX ON :Document(id) +CREATE INDEX FOR (n:Document) ON (n.id) // First query: get first chunk of data + the scroll_id for pagination CALL apoc.es.query('localhost','test-index','test-type','name:Neo4j&size=1&scroll=5m',null) yield value with value._scroll_id as scrollId, value.hits.hits as hits // Do something with hits @@ -108,16 +108,71 @@ call apoc.es.post(host-or-key,index-or-null,type-or-null,id-or-null,query-or-nul === host-or-key parameter -The parameter can be a direct host or url, or an entry to be lookup up in apoc.conf +The parameter can be: * host * host:port +* username:password@host:port * http://host:port -* lookup via key to apoc.es..url -* lookup via key apoc.es..host +* http://username:password@host:port + +For example, by using the `apoc.es.stats`, we can execute: +[source, cypher] +---- +CALL apoc.es.stats('http://username:password@host:port') +---- + +Moreover, it can be an entry to be lookup up in `apoc.conf`: + * lookup apoc.es.url * lookup apoc.es.host +This takes precedence over the direct string host or url as the first parameter, as above. + +For example, with a `apoc.conf` like this: +``` +apoc.es.url=http://username:password@host:port +``` + +or like this : +``` +apoc.es.host=username:password@host:port +``` + +we can connect to elastic by putting null as the first parameter. + +For example, by using the `apoc.es.stats`, we can execute: +[source, cypher] +---- +CALL apoc.es.stats(null) +---- + +Furthermore, it can be an entry to be lookup up in `apoc.conf`, +where `` have be placed in the first parameter: + +* lookup via key to apoc.es..url +* lookup via key apoc.es..host + + +For example, with a `apoc.conf` like this: +``` +apoc.es.custom.url=http://username:password@host:port +``` + +or like this: +``` +apoc.es.custom.host=username:password@host:port +``` + +we can connect to elastic by putting null as the first parameter. + +For example, by using the `apoc.es.stats`, we can execute: +[source, cypher] +---- +CALL apoc.es.stats('custom') +---- + + === index parameter Main ES index, will be sent directly, if null then "_all" multiple indexes can be separated by comma in the string. @@ -147,10 +202,10 @@ Config can be an optional *map*, which can have the following entries: |=== | name | type | default | description | headers | `Map` | {`content-type`: "application/json", `method`, ""} | Contains a header map to add (or replace) the default one. - The `method: ` is needed by APOC to figure out under the hood, which http request method to pass. - That is, by default, it is `PUT` with the `apoc.es.put`, POST with the `apoc.es.post` and `apoc.es.postRaw`, and GET in other cases. -| version | `String` | `DEFAULT` | Can be `DEFAULT` and `EIGHT`, in order to change the RestAPI endpoint based on Elastic version. - See `Endpoint` table below. +The `method: ` is needed by APOC to figure out under the hood, which http request method to pass. +That is, by default, it is `PUT` with the `apoc.es.put`, POST with the `apoc.es.post` and `apoc.es.postRaw`, and GET in other cases. +| version | `String` | `DEFAULT` | Can be `DEFAULT` and `EIGHT`, in order to change the RestAPI endpoint based on Elastic version. +See `Endpoint` table below. |=== @@ -160,7 +215,7 @@ For example, by using the `apoc.es.stats`, we can execute: CALL apoc.es.stats('custom', { headers: {Authorization: "Basic "} }) ---- -to use a https://www.elastic.co/guide/en/elasticsearch/reference/current/http-clients.html[Basic authentication] +to use a https://www.elastic.co/guide/en/elasticsearch/reference/current/http-clients.html[Basic authentication] and create the following HTTP header: ``` Authorization: Basic @@ -177,12 +232,12 @@ for example the `apoc.es.query`. .Endpoint [opts=header] |=== -| procedure(s) | with version: `DEFAULT` | with version: `EIGHT` +| procedure(s) | with version: `DEFAULT` | with version: `EIGHT` | `apoc.es.stats(host)` | /_stats | same as `DEFAULT` | `apoc.es.query(host, index, type, query, payload, $conf)` | ///_stats? | //_stats? | `apoc.es.getRaw/apoc.es.postRaw(host, path, payload, $conf)` | `/` | same as `DEFAULT` | the others `apoc.es.(host, index, type, id, query, payload, $conf)` procedures | `///_stats?` -By default, the `` and `` will be populated as `_all`, while the ``, if not present, will be removed from the endpoint +By default, the `` and `` will be populated as `_all`, while the ``, if not present, will be removed from the endpoint | `///_stats?`. Note that you only need to enter one of three values between ``,`` and ``, the others will eventually be excluded from the endpoint. The type param is usually an underscore string indicating the type of the API, e.g. `_doc` or `_update` (while previously indicated https://www.elastic.co/guide/en/elasticsearch/reference/6.1/removal-of-types.html[the mapping types]). @@ -213,4 +268,4 @@ CALL apoc.es.put($host,'', null, null, null, null, { version: 'EIGHT' === Results -Results are stream of map in value. +Results are stream of map in value. \ No newline at end of file diff --git a/full-it/build.gradle b/full-it/build.gradle index d7936fe069..05310e50d1 100644 --- a/full-it/build.gradle +++ b/full-it/build.gradle @@ -13,6 +13,7 @@ dependencies { testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: '1.12.770' testImplementation group: 'org.xmlunit', name: 'xmlunit-core', version: '2.9.1' + testImplementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0' configurations.all { exclude group: 'org.slf4j', module: 'slf4j-nop' diff --git a/full-it/src/test/java/apoc/full/it/es/ElasticSearchTest.java b/full-it/src/test/java/apoc/full/it/es/ElasticSearchTest.java new file mode 100644 index 0000000000..38e7c267cf --- /dev/null +++ b/full-it/src/test/java/apoc/full/it/es/ElasticSearchTest.java @@ -0,0 +1,429 @@ +package apoc.full.it.es; + +import apoc.es.ElasticSearch; +import apoc.util.JsonUtil; +import apoc.util.TestUtil; +import apoc.util.Util; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.Option; +import org.junit.AfterClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.neo4j.test.rule.DbmsRule; +import org.neo4j.test.rule.ImpermanentDbmsRule; +import org.testcontainers.elasticsearch.ElasticsearchContainer; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static apoc.ApocConfig.apocConfig; +import static apoc.util.MapUtil.map; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author mh + * @since 21.05.16 + */ +public abstract class ElasticSearchTest { + + private static final String URL_CONF = "apoc.es.url"; + static String HTTP_HOST_ADDRESS; + static String HTTP_URL_ADDRESS; + + public static ElasticsearchContainer elastic; + + final static String ES_INDEX = "test-index"; + + abstract String getEsType(); + + final static String ES_ID = UUID.randomUUID().toString(); + + + private static final String DOCUMENT = "{\"name\":\"Neo4j\",\"company\":\"Neo Technology\",\"description\":\"Awesome stuff with a graph database\"}"; + + final static String password = "myPassword"; + static Map basicAuthHeader = Map.of("Authorization", + "Basic " + Base64.getEncoder().encodeToString(("elastic:" + password).getBytes())); + + @ClassRule + public static DbmsRule db = new ImpermanentDbmsRule(); + + static Map paramsWithBasicAuth; + + private static final Configuration JSON_PATH_CONFIG = Configuration.builder().options(Option.DEFAULT_PATH_LEAF_TO_NULL, Option.SUPPRESS_EXCEPTIONS).build(); + + static void getElasticContainer(String tag, Map envMap, Map params) throws JsonProcessingException { + + elastic = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:" + tag) + .withPassword(password) + .withEnv(envMap); + elastic.start(); + + String httpHostAddress = elastic.getHttpHostAddress(); + HTTP_HOST_ADDRESS = String.format("elastic:%s@%s", + password, + httpHostAddress); + + HTTP_URL_ADDRESS = "http://" + HTTP_HOST_ADDRESS; + + params.put("host", elastic.getHttpHostAddress()); + params.put("url", "http://" + elastic.getHttpHostAddress()); + paramsWithBasicAuth = params; + TestUtil.registerProcedure(db, ElasticSearch.class); + insertDocuments(); + } + + static String getRawProcedureUrl(String id, String type) { + return ES_INDEX + "/" + type + "/" + id + "?refresh=true"; + } + + @AfterClass + public static void tearDown() { + elastic.stop(); + db.shutdown(); + } + + /** + * Default params (host, index, type, id) + payload + * + * @param payload + * @return + */ + static Map createDefaultProcedureParametersWithPayloadAndId(String payload, String id) { + try { + Map mapPayload = JsonUtil.OBJECT_MAPPER.readValue(payload, Map.class); + return addPayloadAndIdToParams(paramsWithBasicAuth, mapPayload, id); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static Map addPayloadAndIdToParams(Map params, Object payload, String id) { + return Util.merge(params, Util.map("payload", payload, "id", id)); + } + + private static void insertDocuments() throws JsonProcessingException { + Map params = createDefaultProcedureParametersWithPayloadAndId("{\"procedurePackage\":\"es\",\"procedureName\":\"get\",\"procedureDescription\":\"perform a GET operation to ElasticSearch\"}", UUID.randomUUID().toString()); + TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { + Object created = extractValueFromResponse(r, "$.result"); + assertEquals("created", created); + }); + + params = createDefaultProcedureParametersWithPayloadAndId("{\"procedurePackage\":\"es\",\"procedureName\":\"post\",\"procedureDescription\":\"perform a POST operation to ElasticSearch\"}", UUID.randomUUID().toString()); + TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { + Object created = extractValueFromResponse(r, "$.result"); + assertEquals("created", created); + }); + + params = createDefaultProcedureParametersWithPayloadAndId(DOCUMENT, ES_ID); + TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { + Object created = extractValueFromResponse(r, "$.result"); + assertEquals("created", created); + }); + } + + static Object extractValueFromResponse(Map response, String jsonPath) { + Object jsonResponse = response.get("value"); + assertNotNull(jsonResponse); + + String json = JsonPath.parse(jsonResponse).jsonString(); + return JsonPath.parse(json, JSON_PATH_CONFIG).read(jsonPath); + } + + /** + * Simple get request for document retrieval + * http://localhost:9200/test-index/test-type/ee6749ff-b836-4529-88e9-3105675d625a + * + * @throws Exception + */ + @Test + public void testGetWithQueryNull() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,null,null,$config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + } + + @Test + public void testProceduresWithUrlAndHeaders() { + TestUtil.testCall(db, "CALL apoc.es.stats($url, $config)", paramsWithBasicAuth, + commonEsStatsConsumer()); + + TestUtil.testCall(db, "CALL apoc.es.get($url,$index,$type,$id,null,null,$config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + } + + @Test + public void testGetRowProcedureWithAuthHeader() { + Map params = Map.of("url", elastic.getHttpHostAddress(), + "suffix", getRawProcedureUrl(ES_ID, getEsType()), + "headers", basicAuthHeader); + + TestUtil.testCall(db, "CALL apoc.es.getRaw($url, $suffix, null, {headers: $headers})", params, + commonEsGetConsumer()); + } + + @Test + public void testProceduresWithUrlKeyConfOverridingGenericUrlConf() { + apocConfig().setProperty("apoc.es.customKey.url", HTTP_URL_ADDRESS); + apocConfig().setProperty(URL_CONF, "wrongUrl"); + + TestUtil.testCall(db, "CALL apoc.es.stats('customKey')", + commonEsStatsConsumer()); + + TestUtil.testCall(db, "CALL apoc.es.get('customKey',$index,$type,$id,null,null, $config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + + apocConfig().getConfig().clearProperty(URL_CONF); + } + + /** + * Simple get request for document retrieval but we also send a single command (as a Map) to ES + * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name + * + * @throws Exception + */ + @Test + public void testGetWithQueryAsMapSingleParam() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,{_source_includes:'name'},null, $config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + } + + /** + * Simple get request for document retrieval, but we also send multiple commands (as a string) to ES + * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name&_source_excludes=description + * + * @throws Exception + */ + @Test + public void testGetWithQueryAsStringMultipleParams() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name&_source_excludes=description',null, $config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + } + + /** + * Simple get request for document retrieval but we also send a single command (as a string) to ES + * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name + * + * @throws Exception + */ + @Test + public void testGetWithHeaderAndQueryAsStringSingleParam() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name',null, $config) yield value", paramsWithBasicAuth, + commonEsGetConsumer()); + } + + /** + * We want to search our document by name --> /test-index/test-type/_search? + * This test uses a plain string to query ES + */ + @Test + public void testSearchWithQueryNull() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,null,null, $config) yield value", paramsWithBasicAuth, r -> { + Object hits = extractValueFromResponse(r, "$.hits.hits"); + assertEquals(3, ((List) hits).size()); + }); + } + + @Test + public void testStatsWithAuthHeader() { + TestUtil.testCall(db, "CALL apoc.es.stats($host, $config)", paramsWithBasicAuth, + commonEsStatsConsumer()); + } + + @Test + public void testSearchWithQueryAsAStringAndHeader() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.query($host, $index, $type, 'q=name:Neo4j', null, $config) yield value", paramsWithBasicAuth, r -> { + Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); + assertEquals("Neo4j", name); + }); + } + + /** + * We want to search our document by name --> /test-index/test-type/_search?q=name:* + * This test uses a plain string to query ES + */ + @Test + public void testFullSearchWithQueryAsAString() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'q=name:*',null, $config) yield value", paramsWithBasicAuth, r -> { + Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); + assertEquals("Neo4j", name); + }); + } + + /** + * We want to search our document by name --> /test-index/test-type/_search?q=procedureName:get + * This test uses a plain string to query ES + */ + @Test + public void testFullSearchWithQueryAsAStringWithEquals() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'q=procedureName:get',null, $config) yield value", paramsWithBasicAuth, r -> { + Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); + assertEquals("get", name); + }); + } + + /** + * We want to search our document by name --> /test-index/test-type/_search?size=1&scroll=1m&_source=true + * This test uses a plain string to query ES + */ + @Test + public void testFullSearchWithOtherParametersAsAString() throws Exception { + TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'size=1&scroll=1m&_source=true&q=procedureName:get',null, $config) yield value", paramsWithBasicAuth, r -> { + Object hits = extractValueFromResponse(r, "$.hits.hits"); + assertEquals(1, ((List) hits).size()); + Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); + assertEquals("get", name); + }); + } + + /** + * We create a document with a field tags that is a collection of a single element "awesome". + * Then we update the same field with the collection ["beautiful"] + * and we retrieve the document in order to verify the update. + *

+ * http://localhost:9200/test-index/test-type/f561c1c5-4092-4c5d-98a6-5ea2b3417415/_update + */ + @Test + public void testPutUpdateDocument() throws IOException{ + Map doc = JsonUtil.OBJECT_MAPPER.readValue(DOCUMENT, Map.class); + doc.put("tags", Arrays.asList("awesome")); + Map params = createDefaultProcedureParametersWithPayloadAndId(JsonUtil.OBJECT_MAPPER.writeValueAsString(doc), ES_ID); + TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { + Object updated = extractValueFromResponse(r, "$.result"); + assertEquals("updated", updated); + }); + + TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,null,null, $config) yield value", params, r -> { + Object tag = extractValueFromResponse(r, "$._source.tags[0]"); + assertEquals("awesome", tag); + }); + } + + @Test + public void testPutUpdateDocumentWithAuthHeader() throws IOException{ + String tags = UUID.randomUUID().toString(); + + Map doc = JsonUtil.OBJECT_MAPPER.readValue(DOCUMENT, Map.class); + doc.put("tags", Arrays.asList(tags)); + Map params = addPayloadAndIdToParams(paramsWithBasicAuth, doc, ES_ID); + TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", + params, + r -> { + Object result = extractValueFromResponse(r, "$.result"); + assertEquals("updated", result); + }); + + TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null, $config) yield value", + params, + r -> { + Object actualTags = extractValueFromResponse(r, "$._source.tags[0]"); + assertEquals(tags, actualTags); + }); + } + + @Test + public void testPostRawCreateDocument() throws IOException { + String index = UUID.randomUUID().toString(); + String type = getEsType(); + String id = UUID.randomUUID().toString(); + Map payload = JsonUtil.OBJECT_MAPPER.readValue("{\"ajeje\":\"Brazorf\"}", Map.class); + Map params = Util.map("host", HTTP_HOST_ADDRESS, + "index", index, + "suffix", index, + "type", type, + "payload", payload, + "suffixDelete", index, + "suffixPost", index + "/" + type + "/" + id + "?refresh=true", + "id", id); + + TestUtil.testCall(db, "CALL apoc.es.postRaw($host, $suffixPost, $payload) yield value", params, r -> { + Object result = extractValueFromResponse(r, "$.result"); + assertEquals("created", result); + }); + + TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null) yield value", + params, + r -> { + Object response = extractValueFromResponse(r, "$._source.ajeje"); + assertEquals("Brazorf", response); + }); + + TestUtil.testCall(db, "CALL apoc.es.delete($host, $index, $type, $id, 'refresh=true')", params, r -> { + Object result = extractValueFromResponse(r, "$.result"); + assertEquals("deleted", result); + }); + } + + @Test + public void testPostCreateDocumentWithAuthHeader() throws IOException { + String index = UUID.randomUUID().toString(); + String type = getEsType(); + Map payload = JsonUtil.OBJECT_MAPPER.readValue("{\"ajeje\":\"Brazorf\"}", Map.class); + Map params = Util.map("host", elastic.getHttpHostAddress(), + "index", index, + "type", type, + "payload", payload, + "suffix", index, + "config", map("headers", basicAuthHeader)); + + AtomicReference id = new AtomicReference<>(); + TestUtil.testCall(db, "CALL apoc.es.post($host,$index,$type,'refresh=true', $payload, $config) yield value", params, r -> { + Object result = extractValueFromResponse(r, "$.result"); + assertEquals("created", result); + + id.set((String) ((Map) r.get("value")).get("_id")); + }); + + params.put("id", id.get()); + + TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null, $config) yield value", + params, + r -> { + Object actual = extractValueFromResponse(r, "$._source.ajeje"); + assertEquals("Brazorf", actual); + }); + + TestUtil.testCall(db, "CALL apoc.es.delete($host, $index, $type, $id, 'refresh=true', $config)", params, r -> { + Object result = extractValueFromResponse(r, "$.result"); + assertEquals("deleted", result); + }); + } + + /** + * We want to search our document by name --> /test-index/test-type/_search?q=name:Neo4j + * This test uses a Map to query ES + */ + @Test + public void testSearchWithQueryAsAMap() { + TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,null,{query: {match: {name: 'Neo4j'}}}, $config) yield value", + paramsWithBasicAuth, + r -> { + Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); + assertEquals("Neo4j", name); + }); + } + + static Consumer> commonEsGetConsumer() { + return r -> { + Object name = extractValueFromResponse(r, "$._source.name"); + assertEquals("Neo4j", name); + }; + } + + static Consumer> commonEsStatsConsumer() { + return commonEsStatsConsumer(3); + } + + static Consumer> commonEsStatsConsumer(int expectedNumOfDocs) { + return r -> { + assertNotNull(r.get("value")); + + Object numOfDocs = extractValueFromResponse(r, "$._all.total.docs.count"); + assertEquals(expectedNumOfDocs, numOfDocs); + }; + } +} \ No newline at end of file diff --git a/full/src/test/java/apoc/es/ElasticVersionEightTest.java b/full-it/src/test/java/apoc/full/it/es/ElasticVersionEightTest.java similarity index 87% rename from full/src/test/java/apoc/es/ElasticVersionEightTest.java rename to full-it/src/test/java/apoc/full/it/es/ElasticVersionEightTest.java index 1b2c74a77c..2802725eb5 100644 --- a/full/src/test/java/apoc/es/ElasticVersionEightTest.java +++ b/full-it/src/test/java/apoc/full/it/es/ElasticVersionEightTest.java @@ -1,5 +1,6 @@ -package apoc.es; +package apoc.full.it.es; +import apoc.es.ElasticSearchHandler; import apoc.util.TestUtil; import apoc.util.Util; import org.junit.BeforeClass; @@ -21,7 +22,7 @@ public static void setUp() throws Exception { Map config = Map.of("headers", basicAuthHeader, VERSION_KEY, ElasticSearchHandler.Version.EIGHT.name()); Map params = Util.map("index", ES_INDEX, "id", ES_ID, "type", ES_TYPE, "config", config); - + String tag = "8.12.1"; Map envMap = Map.of( "xpack.security.http.ssl.enabled", "false", @@ -35,25 +36,25 @@ public static void setUp() throws Exception { String getEsType() { return ES_TYPE; } - + @Test public void testCreateIndexAPI() { TestUtil.testCall(db, "CALL apoc.es.put($host,'my-index-000001',null,null,null,null,$config)", paramsWithBasicAuth, r -> { - Object actual = ((Map) r.get("value")).get("index"); - assertEquals("my-index-000001", actual); - }); + Object actual = ((Map) r.get("value")).get("index"); + assertEquals("my-index-000001", actual); + }); } - + @Test public void testGetIndexAPI() { TestUtil.testCall(db, "CALL apoc.es.get($host,$index,null,null,null,null,$config) yield value", paramsWithBasicAuth, r -> { - Set valueKeys = ((Map) r.get("value")).keySet(); - assertEquals(Set.of(ES_INDEX), valueKeys); - }); + Set valueKeys = ((Map) r.get("value")).keySet(); + assertEquals(Set.of(ES_INDEX), valueKeys); + }); } @Test @@ -81,4 +82,4 @@ private void searchQueryPayloadAssertions(Map r) { }); } -} +} \ No newline at end of file diff --git a/full/src/test/java/apoc/es/ElasticVersionSevenTest.java b/full-it/src/test/java/apoc/full/it/es/ElasticVersionSevenTest.java similarity index 97% rename from full/src/test/java/apoc/es/ElasticVersionSevenTest.java rename to full-it/src/test/java/apoc/full/it/es/ElasticVersionSevenTest.java index 61700b5f26..849e9c3d62 100644 --- a/full/src/test/java/apoc/es/ElasticVersionSevenTest.java +++ b/full-it/src/test/java/apoc/full/it/es/ElasticVersionSevenTest.java @@ -1,5 +1,6 @@ -package apoc.es; +package apoc.full.it.es; +import apoc.es.ElasticSearchHandler; import apoc.util.TestUtil; import apoc.util.Util; import org.junit.BeforeClass; @@ -22,10 +23,10 @@ public class ElasticVersionSevenTest extends ElasticSearchTest { public static final ElasticSearchHandler DEFAULT_HANDLER = ElasticSearchHandler.Version.DEFAULT.get(); private static final Map defaultParams = Util.map("index", ES_INDEX, "type", ES_TYPE, "id", ES_ID); - + @BeforeClass public static void setUp() throws Exception { - + Map config = Map.of("headers", basicAuthHeader); Map params = new HashMap<>(defaultParams); params.put("config", config); @@ -33,7 +34,7 @@ public static void setUp() throws Exception { String tag = "7.9.2"; getElasticContainer(tag, Map.of(), params); - + String httpHostAddress = elastic.getHttpHostAddress(); HTTP_HOST_ADDRESS = String.format("elastic:%s@%s", password, @@ -65,17 +66,17 @@ public void testGetRowProcedure() { @Test public void testStats() throws Exception { - TestUtil.testCall(db, "CALL apoc.es.stats($host)", defaultParams, + TestUtil.testCall(db, "CALL apoc.es.stats($host)", defaultParams, commonEsStatsConsumer()); } @Test public void testProceduresWithUrl() { - TestUtil.testCall(db, "CALL apoc.es.stats($url)", defaultParams, + TestUtil.testCall(db, "CALL apoc.es.stats($url)", defaultParams, commonEsStatsConsumer()); - TestUtil.testCall(db, "CALL apoc.es.get($url,$index,$type,$id,null,null) yield value", defaultParams, + TestUtil.testCall(db, "CALL apoc.es.get($url,$index,$type,$id,null,null) yield value", defaultParams, commonEsGetConsumer()); } @@ -83,10 +84,10 @@ public void testProceduresWithUrl() { public void testProceduresWithUrlKeyConf() { apocConfig().setProperty("apoc.es.myUrlKey.url", HTTP_URL_ADDRESS); - TestUtil.testCall(db, "CALL apoc.es.stats('myUrlKey')", + TestUtil.testCall(db, "CALL apoc.es.stats('myUrlKey')", commonEsStatsConsumer()); - TestUtil.testCall(db, "CALL apoc.es.get('myUrlKey',$index,$type,$id,null,null, $config) yield value", paramsWithBasicAuth, + TestUtil.testCall(db, "CALL apoc.es.get('myUrlKey',$index,$type,$id,null,null, $config) yield value", paramsWithBasicAuth, commonEsGetConsumer()); } @@ -100,7 +101,7 @@ public void testProceduresWithHostKeyConf() { TestUtil.testCall(db, "CALL apoc.es.get('myHostKey',$index,$type,$id,null,null, $config) yield value", paramsWithBasicAuth, commonEsGetConsumer()); } - + @Test public void testGetWithQueryAsStringSingleParam() { TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name',null, {}) yield value", defaultParams, @@ -186,4 +187,4 @@ public void testGetWithQueryAsMapMultipleParams() throws Exception { commonEsGetConsumer()); } -} +} \ No newline at end of file diff --git a/full/src/main/java/apoc/es/ElasticSearch.java b/full/src/main/java/apoc/es/ElasticSearch.java index 377862b850..a51941d64e 100644 --- a/full/src/main/java/apoc/es/ElasticSearch.java +++ b/full/src/main/java/apoc/es/ElasticSearch.java @@ -1,35 +1,11 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package apoc.es; -import static apoc.util.MapUtil.map; - import apoc.Extended; import apoc.load.LoadJson; import apoc.result.MapResult; -import apoc.util.UrlResolver; import apoc.util.Util; import java.util.Collections; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; @@ -42,82 +18,6 @@ @Extended public class ElasticSearch { - private static final String fullQueryTemplate = "/%s/%s/%s?%s"; - - // /{index}/{type}/_search?{query} - private static final String fullQuerySearchTemplate = "/%s/%s/_search?%s"; - - /** - * With this pattern we can match both key:value params and key=value params - */ - private static final Pattern KEY_VALUE = Pattern.compile("(.*)(:|=)(.*)"); - - protected String getElasticSearchUrl(String hostOrKey) { - return new UrlResolver("http", "localhost", 9200).getUrl("es", hostOrKey); - } - - /** - * Get the full Elasticsearch url - * - * @param hostOrKey - * @param index - * @param type - * @param id - * @param query - * @return - */ - protected String getQueryUrl(String hostOrKey, String index, String type, String id, Object query) { - return getElasticSearchUrl(hostOrKey) + formatQueryUrl(index, type, id, query); - } - - /** - * @param hostOrKey - * @param index - * @param type - * @param query - * @return - */ - protected String getSearchQueryUrl(String hostOrKey, String index, String type, Object query) { - return getElasticSearchUrl(hostOrKey) + formatSearchQueryUrl(index, type, query); - } - - /** - * @param index - * @param type - * @param query - * @return - */ - private String formatSearchQueryUrl(String index, String type, Object query) { - String queryUrl = String.format( - fullQuerySearchTemplate, - index == null ? "_all" : index, - type == null ? "_all" : type, - toQueryParams(query)); - - return queryUrl.endsWith("?") ? queryUrl.substring(0, queryUrl.length() - 1) : queryUrl; - } - - /** - * Format the query url template according to the parameters. - * The format will be /{index}/{type}/{id}?{query} if query is not empty (or null) otherwise the format will be /{index}/{type}/{id} - * - * @param index - * @param type - * @param id - * @param query - * @return - */ - private String formatQueryUrl(String index, String type, String id, Object query) { - String queryUrl = String.format( - fullQueryTemplate, - index == null ? "_all" : index, - type == null ? "_all" : type, - id == null ? "" : id, - toQueryParams(query)); - - return queryUrl.endsWith("?") ? queryUrl.substring(0, queryUrl.length() - 1) : queryUrl; - } - /** * @param payload * @return @@ -128,42 +28,18 @@ protected String toPayload(Object payload) { return payload.toString(); } - /** - * @param query - * @return - */ - protected String toQueryParams(Object query) { - if (query == null) return ""; - if (query instanceof Map) { - Map map = (Map) query; - if (map.isEmpty()) return ""; - return map.entrySet().stream() - .map(e -> e.getKey() + "=" - + Util.encodeUrlComponent(e.getValue().toString())) - .collect(Collectors.joining("&")); - } else { - // We have to encode only the values not the keys - return Pattern.compile("&") - .splitAsStream(query.toString()) - .map(KEY_VALUE::matcher) - .filter(Matcher::matches) - .map(matcher -> matcher.group(1) + matcher.group(2) + Util.encodeUrlComponent(matcher.group(3))) - .collect(Collectors.joining("&")); - } - } - @Procedure @Description("apoc.es.stats(host-or-key,$config) - elastic search statistics") public Stream stats( - @Name("hostOrKey") String hostOrKey, - @Name(value = "config", defaultValue = "{}") Map config) { - String url = getElasticSearchUrl(hostOrKey); - return loadJsonStream(url + "/_stats", new ElasticSearchConfig(config), null); + @Name("host") String hostOrKey, @Name(value = "config", defaultValue = "{}") Map config) { + ElasticSearchConfig conf = new ElasticSearchConfig(config); + String url = conf.getVersion().getElasticSearchUrl(hostOrKey); + return loadJsonStream(url + "/_stats", conf, null); } @Procedure @Description( - "apoc.es.get(host-or-key,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null) yield value - perform a GET operation on elastic search") + "apoc.es.get(host-or-key,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null,$config) yield value - perform a GET operation on elastic search") public Stream get( @Name("hostOrKey") String hostOrKey, @Name("index") String index, @@ -172,13 +48,14 @@ public Stream get( @Name("query") Object query, @Name("payload") Object payload, @Name(value = "config", defaultValue = "{}") Map config) { - return loadJsonStream( - getQueryUrl(hostOrKey, index, type, id, query), new ElasticSearchConfig(config), toPayload(payload)); + ElasticSearchConfig conf = new ElasticSearchConfig(config); + String queryUrl = conf.getVersion().getQueryUrl(hostOrKey, index, type, id, query); // .replace("mytype/", ""); + return loadJsonStream(queryUrl, conf, toPayload(payload)); } @Procedure @Description( - "apoc.es.query(host-or-key,index-or-null,type-or-null,query-or-null,payload-or-null) yield value - perform a SEARCH operation on elastic search") + "apoc.es.query(host-or-key,index-or-null,type-or-null,query-or-null,payload-or-null,$config) yield value - perform a SEARCH operation on elastic search") public Stream query( @Name("hostOrKey") String hostOrKey, @Name("index") String index, @@ -186,37 +63,42 @@ public Stream query( @Name("query") Object query, @Name("payload") Object payload, @Name(value = "config", defaultValue = "{}") Map config) { - return loadJsonStream( - getSearchQueryUrl(hostOrKey, index, type, query), new ElasticSearchConfig(config), toPayload(payload)); + ElasticSearchConfig conf = new ElasticSearchConfig(config); + String searchQueryUrl = + conf.getVersion().getSearchQueryUrl(hostOrKey, index, type, query); // .replace("mytype/", ""); + + return loadJsonStream(searchQueryUrl, conf, toPayload(payload)); } @Procedure @Description( - "apoc.es.getRaw(host-or-key,path,payload-or-null) yield value - perform a raw GET operation on elastic search") + "apoc.es.getRaw(host-or-key,path,payload-or-null,$config) yield value - perform a raw GET operation on elastic search") public Stream getRaw( @Name("hostOrKey") String hostOrKey, @Name("path") String suffix, @Name("payload") Object payload, @Name(value = "config", defaultValue = "{}") Map config) { - String url = getElasticSearchUrl(hostOrKey); - return loadJsonStream(url + "/" + suffix, new ElasticSearchConfig(config), toPayload(payload)); + ElasticSearchConfig conf = new ElasticSearchConfig(config); + String url = conf.getVersion().getElasticSearchUrl(hostOrKey); + return loadJsonStream(url + "/" + suffix, conf, toPayload(payload)); } @Procedure @Description( - "apoc.es.postRaw(host-or-key,path,payload-or-null) yield value - perform a raw POST operation on elastic search") + "apoc.es.postRaw(host-or-key,path,payload-or-null,$config) yield value - perform a raw POST operation on elastic search") public Stream postRaw( @Name("hostOrKey") String hostOrKey, @Name("path") String suffix, @Name("payload") Object payload, @Name(value = "config", defaultValue = "{}") Map config) { - String url = getElasticSearchUrl(hostOrKey); - return loadJsonStream(url + "/" + suffix, new ElasticSearchConfig(config, "POST"), toPayload(payload)); + ElasticSearchConfig conf = new ElasticSearchConfig(config, "POST"); + String url = conf.getVersion().getElasticSearchUrl(hostOrKey); + return loadJsonStream(url + "/" + suffix, conf, toPayload(payload)); } @Procedure @Description( - "apoc.es.post(host-or-key,index-or-null,type-or-null,query-or-null,payload-or-null) yield value - perform a POST operation on elastic search") + "apoc.es.post(host-or-key,index-or-null,type-or-null,query-or-null,payload-or-null,$config) yield value - perform a POST operation on elastic search") public Stream post( @Name("hostOrKey") String hostOrKey, @Name("index") String index, @@ -227,15 +109,14 @@ public Stream post( if (payload == null) { payload = Collections.emptyMap(); } - return loadJsonStream( - getQueryUrl(hostOrKey, index, type, null, query), - new ElasticSearchConfig(config, "POST"), - toPayload(payload)); + ElasticSearchConfig conf = new ElasticSearchConfig(config, "POST"); + String queryUrl = conf.getVersion().getQueryUrl(hostOrKey, index, type, null, query); + return loadJsonStream(queryUrl, conf, toPayload(payload)); } @Procedure @Description( - "apoc.es.put(host-or-key,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null) yield value - perform a PUT operation on elastic search") + "apoc.es.put(host-or-key,index-or-null,type-or-null,id-or-null,query-or-null,payload-or-null,$config) yield value - perform a PUT operation on elastic search") public Stream put( @Name("hostOrKey") String hostOrKey, @Name("index") String index, @@ -247,10 +128,10 @@ public Stream put( if (payload == null) { payload = Collections.emptyMap(); } - return loadJsonStream( - getQueryUrl(hostOrKey, index, type, id, query), - new ElasticSearchConfig(config, "PUT"), - toPayload(payload)); + + ElasticSearchConfig conf = new ElasticSearchConfig(config, "PUT"); + String queryUrl = conf.getVersion().getQueryUrl(hostOrKey, index, type, id, query); + return loadJsonStream(queryUrl, conf, toPayload(payload)); } @Procedure @@ -269,8 +150,9 @@ public Stream delete( Otherwise, an error `Cannot write to a URLConnection if doOutput=false - call setDoOutput(true)` will be thrown */ String payload = ""; - return loadJsonStream( - getQueryUrl(hostOrKey, index, type, id, query), new ElasticSearchConfig(config, "DELETE"), payload); + ElasticSearchConfig conf = new ElasticSearchConfig(config, "DELETE"); + String queryUrl = conf.getVersion().getQueryUrl(hostOrKey, index, type, id, query); + return loadJsonStream(queryUrl, conf, payload); } private Stream loadJsonStream( diff --git a/full/src/main/java/apoc/es/ElasticSearchConfig.java b/full/src/main/java/apoc/es/ElasticSearchConfig.java index 7ff0ac0b3a..c06bc0d108 100644 --- a/full/src/main/java/apoc/es/ElasticSearchConfig.java +++ b/full/src/main/java/apoc/es/ElasticSearchConfig.java @@ -18,12 +18,12 @@ */ package apoc.es; +import static apoc.es.ElasticSearchHandler.Version; + import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static apoc.es.ElasticSearchHandler.Version; - public class ElasticSearchConfig { public static final String HEADERS_KEY = "headers"; public static final String VERSION_KEY = "version"; @@ -46,7 +46,7 @@ public ElasticSearchConfig(Map config, String httpMethod) { headerConf.putIfAbsent("method", httpMethod); } this.headers = headerConf; - + String versionConf = (String) config.getOrDefault(VERSION_KEY, Version.DEFAULT.name()); this.version = Version.valueOf(versionConf).get(); } diff --git a/full/src/main/java/apoc/es/ElasticSearchHandler.java b/full/src/main/java/apoc/es/ElasticSearchHandler.java index f3c69d1d39..eb13096460 100644 --- a/full/src/main/java/apoc/es/ElasticSearchHandler.java +++ b/full/src/main/java/apoc/es/ElasticSearchHandler.java @@ -1,41 +1,43 @@ package apoc.es; - import apoc.util.UrlResolver; import apoc.util.Util; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; - import java.util.Arrays; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; public abstract class ElasticSearchHandler { /** * With this pattern we can match both key:value params and key=value params */ - private final static Pattern KEY_VALUE = Pattern.compile("(.*)(:|=)(.*)"); + private static final Pattern KEY_VALUE = Pattern.compile("(.*)(:|=)(.*)"); - protected String getElasticSearchUrl(String hostOrKey) { + public String getElasticSearchUrl(String hostOrKey) { return new UrlResolver("http", "localhost", 9200).getUrl("es", hostOrKey); } - + /** * @param query * @return */ - protected String toQueryParams(Object query) { + public String toQueryParams(Object query) { if (query == null) return ""; if (query instanceof Map) { Map map = (Map) query; if (map.isEmpty()) return ""; - return map.entrySet().stream().map(e -> e.getKey() + "=" + Util.encodeUrlComponent(e.getValue().toString())).collect(Collectors.joining("&")); + return map.entrySet().stream() + .map(e -> e.getKey() + "=" + + Util.encodeUrlComponent(e.getValue().toString())) + .collect(Collectors.joining("&")); } else { // We have to encode only the values not the keys - return Pattern.compile("&").splitAsStream(query.toString()) + return Pattern.compile("&") + .splitAsStream(query.toString()) .map(KEY_VALUE::matcher) .filter(Matcher::matches) .map(matcher -> matcher.group(1) + matcher.group(2) + Util.encodeUrlComponent(matcher.group(3))) @@ -46,33 +48,34 @@ protected String toQueryParams(Object query) { /** * Get the full Elasticsearch url */ - protected String getQueryUrl(String hostOrKey, String index, String type, String id, Object query) { + public String getQueryUrl(String hostOrKey, String index, String type, String id, Object query) { return getElasticSearchUrl(hostOrKey) + formatQueryUrl(index, type, id, query); } /** * Get the full Elasticsearch search url */ - protected String getSearchQueryUrl(String hostOrKey, String index, String type, Object query) { + public String getSearchQueryUrl(String hostOrKey, String index, String type, Object query) { return getElasticSearchUrl(hostOrKey) + formatSearchQueryUrl(index, type, query); } /** * Format the Search API url template according to the parameters. */ - protected abstract String formatSearchQueryUrl(String index, String type, Object query); + public abstract String formatSearchQueryUrl(String index, String type, Object query); /** * Format the query url template according to the parameters. * The format will be /{index}/{type}/{id}?{query} if query is not empty (or null) otherwise the format will be /{index}/{type}/{id} */ - protected abstract String formatQueryUrl(String index, String type, String id, Object query); - - enum Version { + public abstract String formatQueryUrl(String index, String type, String id, Object query); + + public enum Version { EIGHT(new Eight()), DEFAULT(new Default()); private final ElasticSearchHandler handler; + Version(ElasticSearchHandler handler) { this.handler = handler; } @@ -82,43 +85,39 @@ public ElasticSearchHandler get() { } } - static class Eight extends ElasticSearchHandler { + public static class Eight extends ElasticSearchHandler { @Override - protected String formatSearchQueryUrl(String index, String type, Object query) { - - String queryUrl = String.format( "/%s/_search?%s", - index == null ? "_all" : index, - toQueryParams(query)); - + public String formatSearchQueryUrl(String index, String type, Object query) { + + String queryUrl = String.format("/%s/_search?%s", index == null ? "_all" : index, toQueryParams(query)); + return removeTerminalQuote(queryUrl); } @Override - protected String formatQueryUrl(String index, String type, String id, Object query) { + public String formatQueryUrl(String index, String type, String id, Object query) { - String queryUrl = Arrays.asList(index, type, id) - .stream() + String queryUrl = Arrays.asList(index, type, id).stream() .filter(StringUtils::isNotBlank) .collect(Collectors.joining("/")); String queryParams = toQueryParams(query); - queryParams = "".equals(queryParams) - ? "" - : ("?" + queryParams); + queryParams = "".equals(queryParams) ? "" : ("?" + queryParams); return "/" + queryUrl + queryParams; } } - static class Default extends ElasticSearchHandler { + public static class Default extends ElasticSearchHandler { private final String fullQueryTemplate = "/%s/%s/%s?%s"; private final String fullQuerySearchTemplate = "/%s/%s/_search?%s"; - + @Override - protected String formatSearchQueryUrl(String index, String type, Object query) { - String queryUrl = String.format(fullQuerySearchTemplate, + public String formatSearchQueryUrl(String index, String type, Object query) { + String queryUrl = String.format( + fullQuerySearchTemplate, index == null ? "_all" : index, type == null ? "_all" : type, toQueryParams(query)); @@ -127,8 +126,9 @@ protected String formatSearchQueryUrl(String index, String type, Object query) { } @Override - protected String formatQueryUrl(String index, String type, String id, Object query) { - String queryUrl = String.format(fullQueryTemplate, + public String formatQueryUrl(String index, String type, String id, Object query) { + String queryUrl = String.format( + fullQueryTemplate, index == null ? "_all" : index, type == null ? "_all" : type, id == null ? "" : id, diff --git a/full/src/test/java/apoc/es/ElasticSearchTest.java b/full/src/test/java/apoc/es/ElasticSearchTest.java deleted file mode 100644 index 4f5fa1784a..0000000000 --- a/full/src/test/java/apoc/es/ElasticSearchTest.java +++ /dev/null @@ -1,786 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package apoc.es; - -import static apoc.ApocConfig.apocConfig; -import static org.junit.Assert.*; - -import apoc.util.JsonUtil; -import apoc.util.TestUtil; -import apoc.util.Util; -import com.jayway.jsonpath.Configuration; -import com.jayway.jsonpath.JsonPath; -import com.jayway.jsonpath.Option; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.neo4j.test.rule.DbmsRule; -import org.neo4j.test.rule.ImpermanentDbmsRule; -import org.testcontainers.elasticsearch.ElasticsearchContainer; - -import java.io.IOException; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import static apoc.ApocConfig.apocConfig; -import static apoc.util.MapUtil.map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * @author mh - * @since 21.05.16 - */ -public class ElasticSearchTest { - - private static final String URL_CONF = "apoc.es.url"; - private static String HTTP_HOST_ADDRESS; - private static String HTTP_URL_ADDRESS; - - static String HTTP_HOST_ADDRESS; - static String HTTP_URL_ADDRESS; - - public static ElasticsearchContainer elastic; - - private static final String ES_INDEX = "test-index"; - final static String ES_INDEX = "test-index"; - - private static final String ES_TYPE = "test-type"; - abstract String getEsType(); - - private static final String ES_ID = UUID.randomUUID().toString(); - final static String ES_ID = UUID.randomUUID().toString(); - - private static final String HOST = "localhost"; - - private static final String DOCUMENT = - "{\"name\":\"Neo4j\",\"company\":\"Neo Technology\",\"description\":\"Awesome stuff with a graph database\"}"; - private static final String DOCUMENT = "{\"name\":\"Neo4j\",\"company\":\"Neo Technology\",\"description\":\"Awesome stuff with a graph database\"}"; - - final static String password = "myPassword"; - static Map basicAuthHeader = Map.of("Authorization", - "Basic " + Base64.getEncoder().encodeToString(("elastic:" + password).getBytes())); - - @ClassRule - public static DbmsRule db = new ImpermanentDbmsRule(); - - private static Map defaultParams = Util.map("index", ES_INDEX, "type", ES_TYPE, "id", ES_ID); - private static Map paramsWithBasicAuth; - private static Map basicAuthHeader; - - static Map paramsWithBasicAuth; - - // We need a reference to the class implementing the procedures - private final ElasticSearch es = new ElasticSearch(); - private static final Configuration JSON_PATH_CONFIG = Configuration.builder() - .options(Option.DEFAULT_PATH_LEAF_TO_NULL, Option.SUPPRESS_EXCEPTIONS) - .build(); - private static final Configuration JSON_PATH_CONFIG = Configuration.builder().options(Option.DEFAULT_PATH_LEAF_TO_NULL, Option.SUPPRESS_EXCEPTIONS).build(); - - @BeforeClass - public static void setUp() throws Exception { - final String password = "myPassword"; - elastic = new ElasticsearchContainer(); - static void getElasticContainer(String tag, Map envMap, Map params) throws JsonProcessingException { - - elastic = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:" + tag) - .withPassword(password) - .withEnv(envMap); - elastic.start(); - defaultParams.put("host", elastic.getHttpHostAddress()); - - String httpHostAddress = elastic.getHttpHostAddress(); - HTTP_HOST_ADDRESS = String.format("elastic:%s@%s", password, httpHostAddress); - - HTTP_HOST_ADDRESS = String.format("elastic:%s@%s", - password, - httpHostAddress); - - HTTP_URL_ADDRESS = "http://" + HTTP_HOST_ADDRESS; - - defaultParams.put("host", HTTP_HOST_ADDRESS); - defaultParams.put("url", HTTP_URL_ADDRESS); - - // We can authenticate to elastic using the url `:@` - // or via Basic authentication, i.e. using the url `` together with the header `Authorization: - // Basic ` - // where is Base64(:) - String token = Base64.getEncoder().encodeToString(("elastic:" + password).getBytes()); - basicAuthHeader = Map.of("Authorization", "Basic " + token); - - paramsWithBasicAuth = new HashMap<>(defaultParams); - paramsWithBasicAuth.put("host", elastic.getHttpHostAddress()); - paramsWithBasicAuth.put("headers", basicAuthHeader); - - params.put("host", elastic.getHttpHostAddress()); - params.put("url", "http://" + elastic.getHttpHostAddress()); - paramsWithBasicAuth = params; - TestUtil.registerProcedure(db, ElasticSearch.class); - insertDocuments(); - } - - private static String getRawProcedureUrl(String id) { - return ES_INDEX + "/" + ES_TYPE + "/" + id + "?refresh=true"; - } - - @AfterClass - public static void tearDown() { - elastic.stop(); - db.shutdown(); - } - - /** - * Default params (host, index, type, id) + payload - * - * @param payload - * @return - */ - private static Map createDefaultProcedureParametersWithPayloadAndId(String payload, String id) { - try { - Map mapPayload = JsonUtil.OBJECT_MAPPER.readValue(payload, Map.class); - return addPayloadAndIdToParams(defaultParams, mapPayload, id); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static Map addPayloadAndIdToParams(Map params, Object payload, String id) { - return Util.merge(params, Util.map("payload", payload, "id", id)); - - static Map addPayloadAndIdToParams(Map params, Object payload, String id) { - return Util.merge(params, Util.map("payload", payload, "id", id)); - } - - private static void insertDocuments() { - Map params = createDefaultProcedureParametersWithPayloadAndId( - "{\"procedurePackage\":\"es\",\"procedureName\":\"get\",\"procedureDescription\":\"perform a GET operation to ElasticSearch\"}", - UUID.randomUUID().toString()); - TestUtil.testCall( - db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - - private static void insertDocuments() throws JsonProcessingException { - Map params = createDefaultProcedureParametersWithPayloadAndId("{\"procedurePackage\":\"es\",\"procedureName\":\"get\",\"procedureDescription\":\"perform a GET operation to ElasticSearch\"}", UUID.randomUUID().toString()); - TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - - params = createDefaultProcedureParametersWithPayloadAndId( - "{\"procedurePackage\":\"es\",\"procedureName\":\"post\",\"procedureDescription\":\"perform a POST operation to ElasticSearch\"}", - UUID.randomUUID().toString()); - TestUtil.testCall( - db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - params = createDefaultProcedureParametersWithPayloadAndId("{\"procedurePackage\":\"es\",\"procedureName\":\"post\",\"procedureDescription\":\"perform a POST operation to ElasticSearch\"}", UUID.randomUUID().toString()); - TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - - params = createDefaultProcedureParametersWithPayloadAndId(DOCUMENT, ES_ID); - TestUtil.testCall( - db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { - Object created = extractValueFromResponse(r, "$.result"); - assertEquals("created", created); - }); - } - - private static Object extractValueFromResponse(Map response, String jsonPath) { - Object jsonResponse = response.get("value"); - assertNotNull(jsonResponse); - - String json = JsonPath.parse(jsonResponse).jsonString(); - Object value = JsonPath.parse(json, JSON_PATH_CONFIG).read(jsonPath); - - return value; - } - - @Test - public void testStats() throws Exception { - TestUtil.testCall(db, "CALL apoc.es.stats($host)", defaultParams, commonEsStatsConsumer()); - } - - @Test - public void testStatsWithAuthHeader() { - TestUtil.testCall( - db, "CALL apoc.es.stats($host, {headers: $headers})", paramsWithBasicAuth, commonEsStatsConsumer()); - return JsonPath.parse(json, JSON_PATH_CONFIG).read(jsonPath); - } - - /** - * Simple get request for document retrieval - * http://localhost:9200/test-index/test-type/ee6749ff-b836-4529-88e9-3105675d625a - * - * @throws Exception - */ - @Test - public void testGetWithQueryNull() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.get($host,$index,$type,$id,null,null) yield value", - defaultParams, - commonEsGetConsumer()); - } - - @Test - public void testProceduresWithUrl() { - TestUtil.testCall(db, "CALL apoc.es.stats($url)", defaultParams, commonEsStatsConsumer()); - - TestUtil.testCall( - db, - "CALL apoc.es.get($url,$index,$type,$id,null,null) yield value", - defaultParams, - TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,null,null,$config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - } - - @Test - public void testProceduresWithUrlAndHeaders() { - TestUtil.testCall( - db, "CALL apoc.es.stats($url, {headers: $headers})", paramsWithBasicAuth, commonEsStatsConsumer()); - TestUtil.testCall(db, "CALL apoc.es.stats($url, $config)", paramsWithBasicAuth, - commonEsStatsConsumer()); - - TestUtil.testCall( - db, - "CALL apoc.es.get($url,$index,$type,$id,null,null) yield value", - paramsWithBasicAuth, - commonEsGetConsumer()); - } - - @Test - public void testGetRowProcedure() { - Map params = Map.of("url", HTTP_URL_ADDRESS, "suffix", getRawProcedureUrl(ES_ID)); - - TestUtil.testCall(db, "CALL apoc.es.getRaw($url,$suffix, null)", params, commonEsGetConsumer()); - TestUtil.testCall(db, "CALL apoc.es.get($url,$index,$type,$id,null,null,$config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - } - - @Test - public void testGetRowProcedureWithAuthHeader() { - Map params = Map.of( - "url", elastic.getHttpHostAddress(), "suffix", getRawProcedureUrl(ES_ID), "headers", basicAuthHeader); - Map params = Map.of("url", elastic.getHttpHostAddress(), - "suffix", getRawProcedureUrl(ES_ID, getEsType()), - "headers", basicAuthHeader); - - TestUtil.testCall( - db, "CALL apoc.es.getRaw($url, $suffix, null, {headers: $headers})", params, commonEsGetConsumer()); - } - - @Test - public void testProceduresWithUrlKeyConfOverridingGenericUrlConf() { - apocConfig().setProperty("apoc.es.customKey.url", HTTP_URL_ADDRESS); - apocConfig().setProperty(URL_CONF, "wrongUrl"); - - TestUtil.testCall(db, "CALL apoc.es.stats('customKey')", commonEsStatsConsumer()); - - TestUtil.testCall( - db, - "CALL apoc.es.get('customKey',$index,$type,$id,null,null) yield value", - defaultParams, - TestUtil.testCall(db, "CALL apoc.es.get('customKey',$index,$type,$id,null,null, $config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - - apocConfig().getConfig().clearProperty(URL_CONF); - } - - @Test - public void testProceduresWithUrlKeyConf() { - apocConfig().setProperty("apoc.es.myUrlKey.url", HTTP_URL_ADDRESS); - - TestUtil.testCall(db, "CALL apoc.es.stats('myUrlKey')", commonEsStatsConsumer()); - - TestUtil.testCall( - db, - "CALL apoc.es.get('myUrlKey',$index,$type,$id,null,null) yield value", - defaultParams, - commonEsGetConsumer()); - } - - @Test - public void testProceduresWithHostKeyConf() { - apocConfig().setProperty("apoc.es.myHostKey.host", HTTP_HOST_ADDRESS); - - TestUtil.testCall(db, "CALL apoc.es.stats('myHostKey')", commonEsStatsConsumer()); - - TestUtil.testCall( - db, - "CALL apoc.es.get('myHostKey',$index,$type,$id,null,null) yield value", - defaultParams, - commonEsGetConsumer()); - } - - /** - * Simple get request for document retrieval but we also send multiple commands (as a Map) to ES - * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name&_source_excludes=description - * - * @throws Exception - */ - @Test - public void testGetWithQueryAsMapMultipleParams() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.get($host,$index,$type,$id,{_source_includes:'name',_source_excludes:'description'},null) yield value", - defaultParams, - commonEsGetConsumer()); - } - - /** - * Simple get request for document retrieval but we also send a single command (as a Map) to ES - * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name - * - * @throws Exception - */ - @Test - public void testGetWithQueryAsMapSingleParam() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.get($host,$index,$type,$id,{_source_includes:'name'},null) yield value", - defaultParams, - TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,{_source_includes:'name'},null, $config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - } - - /** - * Simple get request for document retrieval but we also send multiple commands (as a string) to ES - * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name&_source_excludes=description - * - * @throws Exception - */ - @Test - public void testGetWithQueryAsStringMultipleParams() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name&_source_excludes=description',null) yield value", - defaultParams, - TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name&_source_excludes=description',null, $config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - } - - /** - * Simple get request for document retrieval but we also send a single command (as a string) to ES - * http://localhost:9200/test-index/test-type/4fa40c40-db89-4761-b6a3-75f0015db059?_source_includes=name - * - * @throws Exception - */ - @Test - public void testGetWithQueryAsStringSingleParam() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name',null) yield value", - defaultParams, - public void testGetWithHeaderAndQueryAsStringSingleParam() throws Exception { - TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,'_source_includes=name',null, $config) yield value", paramsWithBasicAuth, - commonEsGetConsumer()); - } - - /** - * We want to search our document by name --> /test-index/test-type/_search? - * This test uses a plain string to query ES - */ - @Test - public void testSearchWithQueryNull() throws Exception { - TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,null,null) yield value", defaultParams, r -> { - Object hits = extractValueFromResponse(r, "$.hits.hits"); - assertEquals(3, ((List) hits).size()); - }); - } - - /** - * We want to search our document by name --> /test-index/test-type/_search?q=name:Neo4j - * This test uses a plain string to query ES - */ - @Test - public void testSearchWithQueryAsAString() throws Exception { - TestUtil.testCall( - db, "CALL apoc.es.query($host,$index,$type,'q=name:Neo4j',null) yield value", defaultParams, r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - public void testStatsWithAuthHeader() { - TestUtil.testCall(db, "CALL apoc.es.stats($host, $config)", paramsWithBasicAuth, - commonEsStatsConsumer()); - } - - @Test - public void testSearchWithQueryAsAStringAndHeader() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.query($host, $index, $type, 'q=name:Neo4j', null, {headers: $headers}) yield value", - paramsWithBasicAuth, - r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - TestUtil.testCall(db, "CALL apoc.es.query($host, $index, $type, 'q=name:Neo4j', null, $config) yield value", paramsWithBasicAuth, r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - } - - /** - * We want to search our document by name --> /test-index/test-type/_search?q=name:* - * This test uses a plain string to query ES - */ - @Test - public void testFullSearchWithQueryAsAString() throws Exception { - TestUtil.testCall( - db, "CALL apoc.es.query($host,$index,$type,'q=name:*',null) yield value", defaultParams, r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'q=name:*',null, $config) yield value", paramsWithBasicAuth, r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - } - - /** - * We want to search our document by name --> /test-index/test-type/_search?q=procedureName:get - * This test uses a plain string to query ES - */ - @Test - public void testFullSearchWithQueryAsAStringWithEquals() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.query($host,$index,$type,'q=procedureName:get',null) yield value", - defaultParams, - r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); - assertEquals("get", name); - }); - TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'q=procedureName:get',null, $config) yield value", paramsWithBasicAuth, r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); - assertEquals("get", name); - }); - } - - /** - * We want to search our document by name --> /test-index/test-type/_search?size=1&scroll=1m&_source=true - * This test uses a plain string to query ES - */ - @Test - public void testFullSearchWithOtherParametersAsAString() throws Exception { - TestUtil.testCall( - db, - "CALL apoc.es.query($host,$index,$type,'size=1&scroll=1m&_source=true&q=procedureName:get',null) yield value", - defaultParams, - r -> { - Object hits = extractValueFromResponse(r, "$.hits.hits"); - assertEquals(1, ((List) hits).size()); - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); - assertEquals("get", name); - }); - TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,'size=1&scroll=1m&_source=true&q=procedureName:get',null, $config) yield value", paramsWithBasicAuth, r -> { - Object hits = extractValueFromResponse(r, "$.hits.hits"); - assertEquals(1, ((List) hits).size()); - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.procedureName"); - assertEquals("get", name); - }); - } - - /** - * We create a document with a field tags that is a collection of a single element "awesome". - * Then we update the same field with the collection ["beautiful"] - * and we retrieve the document in order to verify the update. - *

- * http://localhost:9200/test-index/test-type/f561c1c5-4092-4c5d-98a6-5ea2b3417415/_update - */ - @Test - public void testPutUpdateDocument() throws IOException { - Map doc = JsonUtil.OBJECT_MAPPER.readValue(DOCUMENT, Map.class); - doc.put("tags", Arrays.asList("awesome")); - Map params = - createDefaultProcedureParametersWithPayloadAndId(JsonUtil.OBJECT_MAPPER.writeValueAsString(doc), ES_ID); - TestUtil.testCall( - db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload) yield value", params, r -> { - Object updated = extractValueFromResponse(r, "$.result"); - assertEquals("updated", updated); - }); - Map params = createDefaultProcedureParametersWithPayloadAndId(JsonUtil.OBJECT_MAPPER.writeValueAsString(doc), ES_ID); - TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", params, r -> { - Object updated = extractValueFromResponse(r, "$.result"); - assertEquals("updated", updated); - }); - - TestUtil.testCall(db, "CALL apoc.es.get($host,$index,$type,$id,null,null) yield value", params, r -> { - Object tag = extractValueFromResponse(r, "$._source.tags[0]"); - assertEquals("awesome", tag); - }); - } - - @Test - public void testPutUpdateDocumentWithAuthHeader() throws IOException { - String tags = UUID.randomUUID().toString(); - - Map doc = JsonUtil.OBJECT_MAPPER.readValue(DOCUMENT, Map.class); - doc.put("tags", Arrays.asList(tags)); - Map params = addPayloadAndIdToParams(paramsWithBasicAuth, doc, ES_ID); - TestUtil.testCall( - db, - "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, {headers: $headers}) yield value", - params, - TestUtil.testCall(db, "CALL apoc.es.put($host,$index,$type,$id,'refresh=true',$payload, $config) yield value", - params, - r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("updated", result); - }); - - TestUtil.testCall( - db, - "CALL apoc.es.get($host, $index, $type, $id, null, null, {headers: $headers}) yield value", - TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null, $config) yield value", - params, - r -> { - Object actualTags = extractValueFromResponse(r, "$._source.tags[0]"); - assertEquals(tags, actualTags); - }); - } - - @Test - public void testPostRawCreateDocument() throws IOException { - String index = UUID.randomUUID().toString(); - String type = UUID.randomUUID().toString(); - String id = UUID.randomUUID().toString(); - Map payload = JsonUtil.OBJECT_MAPPER.readValue("{\"ajeje\":\"Brazorf\"}", Map.class); - Map params = Util.map( - "host", - HTTP_HOST_ADDRESS, - "index", - index, - "suffix", - index, - "type", - type, - "payload", - payload, - "suffixDelete", - index, - "suffixPost", - index + "/" + type + "/" + id + "?refresh=true", - "id", - id); - - TestUtil.testCall(db, "CALL apoc.es.postRaw($host, $suffixPost, $payload) yield value", params, r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("created", result); - }); - - TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null) yield value", params, r -> { - Object response = extractValueFromResponse(r, "$._source.ajeje"); - assertEquals("Brazorf", response); - }); - - TestUtil.testCall(db, "CALL apoc.es.delete($host, $index, $type, $id, 'refresh=true')", params, r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("deleted", result); - }); - } - - @Test - public void testPostCreateDocumentWithAuthHeader() throws IOException { - String index = UUID.randomUUID().toString(); - String type = UUID.randomUUID().toString(); - Map payload = JsonUtil.OBJECT_MAPPER.readValue("{\"ajeje\":\"Brazorf\"}", Map.class); - Map params = Util.map( - "host", - elastic.getHttpHostAddress(), - "index", - index, - "type", - type, - "payload", - payload, - "suffix", - index, - "headers", - basicAuthHeader); - - Map params = Util.map("host", elastic.getHttpHostAddress(), - "index", index, - "type", type, - "payload", payload, - "suffix", index, - "config", map("headers", basicAuthHeader)); - - AtomicReference id = new AtomicReference<>(); - TestUtil.testCall( - db, - "CALL apoc.es.post($host,$index,$type,'refresh=true', $payload, {headers: $headers}) yield value", - params, - r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("created", result); - TestUtil.testCall(db, "CALL apoc.es.post($host,$index,$type,'refresh=true', $payload, $config) yield value", params, r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("created", result); - - id.set((String) ((Map) r.get("value")).get("_id")); - }); - - params.put("id", id.get()); - - TestUtil.testCall( - db, - "CALL apoc.es.get($host, $index, $type, $id, null, null, {headers: $headers}) yield value", - - TestUtil.testCall(db, "CALL apoc.es.get($host, $index, $type, $id, null, null, $config) yield value", - params, - r -> { - Object actual = extractValueFromResponse(r, "$._source.ajeje"); - assertEquals("Brazorf", actual); - }); - - TestUtil.testCall( - db, - "CALL apoc.es.delete($host, $index, $type, $id, 'refresh=true', {headers: $headers})", - params, - r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("deleted", result); - }); - - TestUtil.testCall(db, "CALL apoc.es.delete($host, $index, $type, $id, 'refresh=true', $config)", params, r -> { - Object result = extractValueFromResponse(r, "$.result"); - assertEquals("deleted", result); - }); - } - - /** - * We want to to search our document by name --> /test-index/test-type/_search?q=name:Neo4j - * This test uses a Map to query ES - */ - @Test - public void testSearchWithQueryAsAMap() { - TestUtil.testCall( - db, - "CALL apoc.es.query($host,$index,$type,null,{query: {match: {name: 'Neo4j'}}}) yield value", - defaultParams, - TestUtil.testCall(db, "CALL apoc.es.query($host,$index,$type,null,{query: {match: {name: 'Neo4j'}}}, $config) yield value", - paramsWithBasicAuth, - r -> { - Object name = extractValueFromResponse(r, "$.hits.hits[0]._source.name"); - assertEquals("Neo4j", name); - }); - } - - @Test - public void testGetQueryUrlShouldBeTheSameAsOldFormatting() { - String index = ES_INDEX; - String type = ES_TYPE; - String id = ES_ID; - Map query = new HashMap<>(); - query.put("name", "get"); - - String host = HOST; - String hostUrl = es.getElasticSearchUrl(host); - - String queryUrl = hostUrl - + String.format( - "/%s/%s/%s?%s", - index == null ? "_all" : index, - type == null ? "_all" : type, - id == null ? "" : id, - es.toQueryParams(query)); - - assertEquals(queryUrl, es.getQueryUrl(host, index, type, id, query)); - } - - @Test - public void testGetQueryUrlShouldNotHaveTrailingQuestionMarkIfQueryIsNull() { - String index = ES_INDEX; - String type = ES_TYPE; - String id = ES_TYPE; - - String host = HOST; - String hostUrl = es.getElasticSearchUrl(host); - String queryUrl = hostUrl - + String.format( - "/%s/%s/%s?%s", - index == null ? "_all" : index, - type == null ? "_all" : type, - id == null ? "" : id, - es.toQueryParams(null)); - - // First we test the older version against the newest one - assertNotEquals(queryUrl, es.getQueryUrl(host, index, type, id, null)); - assertTrue(!es.getQueryUrl(host, index, type, id, null).endsWith("?")); - } - - @Test - public void testGetQueryUrlShouldNotHaveTrailingQuestionMarkIfQueryIsEmpty() { - String index = ES_INDEX; - String type = ES_TYPE; - String id = ES_ID; - - String host = HOST; - String hostUrl = es.getElasticSearchUrl(host); - String queryUrl = hostUrl - + String.format( - "/%s/%s/%s?%s", - index == null ? "_all" : index, - type == null ? "_all" : type, - id == null ? "" : id, - es.toQueryParams(new HashMap())); - - // First we test the older version against the newest one - assertNotEquals(queryUrl, es.getQueryUrl(host, index, type, id, new HashMap())); - assertTrue(!es.getQueryUrl(host, index, type, id, new HashMap()) - .endsWith("?")); - } - - private static Consumer> commonEsGetConsumer() { - static Consumer> commonEsGetConsumer() { - return r -> { - Object name = extractValueFromResponse(r, "$._source.name"); - assertEquals("Neo4j", name); - }; - } - - private static Consumer> commonEsStatsConsumer() { - static Consumer> commonEsStatsConsumer() { - return commonEsStatsConsumer(3); - } - - static Consumer> commonEsStatsConsumer(int expectedNumOfDocs) { - return r -> { - assertNotNull(r.get("value")); - Object numOfDocs = extractValueFromResponse(r, "$._all.total.docs.count"); - assertEquals(3, numOfDocs); - }; - } -}