HOW to MERGE in neo4j using JAVA - neo4j

Hi I am trying to insert two properties of a node....I am trying like below..
I am trying to add two properties to CUSTOMER NODE
1.name and 2.TOTAL_CALL_DURATION
How can I add two properties
try ( Transaction tx = graphDb.beginTx() )
{
String queryString = "MERGE (n:CUSTOMER {name:{name},TOTAL_CALL_DURATION:{TOTAL_CALL_DURATION}}) RETURN n";
Map<String, Object> callerProperties = new HashMap<>();
callerProperties.put( "name", callerName );
callerProperties.put("TOTAL_CALL_DURATION", 120);
resultIterator_caller = execEngine.execute( queryString, callerProperties ).columnAs( "n" );
tx.success();
}
The error I am getting like below:
Exception in thread "main" org.neo4j.cypher.ParameterNotFoundException: Expected a parameter named TOTAL_CALL_DURATION
at org.neo4j.cypher.internal.compiler.v2_1.pipes.QueryState$$anonfun$getParam$1.apply(QueryState.scala:45)
at org.neo4j.cypher.internal.compiler.v2_1.pipes.QueryState$$anonfun$getParam$1.apply(QueryState.scala:45)
at scala.collection.MapLike$class.getOrElse(MapLike.scala:128)
at scala.collection.AbstractMap.getOrElse(Map.scala:58)
at org.neo4j.cypher.internal.compiler.v2_1.pipes.QueryState.getParam(QueryState.scala:45)
at org.neo4j.cypher.internal.compiler.v2_1.commands.expressions.ParameterExpression.apply(ParameterExpression.scala:27)
at org.neo4j.cypher.internal.compiler.v2_1.helpers.PropertySupport$$anonfun$firstNullPropertyIfAny$1.isDefinedAt(PropertySupport.scala:29)
at org.neo4j.cypher.internal.compiler.v2_1.helpers.PropertySupport$$anonfun$firstNullPropertyIfAny$1.isDefinedAt(PropertySupport.scala:28)
at scala.collection.TraversableOnce$$anonfun$collectFirst$1.apply(TraversableOnce.scala:132)
at scala.collection.TraversableOnce$$anonfun$collectFirst$1.apply(TraversableOnce.scala:131)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
at scala.collection.TraversableOnce$class.collectFirst(TraversableOnce.scala:131)
at scala.collection.AbstractTraversable.collectFirst(Traversable.scala:105)
at org.neo4j.cypher.internal.compiler.v2_1.helpers.PropertySupport$.firstNullPropertyIfAny(PropertySupport.scala:28)
at org.neo4j.cypher.internal.compiler.v2_1.mutation.MergeNodeAction.ensureNoNullNodeProperties(MergeNodeAction.scala:95)
at org.neo4j.cypher.internal.compiler.v2_1.mutation.MergeNodeAction.exec(MergeNodeAction.scala:73)
at org.neo4j.cypher.internal.compiler.v2_1.pipes.ExecuteUpdateCommandsPipe.org$neo4j$cypher$internal$compiler$v2_1$pipes$ExecuteUpdateCommandsPipe$$exec(ExecuteUpdateCommandsPipe.scala:57)
at org.neo4j.cypher.internal.compiler.v2_1.pipes.ExecuteUpdateCommandsPi$$$$1019fdff8b266d7d9d5647386930b3d8$$$$ands$1$$anonfun$apply$2.apply(ExecuteUpdateCommandsPipe.scala:46)
at org.neo4j.cypher.internal.compiler.v2_1.pipes.ExecuteUpdateCommandsPi$$$$1019fdff8b266d7d9d5647386930b3d8$$$$ands$1$$anonfun$apply$2.apply(ExecuteUpdateCommandsPipe.scala:46)
at scala.collection.Iterator$$anon$13.hasNext(Iterator.scala:371)
at scala.collection.Iterator$$anon$13.hasNext(Iterator.scala:371)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator$$anonfun$hasNext$1.apply$mcZ$sp(ClosingIterator.scala:37)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator$$anonfun$hasNext$1.apply(ClosingIterator.scala:34)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator$$anonfun$hasNext$1.apply(ClosingIterator.scala:34)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator$$anonfun$failIfThrows$1.apply(ClosingIterator.scala:93)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.decoratedCypherException(ClosingIterator.scala:102)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.failIfThrows(ClosingIterator.scala:91)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.hasNext(ClosingIterator.scala:34)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.foreach(ClosingIterator.scala:28)
at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48)
at scala.collection.mutable.ListBuffer.$plus$plus$eq(ListBuffer.scala:176)
at scala.collection.mutable.ListBuffer.$plus$plus$eq(ListBuffer.scala:45)
at scala.collection.TraversableOnce$class.to(TraversableOnce.scala:273)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.to(ClosingIterator.scala:28)
at scala.collection.TraversableOnce$class.toList(TraversableOnce.scala:257)
at org.neo4j.cypher.internal.compiler.v2_1.ClosingIterator.toList(ClosingIterator.scala:28)
at org.neo4j.cypher.internal.compiler.v2_1.EagerPipeExecutionResult.<init>(EagerPipeExecutionResult.scala:32)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anonfun$getExecutionPlanFunction$1$$anonfun$apply$2.apply(ExecutionPlanBuilder.scala:125)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anonfun$getExecutionPlanFunction$1$$anonfun$apply$2.apply(ExecutionPlanBuilder.scala:119)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionWorkflowBuilder.runWithQueryState(ExecutionPlanBuilder.scala:168)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anonfun$getExecutionPlanFunction$1.apply(ExecutionPlanBuilder.scala:118)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anonfun$getExecutionPlanFunction$1.apply(ExecutionPlanBuilder.scala:103)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anon$1.execute(ExecutionPlanBuilder.scala:68)
at org.neo4j.cypher.internal.compiler.v2_1.executionplan.ExecutionPlanBuilder$$anon$1.execute(ExecutionPlanBuilder.scala:67)
at org.neo4j.cypher.internal.ExecutionPlanWrapperForV2_1.execute(CypherCompiler.scala:159)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:76)
at org.neo4j.cypher.ExecutionEngine.execute(ExecutionEngine.scala:71)
at org.neo4j.cypher.javacompat.ExecutionEngine.execute(ExecutionEngine.java:84)
at com.emc.neo4jConnectivity.NodeCreation.createNodes(NodeCreation.java:84)
at com.emc.neo4jConnectivity.NodeCreation.main(NodeCreation.java:136)

There's nothing wrong with your code, I've converted it into a self-contained groovy script running fine without any errors.
Try to provide a minimal viable testcase showing the error you're observing
For reference, here's the script:
#Grapes([
#Grab(group="org.neo4j", module="neo4j-kernel", version="2.1.8", classifier="tests"),
#Grab(group="org.neo4j", module="neo4j-kernel", version="2.1.8"),
#Grab(group="org.neo4j", module="neo4j-cypher", version="2.1.8")
])
import org.neo4j.test.TestGraphDatabaseFactory
import org.neo4j.cypher.javacompat.ExecutionEngine
import org.neo4j.helpers.collection.IteratorUtil
def graphDb = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().newGraphDatabase()
def executionEngine = new ExecutionEngine(graphDb)
def resultIterator_caller
def tx = graphDb.beginTx()
try {
String queryString = "MERGE (n:CUSTOMER {name:{name},TOTAL_CALL_DURATION:{TOTAL_CALL_DURATION}}) RETURN n";
Map<String, Object> callerProperties = new HashMap<>();
callerProperties.put( "name", "abc" );
callerProperties.put("TOTAL_CALL_DURATION", 120);
resultIterator_caller = executionEngine.execute( queryString, callerProperties ).columnAs( "n" );
tx.success()
} finally {
tx.close()
}
// we need another tx to consume the result (since we're returning node instances)
transaction = graphDb.beginTx()
try {
assert resultIterator_caller.hasNext()
def nextNode = resultIterator_caller.next()
assert nextNode.getProperty("name",null) == "abc"
assert nextNode.getProperty("TOTAL_CALL_DURATION",null) == 120
assert !resultIterator_caller.hasNext()
tx.success()
} finally {
tx.close()
}

Related

Can milo generate the cutomized datatype using uamodeler/

I have tried milo, but I found it hard to create the data object one by one.
Can milo just generate the corresponding codes by reading the xml files? And how?
Many Thanks!
yes you can
private void addCustomStructTypeVariable(UaFolderNode rootFolder) throws Exception {
NodeId dataTypeId = CustomStructType.TYPE_ID
.localOrThrow(getServer().getNamespaceTable());
NodeId binaryEncodingId = CustomStructType.BINARY_ENCODING_ID
.localOrThrow(getServer().getNamespaceTable());
UaVariableNode customStructTypeVariable = UaVariableNode.builder(getNodeContext())
.setNodeId(newNodeId("HelloWorld/CustomStructTypeVariable"))
.setAccessLevel(AccessLevel.READ_WRITE)
.setUserAccessLevel(AccessLevel.READ_WRITE)
.setBrowseName(newQualifiedName("CustomStructTypeVariable"))
.setDisplayName(LocalizedText.english("CustomStructTypeVariable"))
.setDataType(dataTypeId)
.setTypeDefinition(Identifiers.BaseDataVariableType)
.build();
CustomStructType value = new CustomStructType(
"foo",
uint(42),
true
);
ExtensionObject xo = ExtensionObject.encodeDefaultBinary(
getServer().getSerializationContext(),
value,
binaryEncodingId
);
customStructTypeVariable.setValue(new DataValue(new Variant(xo)));
getNodeManager().addNode(customStructTypeVariable);
customStructTypeVariable.addReference(new Reference(
customStructTypeVariable.getNodeId(),
Identifiers.Organizes,
rootFolder.getNodeId().expanded(),
false
));
}

Neo4jRule : Include algorithm plugins (such as randomWalk) to a Neo4j test server

I'm trying to test some procedure I have made with the code given by Neo4j for testing procedure. However my procedure is based on the results from the random walk algorithm which I have to call through 'algo.randomWalk.stream()'.
To do so, I'm instantiating a Neo4j test server. However it doesn't recognize the algo.randomWalk.stream(), because I think it doesn't have the algorithm package in its plugins.
This is the code I'm working on
package example;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.driver.v1.*;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import static org.neo4j.driver.v1.Values.parameters;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import org.neo4j.harness.junit.Neo4jRule;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;
import static org.neo4j.driver.v1.Values.parameters;
public class ScoringTest {
// This rule starts a Neo4j instance for us
#Rule
public Neo4jRule neo4j = new Neo4jRule()
// This is the Procedure we want to test
.withProcedure( Scoring.class );
//org.neo4j.server.thirdparty_jaxrs_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged
#Test
public void shouldAllowReturningTheLastValue() throws Throwable
{
// This is in a try-block, to make sure we close the driver after the test
try( Driver driver = GraphDatabase
.driver( neo4j.boltURI() , Config.build().withEncryptionLevel( Config.EncryptionLevel.NONE ).toConfig() ) )
{
System.out.println(neo4j.boltURI().toString());
// Given
neo4j.withExtension(neo4j.boltURI().toString(), "..\\graph-algorithms-algo-3.5.4.0.jar");
Session session = driver.session();
String PATH = "..\\data\\data.json";
File JSON_SOURCE = new File(PATH);
List<HashMap<String,Object>> mss = new ObjectMapper().readValue(JSON_SOURCE, List.class);
session.run("UNWIND {bulk} as row " +
"CREATE (n:Users) " +
"SET n += row.properties", parameters("bulk", mss ));
for(int k = 0; k<9; k++) {
PATH = "..\\data\\"+k+".json";
mss = new ObjectMapper().readValue(JSON_SOURCE, List.class);
JSON_SOURCE = new File(PATH);
session.run("UNWIND {bulk} as row " +
"MATCH (from:Users), (to:Clips) " +
"WHERE ID(from) = toInt(row.from) AND ID(to) = toInt(row.to._key) " +
"CREATE (from)-[rel:hasClipped]->(to) " +
"SET rel += row.properties ", parameters("bulk", mss ));
}
// When
Value result = session.run( "MATCH (n:Clips) WHERE ID(n) = 1038 " +
"CALL algo.randomWalk.stream(ID(n), 2, 1) " +
"YIELD nodeIds " +
"UNWIND nodeIds as nodeId " +
"MATCH (l:Clips)-[r:hasClipped]-(q:Users) " +
"WHERE (ID(l) = nodeId) AND (ID(q) in nodeIds) " +
"WITH collect({relation:r,Clip:l,User:q}) as res " +
"RETURN res").single().get("res");
System.out.println(result);
// Then
assertThat( result, equalTo( 0L ) );
}
}
}
The exact error that I get is : org.neo4j.driver.v1.exceptions.ClientException: There is no procedure with the name algo.randomWalk.stream registered for this database instance. Please ensure you've spelled the procedure name correctly and that the procedure is properly deployed.
Thanks for your time and your future answers,
Syndorik
So I found out a solution for this issue.
There's an option for Neo4jRule object that allows you to change the path to the plugin directory.
For instance, I've just added this config to Neo4jRule and then could call the graphalgo library :
public Neo4jRule neo4j = new Neo4jRule()
// This is the Procedure we want to test
.withProcedure( Scoring.class )
.withConfig(GraphDatabaseSettings.plugin_dir, "PATH_TO_PLUGIN_DIR")
.withConfig(GraphDatabaseSettings.procedure_unrestricted, "algo.*" );

Kafka join streams filter by key

I did some very easy example with join streams where two topics have the simple key value structure (integer/string) and it's works perfectly.
May I ask, how can I do something like:
SELECT * FROM stream1, stream2
WHERE stream1.key = stream2.key AND (stream1.key > 50 && stream1.key < 100) AND (stream2.key > 50 AND stream2.key < 100)
Kafka allows something like this?
Finally what I want to do is to filter 2 joined streams where key will be GenericRecord and it will looks somehow:
SELECT * FROM stream1, stream2
WHERE stream1.genericRecordkey.someId. = stream2.genericRecordkey.someId
My test example:
public void joinKStreamToKStreamWhereKeyValueIsIntegerString() throws Exception {
String uniqueKey = new Object() {
}.getClass().getEnclosingMethod().getName();
long timestamp = new Date().getTime();
String firstTopic = String.format("%1$s_1_%2$s", uniqueKey, timestamp);
String secondTopic = String.format("%1$s_2_%2$s", uniqueKey, timestamp);
String outputTopic = String.format("%1$s_output_%2$s", uniqueKey, timestamp);
String appIdConfig = String.format("%1$s_app_id_%2$s", uniqueKey, timestamp);
String groupIdConfig = String.format("%1$s_group_id_%2$s", uniqueKey, timestamp);
List<KeyValue<Integer, String>> ikv1 = Arrays.asList(
new KeyValue<>(1, "Bruce Eckel"),
new KeyValue<>(2, "Robert Lafore"),
new KeyValue<>(3, "Andrew Tanenbaum")
);
List<KeyValue<Integer, String>> ikv2 = Arrays.asList(
new KeyValue<>(3, "Modern Operating System"),
new KeyValue<>(1, "Thinking in Java"),
new KeyValue<>(3, "Computer Architecture"),
new KeyValue<>(4, "Programming in Scala")
);
List<KeyValue<Integer, String>> expectedResults = Arrays.asList(
new KeyValue<>(3, "Andrew Tanenbaum/Modern Operating System"),
new KeyValue<>(1, "Bruce Eckel/Thinking in Java"),
new KeyValue<>(3, "Andrew Tanenbaum/Computer Architecture")
);
Integer partitions = 1;
Integer replication = 1;
Properties topicConfig = new Properties();
TopicUtils.createTopic(firstTopic, partitions, replication, topicConfig);
TopicUtils.createTopic(secondTopic, partitions, replication, topicConfig);
TopicUtils.createTopic(outputTopic, partitions, replication, topicConfig);
final Serde<String> stringSerde = Serdes.String();
final Serde<Integer> integerSerde = Serdes.Integer();
Properties streamsConfiguration = new Properties();
streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, appIdConfig);
streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS_CONFIG);
streamsConfiguration.put(StreamsConfig.ZOOKEEPER_CONNECT_CONFIG, ZOOKEEPER_CONNECT_CONFIG);
streamsConfiguration.put(StreamsConfig.KEY_SERDE_CLASS_CONFIG, Serdes.Integer().getClass().getName());
streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
// The commit interval for flushing records to state stores and downstream must be lower than
// this integration test's timeout (30 secs) to ensure we observe the expected processing results.
streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10 * 1000);
streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
// Use a temporary directory for storing state, which will be automatically removed after the test.
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, TestUtils.tempDirectory().getAbsolutePath());
KStreamBuilder builder = new KStreamBuilder();
KStream<Integer, String> firstStream = builder.stream(integerSerde, stringSerde, firstTopic);
KStream<Integer, String> secondStream = builder.stream(integerSerde, stringSerde, secondTopic);
KStream<Integer, String> outputStream = firstStream.join(secondStream, (l, r) -> {
return l + "/" + r;
}, JoinWindows.of(TimeUnit.SECONDS.toMillis(5)), integerSerde, stringSerde, stringSerde);
outputStream.to(integerSerde, stringSerde, outputTopic);
KafkaStreams streams = new KafkaStreams(builder, streamsConfiguration);
streams.start();
Properties pCfg1 = new Properties();
pCfg1.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS_CONFIG);
pCfg1.put(ProducerConfig.ACKS_CONFIG, "all");
pCfg1.put(ProducerConfig.RETRIES_CONFIG, 0);
pCfg1.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class);
pCfg1.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
IntegrationTestUtils.produceKeyValuesSynchronously(firstTopic, ikv1, pCfg1);
Properties pCfg2 = new Properties();
pCfg2.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS_CONFIG);
pCfg2.put(ProducerConfig.ACKS_CONFIG, "all");
pCfg2.put(ProducerConfig.RETRIES_CONFIG, 0);
pCfg2.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class);
pCfg2.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
IntegrationTestUtils.produceKeyValuesSynchronously(secondTopic, ikv2, pCfg2);
Properties consumerConfig = new Properties();
consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS_CONFIG);
consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, groupIdConfig);
consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class);
consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
List<KeyValue<Integer, String>> actualResults = IntegrationTestUtils.waitUntilMinKeyValueRecordsReceived(consumerConfig, outputTopic, expectedResults.size());
streams.close();
assertThat(actualResults).containsExactlyElementsOf(expectedResults);
}
Hope I explained well and thanks for any help.
You can just apply a filter before you do the join.
outputStream = firstStream.filter(...).join(secondStream.filter(...), ...);
If you want to join on stream1.genericRecordkey.someId you will need to extract someId first and set it as key:
firstStream.selectKey((k,v) -> v.someId) ).join(secondStream.selectKey((k,v) -> v.someId), ...);
For more details, check out the docs: http://docs.confluent.io/current/streams/developer-guide.html

sqljocky query does not have a method 'then' (dart database access)

I'm trying to code sql access to a database using sqljocky in Dart. As I want to make some computation with the result returned by my database Handler, the method return a Future.
But when I try to run it, I'm getting the following error:
Uncaught Error: The null object does not have a method 'then'`
I've ran the debugger and found that this error raise on:
db.query('select * from user where email="$email"').then(...)
but the catchError clause doesn't fire.
My handler method is:
// db is a ConnectionPool
Future<Map<String,String>> queryUser(String email){
print(email);
db.query('select * from user where email="${email}"').then((result) { // here raise the error
Map<String,String> results = new Map<String,String>();
result.forEach((row){
results['status'] = '200';
results['ID'] = row[0];
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
});
return results;
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
return results;
});
}
And the method that call this handler is:
List getUser(String email) {
Future<Map<String,String>> result = dbhandler.queryUser(email);
result.then((Map<String,String> result) {
String statuscode = result['status'];
result.remove('status');
String json = JSON.encode(result);
List pair = new List();
pair.add(statuscode);
pair.add(json);
return pair;
});
If I run the query directly in phpmyadmin, it return correct data, so it is correct.
Can someone give me a hint about how to solve it?
The queryUser() method will always return null, as there is no return statement. In the next release of Dart there will be a static hint warning for this, but at the moment there is none.
Perhaps the code below is what you meant to do. Note the initial return statement before db.query(), and the extra result.toList() call. I haven't tested this, so there's probably a typo or two.
Future<Map<String,String>> queryUser(String email){
print(email);
return db.query('select * from user where email="${email}"')
.then((result) => result.toList())
.then((rows) {
var row = rows.single;
Map<String,String> results = new Map<String,String>();
results['status'] = '200';
results['ID'] = row[0];
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
return results;
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
return results;
});
}
You can also make this a bit cuter using map literals:
Future<Map<String,String>> queryUser(String email){
return db.query('select * from user where email="${email}"')
.then((result) => result.toList())
.then((rows) => <String, String> {
'status': '200',
'ID': rows.single[0],
'Image': rows.single[1],
'Name': rows.single[2],
'Email': rows.single[3],
'Password': rows.single[4] })
.catchError((error) => <String, String> {'status': '500'});
}
Finally I found the answer using Completer to control the Future object, but the real problem was, as Greg Lowe said, that my methods doesn't return anything as they come to end before the then clause.
Using completer, I've made my query method as:
Future<Map<String,String>> queryUser(String email){
Completer c = new Completer();
db.query('select * from user where email="$email"').then((result) {
Map<String,String> results = new Map<String,String>();
result.forEach((row){
results['status'] = '200';
results['ID'] = row[0].toString();
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
}).then((onValue){
c.complete(results);
});
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
c.completeError((e) => print("error en queryUser"));
});
return c.future;
}
I also solved an error when using the foreach method, at first I supposed it return nothing, but after that, I noticed that it return a Future, so I added a then clause.
And my getUser method:
Future<List> getUser(String email) {
Completer c = new Completer();
Future<Map<String,String>> result = dbhandler.queryUser(email);
result.then((Map<String,String> result) {
String statuscode = result['status'];
result.remove('status');
String json = JSON.encode(result);
List pair = new List();
pair.add(statuscode);
pair.add(json);
c.complete(pair);
});
return c.future;
}
After those changes, everything works right

Async Futures running in sequence to completion

I encountered the following example (Example 1 below) of Futures which caused me to wonder if I could alter the way that I was handling Futures and remove all of the nested function calls that preserve order of processing, which however result in indentation which I find a bit messy.
The altered version of my program did not work however. It did not preserve the order of processing and did not “wait” for function to complete. For example, before returning from the first call (fGetUserInput), another subsequent function was called.
Why is it that in Example 1, all of the “1st level” “new Future”s processed sequentially, however in Example 2, my altered code, the order of processing is not preserved. While the call to fGetUserInput is being processed, one of the Futures that follows it is processed?
Is it perhaps that “Example 1” only “works” because all of the statements are synchronous?
I came across a reference to “runAsync”. Can that be used to achieve what I want? (process in sequence without all of the indentation).
// Example 1. Code that I encountered for Futures //
import 'dart:async';
main() {
new Future(() => print('1'))
.then((_) => print('a'))
.then((_) => print('b'));
new Future(() => print('2'))
.then((_) => print('c'))
.then((_) => print('d'));
new Future(() => print('3'))
.then((_) =>
new Future(() => print('e'))
.then((_) => print('f'))
);
new Future(() => print('4'))
.then((_) =>
new Future(() => print('g'))
.then((_) => print('d'))
);
}
The above results in the following console output order :-
1 a b 2 c d 3 4 e f g d
Which I thought made sense.
Therefore, I modified my code to test it as follows :-
// Example 2. Altered version of my code which //
// does not preserve the order of processing, //
// which is necessary for program to function. //
new async.Future(() => fGetUserInput())
.then((lInput) {
iMaxIters = int.parse(lInput[4]);
tClearTable = (lInput[5] == "y");
iDivisor = fInitialize(iMaxIters);
tgPrint = false; // printing off
sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sStartTime = lInput[7];
})
.catchError((oError) => fFatal("Get User Input", oError));
new async.Future(() => fConnectToDb(sUri, sStartTime))
.then((bool tConnected) {
if (ogDb == null)
fFatal("Unable to connect to database", "");
print ("Processing database ......");
})
.catchError((oError) => fFatal("Connect to Db", oError));
new async.Future(() => fClearTable(tClearTable))
.then((sResult) => print (sResult+"\n"))
.catchError((oError) => fFatal("Clear Table", oError));
new async.Future(() => fProcessInserts(iMaxIters, iDivisor))
.then((sResult) => print (""))
.catchError((oError) => fFatal("Process Inserts", oError));
new async.Future(() => fSetupRandKeys())
.then((sResult) => print (""))
.catchError((oError) => fFatal("Setup Random Keys", oError));
new async.Future(() => fProcessUpdates(iMaxIters, iDivisor))
.then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
})
.catchError((oError) => fFatal("Process Updates", oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("\nProcessing Inserts ......");
fResetAndStartWatch();
The following is my code prior to the above changes, and the following Example 3 appears to work OK. I don't like the extent of indentation, and in situations with more function calls, that would increase the extent of indentation. I was hoping for a more elegant way to do it.
// Example 3: The original version of my code //
// which does preserve the order of processing //
void main() {
print("");
String sCheckPoint = "Get User Input";
fGetUserInput()
.then((lInput) {
int iMaxIters = int.parse(lInput[4]);
bool tClearTable = (lInput[5] == "y");
int iDiv = fInitialize(iMaxIters);
tgPrint = false; // printing off
String sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sCheckPoint = "Connect to Database";
fConnectToDb(sUri, lInput[7]).then((bool tConnected) {
if (ogDb == null)
fFatal(sCheckPoint, "Unable to conenct to Db");
print ("Processing database ......");
sCheckPoint = "Clear Table";
fClearTable(tClearTable).then((sResult) {
print (sResult+"\n");
sCheckPoint = "Process Inserts";
fProcessInserts(iMaxIters, iDiv).then((sResult) {
print;
sCheckPoint = "Set-up Random Keys";
fSetupRandKeys().then((sResult) {
print;
sCheckPoint = "Process Updates";
fProcessUpdates(iMaxIters, iDiv).then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
});
});
});
});
});
})
.catchError((oError) => fFatal(sCheckPoint, oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("Processing Inserts ......");
fResetAndStartWatch();
Remember that you can chain futures, which will reduce your indentation by quite a bit.
The downside is that you don't get nested scopes, which can be useful if you have more than one value to propagate between async blocks, but that can be worked around in a few ways.
Here's you example 3 with chaining:
// Example 3 with chaining
void main() {
String checkPoint = "Get User Input";
getUserInput().then((input) {
int maxIters = int.parse(input[4]);
bool clearTable = (input[5] == "y");
int div = initialize(maxIters);
shouldPrint = false; // printing off
String uri =
"postgres://${input[1]}:${input[2]}#localhost:5432/${input[3]}";
checkPoint = "Connect to Database";
return connectToDb(uri, input[7]).then((bool connected) {
if (db == null)
fatal(checkPoint, "Unable to conenct to Db");
print ("Processing database ......");
checkPoint = "Clear Table";
return clearTable(shouldClearTable);
}).then((result) {
print (result+"\n");
checkPoint = "Process Inserts";
return processInserts(maxIters, div);
}).then((result) {
print('');
checkPoint = "Set-up Random Keys";
return setupRandKeys();
}).then((result) {
print('');
checkPoint = "Process Updates";
return processUpdates(maxIters, div);
}).then((result) {
String totValue = formatAmount(grandTotAmt, true, 2);
print("Grand Total added to database = \$${totValue}");
return db.close();
// exit(0); pretty much never call exit()
});
}).catchError((error) => fatal(checkPoint, error));
}
Edit: Oops, looking more closely I got bit by the scoping problem... I added a level of nesting just to capture the needed vars in a scope accessible by the following blocks. I'm also removing the hungarian-ish notation, because... don't do that in Dart :)

Resources