This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 400e24d23203 CAMEL-26228: camel-neo4j - fail clearly on CREATE_NODE
without a body and on a search body that is not a vector, search with a List or
array of numbers, give Cypher records whose first column is not a node and the
score of each hit, and fix the header metadata (#29070)
400e24d23203 is described below
commit 400e24d23203ce3a2687b51051f82668784a5bfb
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Nov 3 12:12:22 2026 +0100
CAMEL-26228: camel-neo4j - fail clearly on CREATE_NODE without a body and
on a search body that is not a vector, search with a List or array of numbers,
give Cypher records whose first column is not a node and the score of each hit,
and fix the header metadata (#29070)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../org/apache/camel/catalog/components/neo4j.json | 26 +-
.../apache/camel/catalog/docs/neo4j-component.adoc | 20 +-
.../org/apache/camel/component/neo4j/neo4j.json | 26 +-
.../camel-neo4j/src/main/docs/neo4j-component.adoc | 20 +-
.../camel/component/neo4j/Neo4jConfiguration.java | 14 +
.../camel/component/neo4j/Neo4jEndpoint.java | 13 +-
.../apache/camel/component/neo4j/Neo4jHeaders.java | 32 +-
.../camel/component/neo4j/Neo4jProducer.java | 119 +++++--
.../component/neo4j/Neo4jProducerBodyTest.java | 343 +++++++++++++++++++++
.../camel/component/neo4j/it/Neo4jNodeIT.java | 26 ++
.../neo4j/it/Neo4jVectorEmbeddingsIT.java | 16 +
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 21 ++
.../dsl/Neo4jComponentBuilderFactory.java | 6 +-
.../endpoint/dsl/Neo4jEndpointBuilderFactory.java | 43 ++-
14 files changed, 638 insertions(+), 87 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/neo4j.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/neo4j.json
index 6ce70e1b0fb3..d5646444b165 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/neo4j.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/neo4j.json
@@ -34,7 +34,7 @@
"maxResults": { "index": 7, "kind": "property", "displayName": "Max
Results", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": 3, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Maximum results for Vector Similarity search"
},
"minScore": { "index": 8, "kind": "property", "displayName": "Min Score",
"group": "producer", "label": "", "required": false, "type": "number",
"javaType": "double", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 0.0, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Minimum score for Vector Similarity search" },
"operation": { "index": 9, "kind": "property", "displayName": "Operation",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "org.apache.camel.component.neo4j.Neo4Operation", "enum": [
"CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "autowired": false, "secr [...]
- "query": { "index": 10, "kind": "property", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Cypher Query" },
+ "query": { "index": 10, "kind": "property", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": true, "deprecationNote": "Not
used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the message
body", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
[...]
"similarityFunction": { "index": 11, "kind": "property", "displayName":
"Similarity Function", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType":
"org.apache.camel.component.neo4j.Neo4jSimilarityFunction", "enum": [ "cosine",
"euclidean" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "cosine", "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "S [...]
"vectorIndexName": { "index": 12, "kind": "property", "displayName":
"Vector Index Name", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Vector Index Name" },
"autowiredEnabled": { "index": 13, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching [...]
@@ -47,21 +47,21 @@
},
"headers": {
"CamelNeo4jOperation": { "index": 0, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"enum": [ "CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The op [...]
- "CamelNeo4jMatchProperties": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "MATCH properties for the generated
MATCH query. Needed only if we are matching properties and values. Example:
{name: 'Alice'}", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#MATCH_PROPERTIES" },
- "CamelNeo4jQueryResult": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Query Result", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT" },
- "CamelNeo4jQueryResultNodesCreated": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes created",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_CREATED" },
- "CamelNeo4jQueryResultNodesDeleted": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes deleted",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_DELETED" },
+ "CamelNeo4jMatchProperties": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "MATCH properties for the generated
MATCH query of RETRIEVE_NODES and DELETE_NODE, as a JSON object of the property
names and values. Needed only if we are matching properties and values.",
"constantName": "org.apache.camel.component.neo [...]
+ "CamelNeo4jQueryResult": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The text of the Cypher query that the operation ran, set
by CREATE_NODE, DELETE_NODE, ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY,
CREATE_VECTOR_INDEX, DROP_VECTOR_INDEX and CREATE_VECTOR", "constantName":
"org.apache.camel.component.neo4j.Neo4jHe [...]
+ "CamelNeo4jQueryResultNodesCreated": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes created",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_CREATED" },
+ "CamelNeo4jQueryResultNodesDeleted": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes deleted",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_DELETED" },
"CamelNeo4jQueryResultContainsUpdates": { "index": 5, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query executed contains update",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_CONTAINS_UPDATES" },
- "CamelNeo4jQueryResultRelationshipsCreated": { "index": 6, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships created", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_CREATED"
},
- "CamelNeo4jQueryResultRelationshipsDeleted": { "index": 7, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships deleted", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_DELETED"
},
- "CamelNeo4jQueryResultRetrieveSize": { "index": 8, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Number of nodes retrieved",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_SIZE" },
- "CamelNeo4jQueryResultListNeo4jNodes": { "index": 9, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query execution time in Milliseconds",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_LIST_NEO4J_NODES"
},
+ "CamelNeo4jQueryResultRelationshipsCreated": { "index": 6, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Integer", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships created", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_CREATED"
},
+ "CamelNeo4jQueryResultRelationshipsDeleted": { "index": 7, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Integer", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships deleted", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_DELETED"
},
+ "CamelNeo4jQueryResultRetrieveSize": { "index": 8, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of records that
RETRIEVE_NODES, RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
VECTOR_SIMILARITY_SEARCH retrieved", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_SIZE" },
+ "CamelNeo4jQueryResultListNeo4jNodes": { "index": 9, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "java.util.List<org.neo4j.driver.Record>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
records that RETRIEVE_NODES, RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
VECTOR_SIMILARITY_SEARCH retrieved, as the Records of the Neo4j driver, with
all their columns", "constantName": [...]
"CamelNeo4jVectorEmbeddingId": { "index": 10, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Vector Id for the embedding",
"constantName": "org.apache.camel.component.neo4j.Neo4jHeaders#VECTOR_ID" },
- "CamelNeo4jLabel": { "index": 11, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Label for the Node - used when inserting from
Embeddings", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#LABEL" }
+ "CamelNeo4jLabel": { "index": 11, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": true, "deprecationNote": "Not used: the producer does not read
this header. CREATE_VECTOR takes the label of the node from the label option,
and Embedding when the option is not set", "autowired": false, "secret": false,
"description": "Label for the Node - used when inserting from Embeddings.
Deprecation note: Not used: the p [...]
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "CREATE_NODE", "carriers": [ "map", "text" ] }, {
"option": "operation", "value": "RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"carriers": [ "text" ] }, { "option": "operation", "value":
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "carriers": [ "text" ] }, { "option":
"operation", "value": "CREATE_VECTOR", "carriers": [ "pojo", "text" ] }, {
"option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", [...]
- "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ {
"option": "operation", "value": "RETRIEVE_NODES", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value":
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", "carriers":
[...]
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "CREATE_NODE", "carriers": [ "map", "text" ] }, {
"option": "operation", "value": "RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"carriers": [ "text" ] }, { "option": "operation", "value":
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "carriers": [ "text" ] }, { "option":
"operation", "value": "CREATE_VECTOR", "carriers": [ "pojo", "text" ] }, {
"option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", [...]
+ "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ {
"option": "operation", "value": "RETRIEVE_NODES", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value":
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", "carriers":
[...]
},
"properties": {
"name": { "index": 0, "kind": "path", "displayName": "Name", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The database name" },
@@ -73,7 +73,7 @@
"maxResults": { "index": 6, "kind": "parameter", "displayName": "Max
Results", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": 3, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Maximum results for Vector Similarity search"
},
"minScore": { "index": 7, "kind": "parameter", "displayName": "Min Score",
"group": "producer", "label": "", "required": false, "type": "number",
"javaType": "double", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 0.0, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Minimum score for Vector Similarity search" },
"operation": { "index": 8, "kind": "parameter", "displayName":
"Operation", "group": "producer", "label": "", "required": false, "type":
"enum", "javaType": "org.apache.camel.component.neo4j.Neo4Operation", "enum": [
"CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "autowired": false, "sec [...]
- "query": { "index": 9, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Cypher Query" },
+ "query": { "index": 9, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": true, "deprecationNote": "Not
used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the message
body", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
[...]
"similarityFunction": { "index": 10, "kind": "parameter", "displayName":
"Similarity Function", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType":
"org.apache.camel.component.neo4j.Neo4jSimilarityFunction", "enum": [ "cosine",
"euclidean" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "cosine", "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": " [...]
"vectorIndexName": { "index": 11, "kind": "parameter", "displayName":
"Vector Index Name", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Vector Index Name" },
"lazyStartProducer": { "index": 12, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/neo4j-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/neo4j-component.adoc
index c89a73e13733..7ba9d57c13e0 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/neo4j-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/neo4j-component.adoc
@@ -51,6 +51,7 @@ include::partial$component-endpoint-headers.adoc[]
To create a node in a database named `test`, define the body as a string
containing the JSON body. Use the operation `CREATE_NODE`.
The URI endpoint should contain also specify the label and the alias.
+A message without a body fails with an `InvalidPayloadException`.
.Example:
@@ -227,6 +228,10 @@ To retrieve or update nodes with Cypher Query in a
database named `test`, set th
The operation is the same as `ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY`, except
that it returns a list of retrieved or updated nodes represented as
`Map<String, Object>`.
+When the first column that the query returns is a node, each `Map` holds the
properties of that node.
+Otherwise, such as for `MATCH (u:User) RETURN u.name AS name, u.age AS age` or
a `count(*)`, each `Map` holds the columns of one record by name, such as
`{name=Ethan, age=41}`.
+The `CamelNeo4jQueryResultListNeo4jNodes` header has the records of the Neo4j
driver, with all their columns.
+
.Example updating a node:
._Java-only: ProducerTemplate test API with string concatenation_
@@ -302,6 +307,12 @@ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Te
To perform a similarity search using vectors in a database named `test`, use
the operation `VECTOR_SIMILARITY_SEARCH`.
The URI endpoint should also specify the label, the alias and the vector index
name.
+The body is the vector to search for: a `Neo4jEmbedding`, a `float[]`, or a
`List` or an array of numbers, such as a `List<Double>`.
+Another body, such as text, or a message without a body, fails with an
`InvalidPayloadException`.
+
+The result is a List of `Map<String, Object>`: the properties of each node
found, and its similarity score as a `Double` under the `score` key (it
replaces a property of the node named `score`).
+The `CamelNeo4jQueryResultListNeo4jNodes` header has the records of the Neo4j
driver, with the `node` and `score` columns.
+
.Example:
._Java-only: ProducerTemplate test API with `List.of()` vector_
@@ -316,6 +327,9 @@ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Te
== Generate Embeddings with Langchain4j-embeddings
You can generate embeddings with an Embedding Models using the camel
Lancghain4j Embeddings components. Camel Neo4j introduces a DataType
`neo4j:embeddings` that automates the transformations of the Lancghain4j
embeddings to Neo4j vectors.
+The `label` option sets the label of the nodes that `CREATE_VECTOR` creates
(`Embedding` when it is not set).
+The deprecated `CamelNeo4jLabel` header is not used.
+
.Example of a camel Route that create embeddings with Camel Langchain4j
Embeddings, and ingest them into Neo4j database.
[tabs]
@@ -327,7 +341,6 @@ Java::
from("direct:in")
.to("langchain4j-embeddings:test")
.setHeader("CamelNeo4jOperation").constant(Neo4Operation.CREATE_VECTOR)
- .setHeader("CamelNeo4jLabel").constant("Test")
.transformDataType(new DataType("neo4j:embeddings"))
.to("neo4j:neo4j?vectorIndexName=myIndex&label=Test");
----
@@ -347,11 +360,6 @@ YAML::
expression:
constant:
expression: CREATE_VECTOR
- - setHeader:
- name: CamelNeo4jLabel
- expression:
- constant:
- expression: Test
- transformDataType:
toType: neo4j:embeddings
- to:
diff --git
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/org/apache/camel/component/neo4j/neo4j.json
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/org/apache/camel/component/neo4j/neo4j.json
index 6ce70e1b0fb3..d5646444b165 100644
---
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/org/apache/camel/component/neo4j/neo4j.json
+++
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/org/apache/camel/component/neo4j/neo4j.json
@@ -34,7 +34,7 @@
"maxResults": { "index": 7, "kind": "property", "displayName": "Max
Results", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": 3, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Maximum results for Vector Similarity search"
},
"minScore": { "index": 8, "kind": "property", "displayName": "Min Score",
"group": "producer", "label": "", "required": false, "type": "number",
"javaType": "double", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 0.0, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Minimum score for Vector Similarity search" },
"operation": { "index": 9, "kind": "property", "displayName": "Operation",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "org.apache.camel.component.neo4j.Neo4Operation", "enum": [
"CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "autowired": false, "secr [...]
- "query": { "index": 10, "kind": "property", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Cypher Query" },
+ "query": { "index": 10, "kind": "property", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": true, "deprecationNote": "Not
used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the message
body", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
[...]
"similarityFunction": { "index": 11, "kind": "property", "displayName":
"Similarity Function", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType":
"org.apache.camel.component.neo4j.Neo4jSimilarityFunction", "enum": [ "cosine",
"euclidean" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "cosine", "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "S [...]
"vectorIndexName": { "index": 12, "kind": "property", "displayName":
"Vector Index Name", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Vector Index Name" },
"autowiredEnabled": { "index": 13, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching [...]
@@ -47,21 +47,21 @@
},
"headers": {
"CamelNeo4jOperation": { "index": 0, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"enum": [ "CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The op [...]
- "CamelNeo4jMatchProperties": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "MATCH properties for the generated
MATCH query. Needed only if we are matching properties and values. Example:
{name: 'Alice'}", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#MATCH_PROPERTIES" },
- "CamelNeo4jQueryResult": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Query Result", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT" },
- "CamelNeo4jQueryResultNodesCreated": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes created",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_CREATED" },
- "CamelNeo4jQueryResultNodesDeleted": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes deleted",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_DELETED" },
+ "CamelNeo4jMatchProperties": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "MATCH properties for the generated
MATCH query of RETRIEVE_NODES and DELETE_NODE, as a JSON object of the property
names and values. Needed only if we are matching properties and values.",
"constantName": "org.apache.camel.component.neo [...]
+ "CamelNeo4jQueryResult": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The text of the Cypher query that the operation ran, set
by CREATE_NODE, DELETE_NODE, ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY,
CREATE_VECTOR_INDEX, DROP_VECTOR_INDEX and CREATE_VECTOR", "constantName":
"org.apache.camel.component.neo4j.Neo4jHe [...]
+ "CamelNeo4jQueryResultNodesCreated": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes created",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_CREATED" },
+ "CamelNeo4jQueryResultNodesDeleted": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query Number of nodes deleted",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_NODES_DELETED" },
"CamelNeo4jQueryResultContainsUpdates": { "index": 5, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query executed contains update",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_CONTAINS_UPDATES" },
- "CamelNeo4jQueryResultRelationshipsCreated": { "index": 6, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships created", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_CREATED"
},
- "CamelNeo4jQueryResultRelationshipsDeleted": { "index": 7, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Long", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships deleted", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_DELETED"
},
- "CamelNeo4jQueryResultRetrieveSize": { "index": 8, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Number of nodes retrieved",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_SIZE" },
- "CamelNeo4jQueryResultListNeo4jNodes": { "index": 9, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Query execution time in Milliseconds",
"constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_LIST_NEO4J_NODES"
},
+ "CamelNeo4jQueryResultRelationshipsCreated": { "index": 6, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Integer", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships created", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_CREATED"
},
+ "CamelNeo4jQueryResultRelationshipsDeleted": { "index": 7, "kind":
"header", "displayName": "", "group": "producer", "label": "", "required":
false, "javaType": "Integer", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Query executed number of
relationships deleted", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RESULT_RELATIONSHIPS_DELETED"
},
+ "CamelNeo4jQueryResultRetrieveSize": { "index": 8, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of records that
RETRIEVE_NODES, RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
VECTOR_SIMILARITY_SEARCH retrieved", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#QUERY_RETRIEVE_SIZE" },
+ "CamelNeo4jQueryResultListNeo4jNodes": { "index": 9, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "java.util.List<org.neo4j.driver.Record>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
records that RETRIEVE_NODES, RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
VECTOR_SIMILARITY_SEARCH retrieved, as the Records of the Neo4j driver, with
all their columns", "constantName": [...]
"CamelNeo4jVectorEmbeddingId": { "index": 10, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Vector Id for the embedding",
"constantName": "org.apache.camel.component.neo4j.Neo4jHeaders#VECTOR_ID" },
- "CamelNeo4jLabel": { "index": 11, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Label for the Node - used when inserting from
Embeddings", "constantName":
"org.apache.camel.component.neo4j.Neo4jHeaders#LABEL" }
+ "CamelNeo4jLabel": { "index": 11, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": true, "deprecationNote": "Not used: the producer does not read
this header. CREATE_VECTOR takes the label of the node from the label option,
and Embedding when the option is not set", "autowired": false, "secret": false,
"description": "Label for the Node - used when inserting from Embeddings.
Deprecation note: Not used: the p [...]
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "CREATE_NODE", "carriers": [ "map", "text" ] }, {
"option": "operation", "value": "RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"carriers": [ "text" ] }, { "option": "operation", "value":
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "carriers": [ "text" ] }, { "option":
"operation", "value": "CREATE_VECTOR", "carriers": [ "pojo", "text" ] }, {
"option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", [...]
- "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ {
"option": "operation", "value": "RETRIEVE_NODES", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value":
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", "carriers":
[...]
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "CREATE_NODE", "carriers": [ "map", "text" ] }, {
"option": "operation", "value": "RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"carriers": [ "text" ] }, { "option": "operation", "value":
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "carriers": [ "text" ] }, { "option":
"operation", "value": "CREATE_VECTOR", "carriers": [ "pojo", "text" ] }, {
"option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", [...]
+ "producerOut": { "carriers": [ "list", "unchanged" ], "byOption": [ {
"option": "operation", "value": "RETRIEVE_NODES", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value":
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY", "carriers": [ "list" ],
"javaType": "java.util.List<java.util.Map<java.lang.String, java.lang.Object>>"
}, { "option": "operation", "value": "VECTOR_SIMILARITY_SEARCH", "carriers":
[...]
},
"properties": {
"name": { "index": 0, "kind": "path", "displayName": "Name", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The database name" },
@@ -73,7 +73,7 @@
"maxResults": { "index": 6, "kind": "parameter", "displayName": "Max
Results", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "int", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": 3, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Maximum results for Vector Similarity search"
},
"minScore": { "index": 7, "kind": "parameter", "displayName": "Min Score",
"group": "producer", "label": "", "required": false, "type": "number",
"javaType": "double", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 0.0, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Minimum score for Vector Similarity search" },
"operation": { "index": 8, "kind": "parameter", "displayName":
"Operation", "group": "producer", "label": "", "required": false, "type":
"enum", "javaType": "org.apache.camel.component.neo4j.Neo4Operation", "enum": [
"CREATE_NODE", "DELETE_NODE", "RETRIEVE_NODES",
"RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY",
"ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY", "CREATE_VECTOR_INDEX",
"DROP_VECTOR_INDEX", "CREATE_VECTOR", "VECTOR_SIMILARITY_SEARCH" ],
"deprecated": false, "autowired": false, "sec [...]
- "query": { "index": 9, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Cypher Query" },
+ "query": { "index": 9, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": true, "deprecationNote": "Not
used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the message
body", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
[...]
"similarityFunction": { "index": 10, "kind": "parameter", "displayName":
"Similarity Function", "group": "producer", "label": "", "required": false,
"type": "enum", "javaType":
"org.apache.camel.component.neo4j.Neo4jSimilarityFunction", "enum": [ "cosine",
"euclidean" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "cosine", "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": " [...]
"vectorIndexName": { "index": 11, "kind": "parameter", "displayName":
"Vector Index Name", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.neo4j.Neo4jConfiguration", "configurationField":
"configuration", "description": "Vector Index Name" },
"lazyStartProducer": { "index": 12, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
diff --git a/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
b/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
index c89a73e13733..7ba9d57c13e0 100644
--- a/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
+++ b/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
@@ -51,6 +51,7 @@ include::partial$component-endpoint-headers.adoc[]
To create a node in a database named `test`, define the body as a string
containing the JSON body. Use the operation `CREATE_NODE`.
The URI endpoint should contain also specify the label and the alias.
+A message without a body fails with an `InvalidPayloadException`.
.Example:
@@ -227,6 +228,10 @@ To retrieve or update nodes with Cypher Query in a
database named `test`, set th
The operation is the same as `ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY`, except
that it returns a list of retrieved or updated nodes represented as
`Map<String, Object>`.
+When the first column that the query returns is a node, each `Map` holds the
properties of that node.
+Otherwise, such as for `MATCH (u:User) RETURN u.name AS name, u.age AS age` or
a `count(*)`, each `Map` holds the columns of one record by name, such as
`{name=Ethan, age=41}`.
+The `CamelNeo4jQueryResultListNeo4jNodes` header has the records of the Neo4j
driver, with all their columns.
+
.Example updating a node:
._Java-only: ProducerTemplate test API with string concatenation_
@@ -302,6 +307,12 @@ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Te
To perform a similarity search using vectors in a database named `test`, use
the operation `VECTOR_SIMILARITY_SEARCH`.
The URI endpoint should also specify the label, the alias and the vector index
name.
+The body is the vector to search for: a `Neo4jEmbedding`, a `float[]`, or a
`List` or an array of numbers, such as a `List<Double>`.
+Another body, such as text, or a message without a body, fails with an
`InvalidPayloadException`.
+
+The result is a List of `Map<String, Object>`: the properties of each node
found, and its similarity score as a `Double` under the `score` key (it
replaces a property of the node named `score`).
+The `CamelNeo4jQueryResultListNeo4jNodes` header has the records of the Neo4j
driver, with the `node` and `score` columns.
+
.Example:
._Java-only: ProducerTemplate test API with `List.of()` vector_
@@ -316,6 +327,9 @@ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Te
== Generate Embeddings with Langchain4j-embeddings
You can generate embeddings with an Embedding Models using the camel
Lancghain4j Embeddings components. Camel Neo4j introduces a DataType
`neo4j:embeddings` that automates the transformations of the Lancghain4j
embeddings to Neo4j vectors.
+The `label` option sets the label of the nodes that `CREATE_VECTOR` creates
(`Embedding` when it is not set).
+The deprecated `CamelNeo4jLabel` header is not used.
+
.Example of a camel Route that create embeddings with Camel Langchain4j
Embeddings, and ingest them into Neo4j database.
[tabs]
@@ -327,7 +341,6 @@ Java::
from("direct:in")
.to("langchain4j-embeddings:test")
.setHeader("CamelNeo4jOperation").constant(Neo4Operation.CREATE_VECTOR)
- .setHeader("CamelNeo4jLabel").constant("Test")
.transformDataType(new DataType("neo4j:embeddings"))
.to("neo4j:neo4j?vectorIndexName=myIndex&label=Test");
----
@@ -347,11 +360,6 @@ YAML::
expression:
constant:
expression: CREATE_VECTOR
- - setHeader:
- name: CamelNeo4jLabel
- expression:
- constant:
- expression: Test
- transformDataType:
toType: neo4j:embeddings
- to:
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jConfiguration.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jConfiguration.java
index 86ff462c1b0f..756e1ae852cd 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jConfiguration.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jConfiguration.java
@@ -50,6 +50,9 @@ public class Neo4jConfiguration implements Cloneable {
@UriParam(description = "The operation to perform. The CamelNeo4jOperation
header, when set, wins over this option.")
private Neo4Operation operation;
@UriParam
+ @Metadata(deprecationNote = "Not used:
RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and"
+ + " ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take
the Cypher query from the message body")
+ @Deprecated
private String query;
@UriParam
private String label;
@@ -248,11 +251,22 @@ public class Neo4jConfiguration implements Cloneable {
/**
* Cypher Query
+ *
+ * @deprecated not used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take
+ * the Cypher query from the message body.
*/
+ @Deprecated
public String getQuery() {
return query;
}
+ /**
+ * Cypher Query
+ *
+ * @deprecated not used: RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take
+ * the Cypher query from the message body.
+ */
+ @Deprecated
public void setQuery(String query) {
this.query = query;
}
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEndpoint.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEndpoint.java
index ef528c5baebb..40afe9c92726 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEndpoint.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEndpoint.java
@@ -74,7 +74,8 @@ import org.neo4j.driver.GraphDatabase;
+ " or no body fail); the two
Cypher operations the query as text (bytes or a stream are"
+ " read as text);
CREATE_VECTOR a Neo4jEmbedding, or the text with the float[] vector in"
+ " the
CamelLangChain4jEmbeddingsVector header; VECTOR_SIMILARITY_SEARCH a
Neo4jEmbedding,"
- + " a float[] or a List of
Float. The other operations do not use the body"),
+ + " a float[], or a List or an
array of numbers (text or no body fail). The other"
+ + " operations do not use the
body"),
producerOut = @BodyType(value = { Carrier.LIST, Carrier.UNCHANGED },
byOption = {
@BodyType.When(option =
"operation", value = "RETRIEVE_NODES",
@@ -100,10 +101,12 @@ import org.neo4j.driver.GraphDatabase;
@BodyType.When(option =
"operation", value = "CREATE_VECTOR",
carriers =
Carrier.UNCHANGED) },
description = "RETRIEVE_NODES,
RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and VECTOR_SIMILARITY_SEARCH"
- + " give a List of Maps, the
properties of each node found (empty when none; the first"
- + " column a Cypher query
returns must be a node), with the Records of the driver in the"
- + "
CamelNeo4jQueryResultListNeo4jNodes header. The other operations leave the body"
- + " unchanged, with the
counts in the CamelNeo4jQueryResult headers"))
+ + " give a List of Maps
(empty when none), the properties of each node found, with the"
+ + " Records of the driver in
the CamelNeo4jQueryResultListNeo4jNodes header. A record of a"
+ + " Cypher query whose first
column is not a node is a Map of its columns, and"
+ + " VECTOR_SIMILARITY_SEARCH
adds the score of each node under the score key. The other"
+ + " operations leave the body
unchanged, with the counts in the CamelNeo4jQueryResult"
+ + " headers"))
public class Neo4jEndpoint extends DefaultEndpoint implements
EndpointServiceLocation {
@Metadata(required = true)
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jHeaders.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jHeaders.java
index c6f6a58c360d..c043fdf3cbc4 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jHeaders.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jHeaders.java
@@ -22,28 +22,42 @@ public class Neo4jHeaders {
@Metadata(description = "The operation to be performed.", javaType =
"String",
enums =
"CREATE_NODE,DELETE_NODE,RETRIEVE_NODES,RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY,ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY,CREATE_VECTOR_INDEX,DROP_VECTOR_INDEX,CREATE_VECTOR,VECTOR_SIMILARITY_SEARCH")
public static final String OPERATION = "CamelNeo4jOperation";
- @Metadata(description = "MATCH properties for the generated MATCH query.
Needed only if we are matching properties and values. Example: {name: 'Alice'}
",
+ @Metadata(description = "MATCH properties for the generated MATCH query of
RETRIEVE_NODES and DELETE_NODE, as a JSON object"
+ + " of the property names and values. Needed only
if we are matching properties and values.",
javaType = "String")
public static final String MATCH_PROPERTIES = "CamelNeo4jMatchProperties";
- @Metadata(description = "Query Result", javaType = "String")
+ @Metadata(description = "The text of the Cypher query that the operation
ran, set by CREATE_NODE, DELETE_NODE,"
+ + " ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY,
CREATE_VECTOR_INDEX, DROP_VECTOR_INDEX and CREATE_VECTOR",
+ javaType = "String")
public static final String QUERY_RESULT = "CamelNeo4jQueryResult";
- @Metadata(description = "Query Number of nodes created", javaType = "Long")
+ @Metadata(description = "Query Number of nodes created", javaType =
"Integer")
public static final String QUERY_RESULT_NODES_CREATED =
"CamelNeo4jQueryResultNodesCreated";
- @Metadata(description = "Query Number of nodes deleted", javaType = "Long")
+ @Metadata(description = "Query Number of nodes deleted", javaType =
"Integer")
public static final String QUERY_RESULT_NODES_DELETED =
"CamelNeo4jQueryResultNodesDeleted";
@Metadata(description = "Query executed contains update", javaType =
"Boolean")
public static final String QUERY_RESULT_CONTAINS_UPDATES =
"CamelNeo4jQueryResultContainsUpdates";
- @Metadata(description = "Query executed number of relationships created",
javaType = "Long")
+ @Metadata(description = "Query executed number of relationships created",
javaType = "Integer")
public static final String QUERY_RESULT_RELATIONSHIPS_CREATED =
"CamelNeo4jQueryResultRelationshipsCreated";
- @Metadata(description = "Query executed number of relationships deleted",
javaType = "Long")
+ @Metadata(description = "Query executed number of relationships deleted",
javaType = "Integer")
public static final String QUERY_RESULT_RELATIONSHIPS_DELETED =
"CamelNeo4jQueryResultRelationshipsDeleted";
- @Metadata(description = "Number of nodes retrieved", javaType = "Long")
+ @Metadata(description = "The number of records that RETRIEVE_NODES,
RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or"
+ + " VECTOR_SIMILARITY_SEARCH retrieved",
+ javaType = "Integer")
public static final String QUERY_RETRIEVE_SIZE =
"CamelNeo4jQueryResultRetrieveSize";
- @Metadata(description = "Query execution time in Milliseconds", javaType =
"Long")
+ @Metadata(description = "The records that RETRIEVE_NODES,
RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or"
+ + " VECTOR_SIMILARITY_SEARCH retrieved, as the
Records of the Neo4j driver, with all their columns",
+ javaType = "java.util.List<org.neo4j.driver.Record>")
public static final String QUERY_RETRIEVE_LIST_NEO4J_NODES =
"CamelNeo4jQueryResultListNeo4jNodes";
@Metadata(description = "Vector Id for the embedding", javaType = "String")
public static final String VECTOR_ID = "CamelNeo4jVectorEmbeddingId";
- @Metadata(description = "Label for the Node - used when inserting from
Embeddings", javaType = "String")
+ /**
+ * @deprecated not used: CREATE_VECTOR takes the label of the node from
the label option, and Embedding when the
+ * option is not set.
+ */
+ @Deprecated(since = "4.24")
+ @Metadata(description = "Label for the Node - used when inserting from
Embeddings", javaType = "String",
+ deprecationNote = "Not used: the producer does not read this
header. CREATE_VECTOR takes the label of the"
+ + " node from the label option, and Embedding
when the option is not set")
public static final String LABEL = "CamelNeo4jLabel";
}
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
index ed210a8d3a96..d2d68ac85872 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
@@ -16,12 +16,15 @@
*/
package org.apache.camel.component.neo4j;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -38,6 +41,7 @@ import org.neo4j.driver.QueryConfig;
import org.neo4j.driver.Record;
import org.neo4j.driver.Values;
import org.neo4j.driver.summary.ResultSummary;
+import org.neo4j.driver.types.TypeSystem;
import static org.apache.camel.component.neo4j.Neo4Operation.RETRIEVE_NODES;
import static
org.apache.camel.component.neo4j.Neo4Operation.RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY;
@@ -58,6 +62,13 @@ public class Neo4jProducer extends DefaultProducer {
private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new
TypeReference<>() {
};
+ // the key of the score of a node in the result Maps of
VECTOR_SIMILARITY_SEARCH
+ static final String SCORE = "score";
+
+ private static final String CREATE_NODE_BODY = "CREATE_NODE needs the
properties of the node as a Map or as JSON text";
+ private static final String VECTOR_BODY
+ = "VECTOR_SIMILARITY_SEARCH needs the vector as a Neo4jEmbedding,
a float[], or a List or an array of numbers";
+
// Only property values are passed as bound parameters; the property name
is spliced into the
// Cypher query text, so it must be restricted to a safe identifier
pattern.
private static final Pattern VALID_PROPERTY_NAME =
Pattern.compile("^[A-Za-z_][A-Za-z0-9_]*$");
@@ -122,26 +133,32 @@ public class Neo4jProducer extends DefaultProducer {
// Always use parameterized queries to prevent Cypher injection
var query = String.format("CREATE (%s:%s $props)", alias, label);
- Map<String, Object> properties;
+ Object props;
if (body instanceof String bodyString) {
try {
// Convert JSON string to Map for parameterized query
- Map<String, Object> bodyMap =
OBJECT_MAPPER.readValue(bodyString, MAP_TYPE_REF);
- properties = Map.of("props", bodyMap);
+ props = OBJECT_MAPPER.readValue(bodyString, MAP_TYPE_REF);
} catch (Exception e) {
+ // the body is not in the message: it holds the properties of
the node, which can be sensitive
exchange.setException(
new Neo4jOperationException(
Neo4Operation.CREATE_NODE,
- new IllegalArgumentException("Failed to parse
body as JSON: " + body, e)));
+ new IllegalArgumentException("Failed to parse
the body of CREATE_NODE as a JSON object", e)));
return;
}
+ if (props == null) {
+ throw new InvalidPayloadException(
+ exchange, Map.class, exchange.getMessage(),
CREATE_NODE_BODY + ", and the JSON text is null");
+ }
+ } else if (body == null) {
+ throw new InvalidPayloadException(exchange, Map.class,
exchange.getMessage(), CREATE_NODE_BODY);
} else {
// body should be a Map or similar object
- properties = Map.of("props", body);
+ props = body;
}
- executeWriteQuery(exchange, query, properties, databaseName,
Neo4Operation.CREATE_NODE);
+ executeWriteQuery(exchange, query, Map.of("props", props),
databaseName, Neo4Operation.CREATE_NODE);
}
/**
@@ -244,17 +261,41 @@ public class Neo4jProducer extends DefaultProducer {
var records = result.records();
exchange.getMessage().setHeader(QUERY_RETRIEVE_SIZE,
records.size());
exchange.getMessage().setHeader(QUERY_RETRIEVE_LIST_NEO4J_NODES,
records);
- exchange.getMessage().setBody(nodeProperties(records));
+ exchange.getMessage().setBody(
+ operation == VECTOR_SIMILARITY_SEARCH ?
similarityHits(records) : nodeProperties(records));
} catch (Exception error) {
exchange.setException(new Neo4jOperationException(operation,
error));
}
}
- private List nodeProperties(List<Record> result) {
- return result.stream()
- .map(record -> record.get(0).asNode())
- .map(node -> node.asMap())
- .collect(Collectors.toList());
+ /**
+ * The properties of the node of each record, when the first column is a
node. A record whose first column is not a
+ * node, such as a property, a count or a path that a Cypher query
returns, is given as a Map of its columns.
+ */
+ private static List<Map<String, Object>> nodeProperties(List<Record>
records) {
+ List<Map<String, Object>> answer = new ArrayList<>(records.size());
+ for (Record record : records) {
+ if (record.size() > 0 &&
record.get(0).hasType(TypeSystem.getDefault().NODE())) {
+ answer.add(record.get(0).asNode().asMap());
+ } else {
+ answer.add(record.asMap());
+ }
+ }
+ return answer;
+ }
+
+ /**
+ * The properties of the node of each hit of a similarity search, with the
score of the hit under the score key (it
+ * replaces a property of the node with that name).
+ */
+ private static List<Map<String, Object>> similarityHits(List<Record>
records) {
+ List<Map<String, Object>> answer = new ArrayList<>(records.size());
+ for (Record record : records) {
+ Map<String, Object> hit = new
LinkedHashMap<>(record.get("node").asNode().asMap());
+ hit.put(SCORE, record.get(SCORE).asDouble());
+ answer.add(hit);
+ }
+ return answer;
}
private void deleteNode(Exchange exchange) throws NoSuchHeaderException {
@@ -412,14 +453,7 @@ public class Neo4jProducer extends DefaultProducer {
final String vectorIndexName =
getEndpoint().getConfiguration().getVectorIndexName();
ObjectHelper.notNull(vectorIndexName, "vectorIndexName");
- float[] vectors;
-
- Object body = exchange.getMessage().getMandatoryBody();
- if (body instanceof Neo4jEmbedding neo4jEmbed) {
- vectors = neo4jEmbed.getVectors();
- } else {
- vectors = exchange.getMessage().getBody(float[].class);
- }
+ final float[] vectors = vectorOf(exchange);
final double minScore = getEndpoint().getConfiguration().getMinScore();
@@ -443,6 +477,49 @@ public class Neo4jProducer extends DefaultProducer {
}
+ /**
+ * The vector of VECTOR_SIMILARITY_SEARCH: a Neo4jEmbedding, a float[], or
a List (another Collection) or an array
+ * of numbers, such as a List of Double. Another body is converted to a
float[] by the type converters, and fails
+ * when it does not convert, such as text.
+ */
+ private static float[] vectorOf(Exchange exchange) throws
InvalidPayloadException {
+ final Message message = exchange.getMessage();
+ final Object body = message.getMandatoryBody();
+ float[] vector;
+ if (body instanceof Neo4jEmbedding neo4jEmbedding) {
+ vector = neo4jEmbedding.getVectors();
+ } else if (body instanceof float[] floats) {
+ vector = floats;
+ } else if (body instanceof Collection<?> collection) {
+ vector = toFloats(exchange, collection.toArray());
+ } else if (body.getClass().isArray()) {
+ vector = toFloats(exchange, body);
+ } else {
+ vector = message.getBody(float[].class);
+ }
+ if (vector == null) {
+ throw new InvalidPayloadException(exchange, float[].class,
message, VECTOR_BODY);
+ }
+ return vector;
+ }
+
+ private static float[] toFloats(Exchange exchange, Object array) throws
InvalidPayloadException {
+ final int length = Array.getLength(array);
+ final float[] vector = new float[length];
+ for (int i = 0; i < length; i++) {
+ Object element = Array.get(array, i);
+ if (!(element instanceof Number number)) {
+ // the type of the element only: the values of the body are
not put in the message
+ String type = element == null ? "null" :
element.getClass().getName();
+ throw new InvalidPayloadException(
+ exchange, float[].class, exchange.getMessage(),
+ VECTOR_BODY + ", but the element at index " + i + " is
" + type);
+ }
+ vector[i] = number.floatValue();
+ }
+ return vector;
+ }
+
private void writeWithCypherQuery(Exchange exchange) {
final String query = exchange.getMessage().getBody(String.class);
final String databaseName = getEndpoint().getName();
diff --git
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jProducerBodyTest.java
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jProducerBodyTest.java
new file mode 100644
index 000000000000..1f993a9715bd
--- /dev/null
+++
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jProducerBodyTest.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.neo4j;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiFunction;
+import java.util.stream.Stream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.neo4j.driver.Driver;
+import org.neo4j.driver.EagerResult;
+import org.neo4j.driver.ExecutableQuery;
+import org.neo4j.driver.Query;
+import org.neo4j.driver.Record;
+import org.neo4j.driver.Value;
+import org.neo4j.driver.Values;
+import org.neo4j.driver.internal.InternalNode;
+import org.neo4j.driver.internal.InternalRecord;
+import org.neo4j.driver.summary.ResultSummary;
+import org.neo4j.driver.summary.SummaryCounters;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The bodies the producer takes and gives, against a fake driver that records
the queries and their parameters and
+ * returns the records the test sets: no Neo4j server is needed.
+ */
+class Neo4jProducerBodyTest extends CamelTestSupport {
+
+ private static final String URI = "neo4j:test?driver=#fakeDriver";
+ private static final String CREATE_NODE = URI +
"&operation=CREATE_NODE&alias=u&label=User";
+ private static final String SEARCH = URI +
"&operation=VECTOR_SIMILARITY_SEARCH&vectorIndexName=idx";
+ private static final String CYPHER = URI +
"&operation=RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY";
+
+ // the queries the producer ran, and their parameters
+ private final List<String> queries = new CopyOnWriteArrayList<>();
+ private final List<Map<String, Object>> parameters = new
CopyOnWriteArrayList<>();
+ // the records that the queries return
+ private volatile List<Record> records = List.of();
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+ context.getRegistry().bind("fakeDriver", fakeDriver());
+ return context;
+ }
+
+ @Test
+ void createNodeWithoutBodyFails() {
+ Exchange result =
fluentTemplate.to(CREATE_NODE).request(Exchange.class);
+
+ InvalidPayloadException e =
assertInstanceOf(InvalidPayloadException.class, result.getException());
+ assertTrue(e.getMessage().contains("CREATE_NODE needs the properties
of the node as a Map or as JSON text"),
+ e.getMessage());
+ assertEquals(List.of(), queries, "nothing must be sent to Neo4j");
+ }
+
+ @Test
+ void createNodeWithTheJsonNullFails() {
+ Exchange result =
fluentTemplate.to(CREATE_NODE).withBody("null").request(Exchange.class);
+
+ InvalidPayloadException e =
assertInstanceOf(InvalidPayloadException.class, result.getException());
+ assertTrue(e.getMessage().contains("CREATE_NODE needs the properties
of the node as a Map or as JSON text"),
+ e.getMessage());
+ assertEquals(List.of(), queries, "nothing must be sent to Neo4j");
+ }
+
+ @Test
+ void createNodeWithAMapOrJsonText() {
+ Map<String, Object> properties = Map.of("name", "Bob", "age", 25);
+ Exchange result =
fluentTemplate.to(CREATE_NODE).withBody(properties).request(Exchange.class);
+ assertNull(result.getException());
+
+ result = fluentTemplate.to(CREATE_NODE).withBody("{\"name\": \"Bob\",
\"age\": 25}").request(Exchange.class);
+ assertNull(result.getException());
+
+ assertEquals(List.of("CREATE (u:User $props)", "CREATE (u:User
$props)"), queries);
+ assertEquals(List.of(Map.of("props", properties), Map.of("props",
properties)), parameters);
+ }
+
+ @Test
+ void createNodeWithTextThatIsNotJsonDoesNotPutTheBodyInTheMessage() {
+ Exchange result =
fluentTemplate.to(CREATE_NODE).withBody("{\"password\":
\"s3cret\"").request(Exchange.class);
+
+ Neo4jOperationException e =
assertInstanceOf(Neo4jOperationException.class, result.getException());
+ assertInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertFalse(e.getCause().getMessage().contains("s3cret"),
e.getCause().getMessage());
+ assertEquals(List.of(), queries, "nothing must be sent to Neo4j");
+ }
+
+ static Stream<Arguments> vectors() {
+ return Stream.of(
+ Arguments.of(List.of(0.75d, 0.65d)),
+ Arguments.of(List.of(0.75f, 0.65f)),
+ Arguments.of(new double[] { 0.75d, 0.65d }),
+ Arguments.of((Object) new Double[] { 0.75d, 0.65d }),
+ Arguments.of(new float[] { 0.75f, 0.65f }),
+ Arguments.of(new Neo4jEmbedding("1", "hello", new float[] {
0.75f, 0.65f })));
+ }
+
+ @ParameterizedTest
+ @MethodSource("vectors")
+ void similaritySearchTakesAVectorOfNumbers(Object body) {
+ Exchange result =
fluentTemplate.to(SEARCH).withBody(body).request(Exchange.class);
+
+ assertNull(result.getException(), () -> "a vector as a " +
body.getClass().getName() + " must be searched");
+ assertArrayEquals(new float[] { 0.75f, 0.65f }, (float[])
parameters.get(0).get("embeddingValue"));
+ }
+
+ @Test
+ void similaritySearchWithTextFails() {
+ Exchange result =
fluentTemplate.to(SEARCH).withBody("0.75,0.65").request(Exchange.class);
+
+ InvalidPayloadException e =
assertInstanceOf(InvalidPayloadException.class, result.getException());
+ assertTrue(e.getMessage().contains("VECTOR_SIMILARITY_SEARCH needs the
vector as a Neo4jEmbedding, a float[], or a"
+ + " List or an array of numbers"),
+ e.getMessage());
+ assertEquals(List.of(), queries, "nothing must be sent to Neo4j");
+ }
+
+ @Test
+ void similaritySearchWithAListThatIsNotOfNumbersFails() {
+ Exchange result = fluentTemplate.to(SEARCH).withBody(List.of("0.75",
"0.65")).request(Exchange.class);
+
+ InvalidPayloadException e =
assertInstanceOf(InvalidPayloadException.class, result.getException());
+ assertTrue(e.getMessage().contains("the element at index 0 is
java.lang.String"), e.getMessage());
+ assertFalse(e.getMessage().contains("0.75"), "the values of the body
must not be in the message");
+ assertEquals(List.of(), queries, "nothing must be sent to Neo4j");
+ }
+
+ @Test
+ void similaritySearchGivesTheScoreOfEachNode() {
+ records = List.of(
+ record(List.of("node", "score"), node(Map.of("id", "1",
"text", "hello")), Values.value(0.9d)),
+ record(List.of("node", "score"), node(Map.of("id", "2",
"text", "world")), Values.value(0.8d)));
+
+ Exchange result = fluentTemplate.to(SEARCH).withBody(List.of(0.75d,
0.65d)).request(Exchange.class);
+
+ assertNull(result.getException());
+ assertEquals(List.of(
+ Map.of("id", "1", "text", "hello", "score", 0.9d),
+ Map.of("id", "2", "text", "world", "score", 0.8d)),
+ result.getMessage().getBody(List.class));
+ assertEquals(2,
result.getMessage().getHeader(Neo4jHeaders.QUERY_RETRIEVE_SIZE));
+ assertEquals(records,
result.getMessage().getHeader(Neo4jHeaders.QUERY_RETRIEVE_LIST_NEO4J_NODES));
+ }
+
+ @Test
+ void cypherQueryWhoseFirstColumnIsANodeGivesItsProperties() {
+ records = List.of(record(List.of("u"), node(Map.of("name", "Ethan",
"age", 41))));
+
+ Exchange result = fluentTemplate.to(CYPHER).withBody("MATCH (u:User
{name: 'Ethan'}) SET u.age = 41 RETURN u")
+ .request(Exchange.class);
+
+ assertNull(result.getException());
+ assertEquals(List.of(Map.of("name", "Ethan", "age", 41L)),
result.getMessage().getBody(List.class));
+ }
+
+ @Test
+ void cypherQueryWhoseFirstColumnIsNotANodeGivesTheRecords() {
+ records = List.of(
+ record(List.of("name", "age"), Values.value("Ethan"),
Values.value(41)),
+ record(List.of("name", "age"), Values.value("Diana"),
Values.value(30)));
+
+ Exchange result = fluentTemplate.to(CYPHER).withBody("MATCH (u:User)
RETURN u.name AS name, u.age AS age")
+ .request(Exchange.class);
+
+ assertNull(result.getException());
+ assertEquals(List.of(Map.of("name", "Ethan", "age", 41L),
Map.of("name", "Diana", "age", 30L)),
+ result.getMessage().getBody(List.class));
+ }
+
+ @Test
+ void cypherQueryThatCountsGivesTheCount() {
+ records = List.of(record(List.of("users"), Values.value(5)));
+
+ Exchange result = fluentTemplate.to(CYPHER).withBody("MATCH (u:User)
RETURN count(u) AS users")
+ .request(Exchange.class);
+
+ assertNull(result.getException());
+ assertEquals(List.of(Map.of("users", 5L)),
result.getMessage().getBody(List.class));
+ }
+
+ @Test
+ void theHeadersHaveTheJavaTypeTheirMetadataSays() throws Exception {
+ records = List.of(record(List.of("u"), node(Map.of("name", "Ethan"))));
+ Exchange retrieved = fluentTemplate.to(CYPHER).withBody("MATCH
(u:User) RETURN u").request(Exchange.class);
+ Exchange created =
fluentTemplate.to(CREATE_NODE).withBody(Map.of("name",
"Bob")).request(Exchange.class);
+
+ assertInstanceOf(Integer.class,
retrieved.getMessage().getHeader(Neo4jHeaders.QUERY_RETRIEVE_SIZE));
+ assertEquals("Integer", metadataOf("QUERY_RETRIEVE_SIZE").javaType());
+ assertEquals(records,
retrieved.getMessage().getHeader(Neo4jHeaders.QUERY_RETRIEVE_LIST_NEO4J_NODES));
+ assertEquals("java.util.List<org.neo4j.driver.Record>",
metadataOf("QUERY_RETRIEVE_LIST_NEO4J_NODES").javaType());
+
assertTrue(metadataOf("QUERY_RETRIEVE_LIST_NEO4J_NODES").description().contains("Records
of the Neo4j driver"));
+
+ for (String counter : List.of("QUERY_RESULT_NODES_CREATED",
"QUERY_RESULT_NODES_DELETED",
+ "QUERY_RESULT_RELATIONSHIPS_CREATED",
"QUERY_RESULT_RELATIONSHIPS_DELETED")) {
+ String header = (String)
Neo4jHeaders.class.getField(counter).get(null);
+ assertInstanceOf(Integer.class,
created.getMessage().getHeader(header), counter);
+ assertEquals("Integer", metadataOf(counter).javaType(), counter);
+ }
+ }
+
+ @Test
+ void theUnusedLabelHeaderAndQueryOptionAreDeprecated() throws Exception {
+ Field label = Neo4jHeaders.class.getField("LABEL");
+ assertNotNull(label.getAnnotation(Deprecated.class));
+
assertFalse(label.getAnnotation(Metadata.class).deprecationNote().isEmpty());
+
+ Field query = Neo4jConfiguration.class.getDeclaredField("query");
+ assertNotNull(query.getAnnotation(Deprecated.class));
+
assertFalse(query.getAnnotation(Metadata.class).deprecationNote().isEmpty());
+ }
+
+ private static Metadata metadataOf(String constant) throws
NoSuchFieldException {
+ return
Neo4jHeaders.class.getField(constant).getAnnotation(Metadata.class);
+ }
+
+ private static Record record(List<String> keys, Value... values) {
+ return new InternalRecord(keys, List.of(values));
+ }
+
+ private static Value node(Map<String, Object> properties) {
+ Map<String, Value> values = new LinkedHashMap<>();
+ properties.forEach((key, value) -> values.put(key,
Values.value(value)));
+ return new InternalNode(1, List.of("User"), values).asValue();
+ }
+
+ private Driver fakeDriver() {
+ return proxy(Driver.class, (method, args) -> {
+ if ("executableQuery".equals(method.getName())) {
+ return executableQuery((String) args[0]);
+ }
+ if ("close".equals(method.getName())) {
+ return null;
+ }
+ throw new UnsupportedOperationException(method.getName());
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private ExecutableQuery executableQuery(String query) {
+ AtomicReference<ExecutableQuery> self = new AtomicReference<>();
+ self.set(proxy(ExecutableQuery.class, (method, args) -> {
+ switch (method.getName()) {
+ case "withConfig":
+ return self.get();
+ case "withParameters":
+ parameters.add((Map<String, Object>) args[0]);
+ return self.get();
+ case "execute":
+ if (args == null || args.length == 0) {
+ queries.add(query);
+ return result(query, records);
+ }
+ throw new UnsupportedOperationException("execute with a
collector");
+ default:
+ throw new UnsupportedOperationException(method.getName());
+ }
+ }));
+ return self.get();
+ }
+
+ private static EagerResult result(String query, List<Record> records) {
+ // the counters of a query that created one node
+ SummaryCounters counters = proxy(SummaryCounters.class, (method, args)
-> {
+ if (method.getReturnType() == boolean.class) {
+ return true;
+ }
+ return "nodesCreated".equals(method.getName()) ? 1 : 0;
+ });
+ ResultSummary summary = proxy(ResultSummary.class, (method, args) ->
switch (method.getName()) {
+ case "query" -> new Query(query);
+ case "counters" -> counters;
+ default -> throw new
UnsupportedOperationException(method.getName());
+ });
+ return new EagerResult() {
+ @Override
+ public List<String> keys() {
+ return records.isEmpty() ? List.of() : records.get(0).keys();
+ }
+
+ @Override
+ public List<Record> records() {
+ return records;
+ }
+
+ @Override
+ public ResultSummary summary() {
+ return summary;
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T proxy(Class<T> type, BiFunction<Method, Object[],
Object> handler) {
+ return (T) Proxy.newProxyInstance(type.getClassLoader(), new
Class<?>[] { type }, (proxy, method, args) -> {
+ if (method.getDeclaringClass() == Object.class) {
+ return switch (method.getName()) {
+ case "equals" -> proxy == args[0];
+ case "hashCode" -> System.identityHashCode(proxy);
+ default -> type.getSimpleName() + " (test)";
+ };
+ }
+ return handler.apply(method, args);
+ });
+ }
+}
diff --git
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
index b23502291eb6..bffb493fa610 100644
---
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
+++
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
@@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
import org.apache.camel.Message;
import org.apache.camel.component.neo4j.Neo4Operation;
import org.apache.camel.component.neo4j.Neo4jHeaders;
@@ -358,4 +359,29 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
}
+ @Test
+ @Order(10)
+ void testRetrieveWithCypherQueryReturningProperties() {
+ var cypherQuery = "MATCH (u:User {name: 'Ethan'}) RETURN u.name AS
name, u.age AS age";
+
+ Exchange result = fluentTemplate.to("neo4j:neo4j")
+ .withBodyAs(cypherQuery, String.class)
+ .withHeader(Neo4jHeaders.OPERATION,
Neo4Operation.RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY)
+ .request(Exchange.class);
+
+ Assertions.assertNull(result.getException());
+ Assertions.assertEquals(List.of(Map.of("name", "Ethan", "age", 41L)),
result.getMessage().getBody(List.class),
+ "The first column is not a node: each record is given as a Map
of its columns");
+ }
+
+ @Test
+ @Order(11)
+ void testCreateNodeWithoutBody() {
+ Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u6&label=User")
+ .withHeader(Neo4jHeaders.OPERATION, Neo4Operation.CREATE_NODE)
+ .request(Exchange.class);
+
+ Assertions.assertInstanceOf(InvalidPayloadException.class,
result.getException());
+ }
+
}
diff --git
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
index 7c98894000aa..064fd4347e48 100644
---
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
+++
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.neo4j.it;
import java.util.List;
+import java.util.Map;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
@@ -117,11 +118,26 @@ public class Neo4jVectorEmbeddingsIT extends
Neo4jTestSupport {
Assertions.assertNotNull(resultList);
Assertions.assertEquals(3, resultList.size());
+ for (Object hit : resultList) {
+ Assertions.assertInstanceOf(Double.class, ((Map<?, ?>)
hit).get("score"), "each node has its score");
+ }
}
@Test
@Order(4)
+ void similaritySearchWithAListOfDouble() {
+ Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx&label=Movie&alias=m")
+ .withHeader(Neo4jHeaders.OPERATION,
Neo4Operation.VECTOR_SIMILARITY_SEARCH)
+ .withBody(List.of(0.75d, 0.65d))
+ .request(Exchange.class);
+
+ Assertions.assertNull(result.getException());
+ Assertions.assertEquals(3,
result.getMessage().getHeader(Neo4jHeaders.QUERY_RETRIEVE_SIZE));
+ }
+
+ @Test
+ @Order(5)
void dropVectorIndex() {
Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx")
.withHeader(Neo4jHeaders.OPERATION,
Neo4Operation.DROP_VECTOR_INDEX)
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index eead8427aa02..b75ab007c613 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -4625,6 +4625,27 @@ has an entry with a wildcard such as `https://*` or an
entry `null`, the origin
were allowed for every origin. A cross-origin client that sends cookies or
other credentials must have its origin
listed in `allowedOrigins`.
+=== camel-neo4j - the similarity search gives the score, and the
CamelNeo4jLabel header and query option are deprecated
+
+`VECTOR_SIMILARITY_SEARCH` now adds the similarity score of each node to its
`Map` in the body, as a `Double` under the
+`score` key. It replaces a property of the node named `score`; the records of
the Neo4j driver in the
+`CamelNeo4jQueryResultListNeo4jNodes` header keep both. The body of the search
can now also be a `List` or an array of
+any numbers, such as a `List<Double>`. A body that is not a vector, such as
text, now fails with an
+`InvalidPayloadException` instead of a `NullPointerException` or a type
conversion error.
+
+`RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY` with a query whose first column
is not a node, such as a property or a
+count, now gives each record as a `Map` of its columns. Before, it failed with
`Cannot coerce ... to Node`.
+
+`CREATE_NODE` without a body now fails with an `InvalidPayloadException`
instead of a `NullPointerException`.
+
+The `CamelNeo4jLabel` header and the `query` option are deprecated: the
component never used them. `CREATE_VECTOR` takes
+the label of the node from the `label` option (`Embedding` when it is not
set), and the Cypher operations take the query
+from the message body.
+
+The catalog now declares the `CamelNeo4jQueryResultRetrieveSize` header and
the counters of the write operations (such
+as `CamelNeo4jQueryResultNodesCreated`) as `Integer`, the type the producer
always set, and
+`CamelNeo4jQueryResultListNeo4jNodes` as a `List` of the `Record` of the Neo4j
driver.
+
=== camel-tahu - a Device producer uses the client of its started Edge Node
producer
A Device producer (`tahu-edge:group/edgeNode/device`) no longer creates the
client of its Edge Node: only the Edge
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Neo4jComponentBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Neo4jComponentBuilderFactory.java
index 3a1616b5cc89..92b88a57b62f 100644
---
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Neo4jComponentBuilderFactory.java
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/Neo4jComponentBuilderFactory.java
@@ -219,7 +219,10 @@ public interface Neo4jComponentBuilderFactory {
}
/**
- * Cypher Query.
+ * Cypher Query. Deprecation note: Not used:
+ * RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
+ * ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the
+ * message body.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -228,6 +231,7 @@ public interface Neo4jComponentBuilderFactory {
* @param query the value to set
* @return the dsl builder
*/
+ @Deprecated
default Neo4jComponentBuilder query(java.lang.String query) {
doSetProperty("query", query);
return this;
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/Neo4jEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/Neo4jEndpointBuilderFactory.java
index 364aa00357f3..8cfaaf3a8954 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/Neo4jEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/Neo4jEndpointBuilderFactory.java
@@ -240,7 +240,10 @@ public interface Neo4jEndpointBuilderFactory {
return this;
}
/**
- * Cypher Query.
+ * Cypher Query. Deprecation note: Not used:
+ * RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY and
+ * ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY take the Cypher query from the
+ * message body.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -249,6 +252,7 @@ public interface Neo4jEndpointBuilderFactory {
* @param query the value to set
* @return the dsl builder
*/
+ @Deprecated
default Neo4jEndpointBuilder query(String query) {
doSetProperty("query", query);
return this;
@@ -539,8 +543,9 @@ public interface Neo4jEndpointBuilderFactory {
return "CamelNeo4jOperation";
}
/**
- * MATCH properties for the generated MATCH query. Needed only if we
are
- * matching properties and values. Example: {name: 'Alice'}.
+ * MATCH properties for the generated MATCH query of RETRIEVE_NODES and
+ * DELETE_NODE, as a JSON object of the property names and values.
+ * Needed only if we are matching properties and values.
*
* The option is a: {@code String} type.
*
@@ -552,7 +557,9 @@ public interface Neo4jEndpointBuilderFactory {
return "CamelNeo4jMatchProperties";
}
/**
- * Query Result.
+ * The text of the Cypher query that the operation ran, set by
+ * CREATE_NODE, DELETE_NODE, ADD_OR_DELETE_NODE_WITH_CYPHER_QUERY,
+ * CREATE_VECTOR_INDEX, DROP_VECTOR_INDEX and CREATE_VECTOR.
*
* The option is a: {@code String} type.
*
@@ -566,7 +573,7 @@ public interface Neo4jEndpointBuilderFactory {
/**
* Query Number of nodes created.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code Integer} type.
*
* Group: producer
*
@@ -578,7 +585,7 @@ public interface Neo4jEndpointBuilderFactory {
/**
* Query Number of nodes deleted.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code Integer} type.
*
* Group: producer
*
@@ -603,7 +610,7 @@ public interface Neo4jEndpointBuilderFactory {
/**
* Query executed number of relationships created.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code Integer} type.
*
* Group: producer
*
@@ -616,7 +623,7 @@ public interface Neo4jEndpointBuilderFactory {
/**
* Query executed number of relationships deleted.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code Integer} type.
*
* Group: producer
*
@@ -627,9 +634,11 @@ public interface Neo4jEndpointBuilderFactory {
return "CamelNeo4jQueryResultRelationshipsDeleted";
}
/**
- * Number of nodes retrieved.
+ * The number of records that RETRIEVE_NODES,
+ * RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
+ * VECTOR_SIMILARITY_SEARCH retrieved.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code Integer} type.
*
* Group: producer
*
@@ -639,9 +648,13 @@ public interface Neo4jEndpointBuilderFactory {
return "CamelNeo4jQueryResultRetrieveSize";
}
/**
- * Query execution time in Milliseconds.
+ * The records that RETRIEVE_NODES,
+ * RETRIEVE_NODES_AND_UPDATE_WITH_CYPHER_QUERY or
+ * VECTOR_SIMILARITY_SEARCH retrieved, as the Records of the Neo4j
+ * driver, with all their columns.
*
- * The option is a: {@code Long} type.
+ * The option is a: {@code java.util.List<org.neo4j.driver.Record>}
+ * type.
*
* Group: producer
*
@@ -664,7 +677,10 @@ public interface Neo4jEndpointBuilderFactory {
return "CamelNeo4jVectorEmbeddingId";
}
/**
- * Label for the Node - used when inserting from Embeddings.
+ * Label for the Node - used when inserting from Embeddings.
Deprecation
+ * note: Not used: the producer does not read this header.
CREATE_VECTOR
+ * takes the label of the node from the label option, and Embedding
when
+ * the option is not set.
*
* The option is a: {@code String} type.
*
@@ -672,6 +688,7 @@ public interface Neo4jEndpointBuilderFactory {
*
* @return the name of the header {@code Neo4jLabel}.
*/
+ @Deprecated
public String neo4jLabel() {
return "CamelNeo4jLabel";
}