2014-06-17 7 views
5

Ho problemi con i parametri in cypher in Neo4J da Java. Eseguo il database incorporato.In Neo4J, come impostare l'etichetta come parametro in una query cifrata da Java?

Il codice dovrebbe essere simile a questo (GraphDB.cypher va direttamente al ExecutionEngine)

HashMap<String, Object> parameter = new HashMap<>(); 
parameter.put("theLabel1", "Group"); 
parameter.put("theRelation", "isMemberOf"); 
parameter.put("theLabel2", "Person"); 
GraphDB.cypher("MATCH (n1:{theLabel1})-[r:{theRelation}]->(n2:{theLabel2}) RETURN n1, r, n2", parameter); 

ma finisce in questa eccezione

Exception in thread "main" Invalid input '{': expected whitespace or a label name (line 1, column 11) 
"MATCH (n1:{theLabel1})-[r:{theRelation}]->(n2:{theLabel2}) RETURN n1, r, n2" 

La documentazione (e tutorial) dice di utilizzare il {} per coprire i parametri, MA questo viene anche usato come notazione di cypher json per le proprietà. @see http://docs.neo4j.org/chunked/milestone/tutorials-cypher-parameters-java.html

C'è un altro modo per risolvere questo problema piuttosto che costruire la stringa di query come questo (o con altri metodi di template)

GraphDB.cypher("MATCH (n:" + labelName + ")-[r:" + relationName + "]->... 

Ciò è necessario perché l'etichetta di destinazione può cambiare e voglio riutilizzare il codice completamente.

Grazie in anticipo.

[[Modificato dopo aver ottenuto un (sigh) NO COME RISPOSTA]]

Dal momento che questa forma di parametro è attualmente (2.014,6) non supportato, mi verrà eseguito un po 'di sostituto del diritto prima di inviare la query.

HashMap<String, Object> parameter = new HashMap<>(); 
parameter.put("theLabel1", "Group"); 
parameter.put("theRelation", "isMemberOf"); 
parameter.put("theLabel2", "Person"); 

parameter.put("aName", "Donald Duck"); 

GraphDB.cypher("MATCH (n1:#theLabel1#)-[r:#theRelation#]->(n2:#theLabel2#) WHERE n2.Name = {aName} RETURN n1, r, n2", parameter); 

... with ... 

public static ExecutionResult cypher(String query, Map<String, Object> params) { 
    for (String key : params.keySet()) { 
     query = query.replaceAll("#" + key + "#", String.valueOf(params.get(key))); 
    } 
    return params == null ? cypherEngine.execute(query) : cypherEngine.execute(query, params); 
} 

Non ci può essere un altro readble

+0

Beh, sì, è possibile definire in modo statico modelli di query, ma questo è un pò fuori portata Neo4j per fornire questo tipo di caratteristiche, non lo fanno tu pensi? – Rolf

+0

@Raxa, la tua soluzione con "replacer" è molto interessante. A prima vista, l'uso di "# theLabel1 #" sembra statico, quindi non ha risolto il vero problema di specificare Cypher 'label' con' parameter' "dynamically". Ma quando viene considerato semplicemente un segnaposto che deve essere sostituito dall'incontro in 'parametro', ottiene il passaggio dell'etichetta in modo dinamico.Non sono sicuro se ci sia qualche soluzione standard in arrivo, ma è una mitigazione piuttosto interessante. – Causality

risposta

5

temo che questo non è supportato in questo momento.

E potrebbe per la stessa ragione di quello spiegato in questo numero: https://github.com/neo4j/neo4j/pull/1542.

L'idea alla base delle query parametrizzate consiste nel riutilizzare i piani di esecuzione (cache). Se un'etichetta di nodo o un tipo di relazione varia, il piano di esecuzione non sarebbe affatto lo stesso, rovinando così l'utilità della memorizzazione nella cache del piano di esecuzione.

+1

È un peccato che la documentazione di Neo4J ci venda l'idea in 7.5 che "Ciò significa che gli sviluppatori non devono ricorrere alla creazione di stringhe per creare una query". Chiaramente questa non è la vera motivazione. Sarebbe più sensato se il motore di Cypher lavorasse a questa roba e facesse sostituzioni come quelle sopra per nostro conto. Gli sviluppatori potrebbero quindi parametrizzare completamente le query e, con il miglioramento di Neo4J nel tempo, potrebbe sfruttare questa parametrizzazione laddove possibile. – Herc

+0

Sentiti libero di aprire un problema: https://github.com/neo4j/neo4j/issues :) – Rolf

4

Basta trovato un modo per ottenere questo risultato come stavo correndo nella stessa cosa:

MATCH (n) WHERE {label} IN labels(n) 

Le etichette() restituisce tutte le etichette su un nodo, e le prove operatore IN per l'esistenza nella lista . Apparentemente Cypher consente questo costrutto perché sfrutta un parametro in un campo variabile nel predicato. Secondo i documenti di Cypher, il confronto tra etichette o proprietà nelle clausole WHERE e gli incorporamenti diretti di proprietà/etichette nella definizione del nodo sono ottimizzati allo stesso modo, quindi non dovrebbe essere un impatto significativo sulle prestazioni.

Non sei sicuro di un modo semplice per supportare più etichette possibili ...

+1

Ottima e semplice risposta. Genio! Giusto per completare questa risposta, se si vuole fare lo stesso anche per le relazioni è necessario utilizzare la funzione integrata TYPE, ad esempio: 'MATCH (a) - [r] -> (b) WHERE {relName} = TYPE (r) ID RITORNO (r) AS relId, TYPE (r) AS relName' – artemisian

+0

Puoi supportare più etichette usando la funzione 'any()'. Per esempio 'MATCH (o) WHERE ANY (item IN labels (o) WHERE voce IN $ list_of_labels)' – John

+0

ma non c'è ottimizzazione, sarà una scansione completa del database con controlli per nodo, mentre una ricerca per etichetta è un'etichetta -scan-store operation. è possibile costruire dinamicamente la query cifrata e quindi .e.g. utilizzare apoc.cypher.run per eseguirlo –

Problemi correlati