How to delete multiple nodes by specific ID using Cypher - jdbc

I am trying to optimize this cypher query.
I am using neo4j-jdbc
For a given source node and list of target nodes I would like to remove the relationship("follow) between them).
Right now I only found a way to execute them on each transaction:
for example:
source node: 34 and targetNodes: 777
execute:
MATCH (p1:users)-[r:follow]->(p2:users)
WHERE p1.userId='34' and p2.userId='777' and
delete r
java neo4j-jdbc code:
public void deleteNodes(String sourceUserId, Set<String> userIdsList) {
try {
conn = neo4jDataSource.openConnection();
conn.setAutoCommit(false);
newUserDistanceList.forEach(user -> {
try {
this.deleteRelationship(sourceUserId, user.getUserId());
} catch (SQLException e) {
throw new RuntimeException("Error deleting relationship for sourceNode=" + sourceUserId + ", targetNode=" + user.getUserId(), e);
}
});
conn.commit();
} catch (Exception e) {
throw new RuntimeException("Error deleting relationship for sourceNode=" + sourceUserId, e);
} finally {
neo4jDataSource.closeConnection(conn);
}
}
public void createNodeAndSetRelationship(String sourceUserId, String targetUserId) throws SQLException {
String deleteUserStmnt =
"MATCH (p1:users)-[r:follow]->(p2:users) "+
"WHERE p1.userId=~{1} and p2.userId=~{2} and "+
"delete r";
try (final PreparedStatement ps1 = conn.prepareStatement(deleteUserStmnt)) {
ps1.setString(1, sourceUserId);
ps1.setString(2, targetUserId);
ps1.executeQuery();
}
}
How would you optimize this into one query to include it all? is it even possible?
*P.S please add any other notes about this code if you find them helpful.
Response to comments:
I added my nodes this way:
MERGE (p1:C10{userId:'1'})
MERGE (p2:C10{userId:'2'})
MERGE (p3:C10{userId:'3'})
MERGE (p1)-[r1:follow]->(p2)
MERGE (p1)-[t1:follow]->(p3)
When executing the delete query nothing is being deleted:
MATCH (user1:C10 {userId: 1})-[r:follow]->(users:C10) WHERE
users.userId in [3, 2] DELETE r
userId is unique property on each node
Any idea?
Thank you,
ray.

Query
Let's create some data to test that our query will work.
CREATE (user1:User {id: "1"})
CREATE (user2:User {id: "2"})
CREATE (user3:User {id: "3"})
CREATE (user1)-[:FOLLOW]->(user2)
CREATE (user1)-[:FOLLOW]->(user3)
How our data looks right now:
Let's write query to delete :FOLLOW relationship from between user1 and user2 and user3:
MATCH (user1:User {id: "1"})-[r:FOLLOW]->(users:User)
WHERE users.id in ["2", "3"]
DELETE r
Now relationship's are gone:
Good luck :)
Note: this works with strings too.
Note2: you should check that index (or event constraint) exists for your userId property on :user nodes.
neo4j-jdbc
Code sample for neo4j-jdbc that does work:
Class.forName("org.neo4j.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:neo4j://localhost:7474/");
String removeRelationships = "MATCH (user1:User {id: {1}})-[r:FOLLOW]->(users:User) " +
"WHERE users.id in {2} " +
"DELETE r";
try (final PreparedStatement stmt = connection.prepareStatement(removeRelationships)) {
stmt.setObject(1, "1");
stmt.setObject(2, new ArrayList<Integer>() {{
add("2");
add("3");
}});
stmt.executeQuery();
}

Related

How to get count(id) from table with graphql

I've been playing around with resolvers in graphql and need what's seemingly a simple query, but I can't figure it out.
I want to query a table and get results in something like this:
SELECT hero_id, count(id) FROM "Build"
GROUP BY hero_id
ORDER BY hero_id
How do I write the resolver to return the count of rows by id on a table?
I thought the Table.findAndCountAll() would return results I'm looking for.
const buildCount = {
type: BuildCountType,
resolve(parent, args){
return Build.findAndCountAll().then(result => {
console.log(result)
return {
count: result.rows
}
})
}
}
Thanks,
StuckAndConfused ;)

order by in sub document in DocumentDB

I have a json document like following with sub-document
[
{
"id": "73e799df-b7a5-7470-4f25-ee6c1811a5b2",
"tblType": "search",
"memberId": 2,
"results": [
{"prcnt": 89,"distance": 8867775.747141607},
{"prcnt": 30,"distance": 11010216.470879028},
{"prcnt": 96,"distance": 9128590.716183286},
{"prcnt": 41,"distance": 9652043.937920697}
]
}
]
i want to get the 'results' tag data only in the query with order by prcnt
SELECT top 10 m.results FROM m join r in m.results where m.memberId=2
and m.tblType='search' order by r.prcnt
when i am executing the query, getting the error as follows..
Order-by over correlated collections is not supported.
how to get the data per my requirement.
Thanks in advance!!
According to your requirement, I have checked this issue. Here are my understanding about this issue:
If the memberId and tblType could locate the single document, then you could refer to this similar issue:
UDF
function sortByPrcntNumber (results) {
return results.sort(function(a,b){
return a.prcnt-b.prcnt;
});
}
QUERY
SELECT value udf.sortByPrcntNumber(c.results)
from c
where c.memberId=2 and c.tblType='search'
If the memberId and tblType could retrieve multiple documents, I assumed that you could leverage STORED PROCEDURES as follows:
function sample(top,tblType,memberId){
var collection=getContext().getCollection();
var isAccepted=collection.queryDocuments(
collection.getSelfLink(),
"SELECT value {\"prcnt\":r.prcnt,\"distance\":r.distance} from c join r in c.results where c.tblType='"+tblType+"' and c.memberId="+memberId+"",
function(err,feed,options){
if(err) throw err;
if(!feed||!feed.length) getContext().getResponse().setBody("no docs found");
else {
//order by prcnt
var orderedFeed=feed.sort(function(a,b){
return a.prcnt-b.prcnt;
});
//select top
var topFeed=orderedFeed.slice(0,top);
getContext().getResponse().setBody(topFeed);
}
});
if(!isAccepted) throw new Error("The query was not accepted by the server.");
}
Result

Search returns document after delete

I have the weird effect that a search on an index returns a document I have just deleted before. A "get" works correctly. Am I doing something wrong? The search has no restrictions(client.prepareSearch("test").execute(...))
I'm running an "ESIntegTestCase" with Elastic Search 5.0
#Test
public void testES() throws Exception {
String index = "test";
String type = "event";
String doc = "{\"Key0\":\"Val0\"}";
createIndex(index);
Semaphore sem = new Semaphore(0);
client().prepareIndex(index, type).setSource(doc).execute(handleOrError(postResp -> {
client().prepareGet(postResp.getIndex(), postResp.getType(), postResp.getId()).execute(handleOrError(getResp -> {
printGR(getResp);
client().prepareSearch(postResp.getIndex()).execute(handleOrError(searchResponse -> {
printSR(searchResponse);
client().prepareDelete(postResp.getIndex(), postResp.getType(), postResp.getId()).execute(handleOrError(resp -> {
printDR(resp);
client().prepareGet(postResp.getIndex(), postResp.getType(), postResp.getId()).execute(handleOrError(getResp2 -> {
printGR(getResp2);
client().prepareSearch(postResp.getIndex()).execute(handleOrError(searchResponse2 -> {
printSR(searchResponse2);
sem.release();
}));
}));
}));
}));
}));
}));
sem.acquire();
}
Prints:
1) GetResponse: {"_index":"test","_type":"events","_id":"AVgv1NHPHZ0vJaA-eRhJ","_version":1,"found":true,"_source":{"Key0":"Val0"}}
2) SearchResponse:{"took":2,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"test","_type":"events","_id":"AVgv1NHPHZ0vJaA-eRhJ","_score":1.0,"_source":{"Key0":"Val0"}}]}}
3) DeleteResponse: DeleteResponse[index=test,type=events,id=AVgv1NHPHZ0vJaA-eRhJ,version=2,result=deleted,shards="_shards"{"total":2,"successful":2,"failed":0}]
4) GetResponse: {"_index":"test","_type":"events","_id":"AVgv1NHPHZ0vJaA-eRhJ","found":false}
5) SearchResponse:{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"test","_type":"events","_id":"AVgv1NHPHZ0vJaA-eRhJ","_score":1.0,"_source":{"Key0":"Val0"}}]}}
You found the difference between the search index and doing a get request. A get request also makes use of the transaction log. If you want the delete to have an effect on search, you need to execute a refresh. With elastic 5 there now is an option to wait for a refresh after an insert or a delete. Using that functionality should give you what you want. More information can be found here:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-refresh.html

WrapperQueryBuilder - aggs query throwing Query Malformed exception

I have a Json query string:
"\"query\":{\"match_all\": {}},\"aggs\":{\"avg1\":{\"avg\":{\"field\":\"age\"} } }";
When query is executed via Jest Client, aggregation values are available.
But when this query is converted into a Query Builder (WrapperQueryBuilder) object, getting the following exception.
; nested: QueryParsingException[[st1index] [_na] query malformed, must start with start_object]; }{[ixJ-6RHNR5C6fC7HfJHqaw][st1index][4]: SearchParseException[[st1index][4]: from[-1],size[-1]: Parse Failure [Failed to parse source [{
"query" : {
"wrapper" : {
"query" : "InF1ZXJ5Ijp7Im1hdGNoX2FsbCI6IHt9fSwiYWdncyI6eyJhdmcxIjp7ImF2ZyI6eyJmaWVsZCI6ImFnZSJ9IH0gfQ=="
}
}
}]]]; nested: QueryParsingException[[st1index] [_na] query malformed, must start with start_object]; }]
How do i fix this?
Edit 1: Code analysis:
Code analysis details added:
public static void main(String[] args) throws Exception
{
try{
//Jest client building
JestClientFactory factory = new JestClientFactory();
HttpClientConfig config = new HttpClientConfig.
Builder("http://localhost:9201")
.connTimeout(10000)
.readTimeout(10000)
.multiThreaded(true).build();
factory.setHttpClientConfig(config);
JestClient jestClient = factory.getObject();
String query ="{\"query\":{\"match_all\": {}},\"aggs\":{\"avg1\":{\"avg\":{\"field\":\"age\"} } }}";
String query2 ="{\"match_all\": {}},\"aggs\":{\"avg1\":{\"avg\":{\"field\":\"age\"} } }}";
WrapperQueryBuilder wrapQB = new WrapperQueryBuilder(query2);
SearchSourceBuilder ssb = new SearchSourceBuilder();
ssb.query(wrapQB);
//working code commented
// Search.Builder searchBuilder = new Search.Builder(query).addIndex("st1index").addType("st1type");
//code which needs to be fixed
Search.Builder searchBuilder = new
Search.Builder(ssb.toString()).addIndex("st1index").addType("st1type");
SearchResult result = jestClient.execute(searchBuilder.build());
System.out.println(result.getJsonString());
}
catch(Exception e)
{
System.out.println("inside exception block");
e.printStackTrace();
}
}
with String query and with commented SearchSourceBuilder, aggs results are displayed. But by using WrapperQueryBuilder , unable to retrieve aggs results
You're almost there, you're simply missing enclosing braces:
"{\"query\":{\"match_all\": {}},\"aggs\":{\"avg1\":{\"avg\":{\"field\":\"age\"}}}}";
^ ^
| |
this one... ...and this one
UPDATE
In the WrapperQueryBuilder, you can only pass the content of the query part, and not the aggregations part. You need to add the aggregation part directly on the SearchSourceBuilderlike this:
SearchSourceBuilder ssb = new SearchSourceBuilder();
// add the query part
String query ="{\"match_all\": {}}";
WrapperQueryBuilder wrapQB = new WrapperQueryBuilder(query);
ssb.query(wrapQB);
// add the aggregation part
AvgBuilder avgAgg = AggregationBuilders.avg("avg1").field("age");
ssb.aggregation(avgAgg);

Couchbase CRUD operations with query language

Im using couchbase 4.0 beta version. And Im successfully able to save the objects into the bucket using rest service. Now I need to access the bucket and do GET, UPDATE and DELETE operations using queries.
This is the my sample object in bucket.
{
"id": "friend$43403778",
"domain": "FRIEND",
"profileId": 43403778,
"screenName": "49ers",
"name": "San Francisco 49ers",
"profileUrl": "http://twitter.com/49ers",
"description": "Official Twitter account of the five-time Super Bowl Champion San Francisco #49ers.",
"friendsCount": 121,
"followersCount": 964650,
"timeZone": "Pacific Time (US & Canada)",
"utcOffset": -25200,
"location": "Santa Clara, CA",
"profileCreated": 1243629277000
}
Now I want to get this object using id. But id value contains special character so my query doesn't works for it. How can I access it???
This is my code snippet which I tried to get this.It doesn't gives me the 300 error code syntax error.
String strQuery = "SELECT * FROM twitter WHERE id=" + id;
Bucket bucket = singletonBucket.getBucket();
QueryResult queryResult = bucket.query(Query.simple(strQuery));
List<QueryRow> result = Collections.emptyList();
if (!queryResult.finalSuccess()) {
log.error("Error occur while retrieving TwitterProfile document for id: {}",id);
String errorStr = "";
for (JsonObject error : queryResult.errors()) {
errorStr = error.toString() + ". ";
log.error("{}", error);
}
throw new DataAccessException(errorStr);
} else {
result = queryResult.allRows();
for(QueryRow row: result){
Object doc = row.value().get(AppConstants.BUCKET_NAME);
TwitterProfile twitterProfile = objectMapper.readValue(doc.toString(), TwitterProfile.class);
twitterProfileList.add(twitterProfile);
}
log.info("Successfully found {} TwitterProfile record for id {}",twitterProfileList.size(), id);
return twitterProfileList.size()>0 ?twitterProfileList.get(0):"";
}
If I tried to get the record using profileId It also doesnt work.
How can I write the simple queries for this bucket.
These are the queries which I tried. Also my bucket name is twitter
String strQuery = "SELECT * FROM twitter WHERE domain=" + AppConstants.DOMAIN_FRIEND;
String strQuery = "DELETE FROM twitter WHERE domain=" + AppConstants.DOMAIN_FRIEND;
Thanks in advance.
When building your query you have to use quotes to indicate your id is a string. Try adding quotes around your id when building your query:
String strQuery = "SELECT * FROM twitter WHERE id='" + id + "'";

Resources