How do I return a relation in RedisGraph? - redisgraph

Using RedisGraph, I'd like for a QUERY to RETURN a relation's type.
An example query:
MATCH (n1:Entity { id: "foo" }) MATCH (n2:Entity2 { id: "bar" }) CREATE (n1)-[r:areFriends]->(n2) RETURN *
Unfortunately, the returned value only includes n1 and n2, but not r.
The returned record contains the following:
Record {
_header: [ 'n1.id', 'n1.name', 'n2.id', 'n2.name' ]
This is compliant with my schema, yet r is notoriously missing there.
How can I get RETURN to return the relation?
OpenCypher supports syntax like:
RETURN n1, n2, type(r) but this doesn't seem to work in RedisGraph.

PR just merged to MASTER
127.0.0.1:6379> GRAPH.QUERY G "create (:Entity {id:'foo'}), (:Entity {id:'bar'})"
1) (empty list or set)
2) 1) "Labels added: 1"
2) "Nodes created: 2"
3) "Properties set: 2"
4) "Query internal execution time: 0.536000 milliseconds"
127.0.0.1:6379> GRAPH.QUERY G "MATCH (n1:Entity {id:'foo'}), (n2:Entity {id:'bar'}) CREATE (n1)-[r:areFriends]->(n2) RETURN n1,n2,TYPE(r)"
1) 1) 1) "n1.id"
2) "n2.id"
3) "TYPE(r)"
2) 1) "foo"
2) "bar"
3) "areFriends"
2) 1) "Relationships created: 1"
2) "Query internal execution time: 0.409000 milliseconds"
Please note, this change will be part of version 1.0.12
Until that version is released, you can either build from source or use docker image redislabs/redisgraph:edge

Currently RedisGraph doesn't support mixing the CREATE and RETURN clauses, although this work is in the making and should be available within a couple of days.

Related

Algolia Kotlin DSL query using AND + OR

Example Search Data Structure:
{
"tpe": "HOME",
"sid": "fyyb1-YQWMAs6Y8vGrk6OAcgjZ-XzTY03Ngfr",
"sessionCreatedAtUtc": 1623854018195,
"title": "Baked Enchiladas",
"recipeCreatedAtUtc": 1623854008999,
"releaseStatus": 0,
"rid": "iBtk3PJ7HS9JKLRu4PHa",
"uid": "SelHOKTaw1k4WZpTH9y",
"desc": "Some info about this recipe...",
"objectID": "iBtk3PJ7HS9JKLRu4PHa"
}
Query I am unable to build:
Search for text, where uid = "SelHOKTaw1k4WZpTH9y" OR (tpe = "PRO" AND releaseStatus=1)
So far I have only been able to get the latter part of filtering to work:
filters {
and {
facet("tpe", "PRO")
facet("releaseStatus", 1)
}
}
For anyone else that struggled through this, I found a solution.
First and foremost, from the Algolia docs:
Combining ANDs and ORs# While you may use as many ANDs and ORs as you
need, you’ll need to be careful about how you combine them.
For performance reasons, we do not allow you to put groups of ANDs
within ORs. Here are some considerations to take into account:
We allow ( x AND (y OR z) AND (a OR b) )
We allow ( x AND y OR z AND a OR b )
We don’t allow ( x OR ( y AND z) OR ( a AND b) )
We don’t allow (a AND b) OR (c AND d)
Source
So the query I was attempting to develop was not easily possible.
In the Kotlin Algolia SDK:
and {
facet("tpe", "PRO")
facet("releaseStatus", 1)
}
Means tpe = PRO AND releaseStatus = 1. You can verify this when you attach your debugger to the result from the query.filter DSL block.
filter {
and {
facet("tpe", "PRO")
facet("releaseStatus", 1)
}
orFacet {
facet("uid", "SelHOKTaw1k4WZpTH9y")
facet("rid", "iBtk3PJ7HS9JKLRu4PHa")
}
}
Means (tpe = PRO AND releaseStatus = 1) AND (uid = SelHOKTaw1k4WZpTH9y OR rid = iBtk3PJ7HS9JKLRu4PHa). The junction between all blocks in the filter block is always AND. And that aligns with the documentation.
My solution: Add another datapoint in search structure. I combined "tpe" and "releaseStatus" into a single attribute. tpeRe = PRO-1.
The resulting query that fits my original question:
filters {
orFacet {
facet("uid", userId)
facet("tpeRs", "PRO-1")
}
}

What is the proper way to use IF THEN in AQL?

I'm trying to use IF THEN style AQL, but the only relevant operator I could find in the AQL documentation was the ternary operator. I tried to add IF THEN syntax to my already working AQL but it gives syntax errors no matter what I try.
LET doc = DOCUMENT('xp/a-b')
LET now = DATE_NOW()
doc == null || now - doc.last >= 45e3 ?
LET mult = (doc == null || now - doc.last >= 6e5 ? 1 : doc.multiplier)
LET gained = FLOOR((RAND() * 3 + 3) * mult)
UPSERT {_key: 'a-b'}
INSERT {
amount: gained,
total: gained,
multiplier: 1.1,
last: now
}
UPDATE {
amount: doc.amount + gained,
total: doc.total + gained,
multiplier: (mult < 4 ? FLOOR((mult + 0.1) * 10) / 10 : 4),
last: now
}
IN xp
RETURN NEW
:
RETURN null
Gives the following error message:
stacktrace: ArangoError: AQL: syntax error, unexpected identifier near 'doc == null || now - doc.last >=...' at position 1:51 (while parsing)
The ternary operator can not be used like an if/else construct in the way to tried. It is for conditional (sub-)expressions like you use to calculate mult. It can not stand by itself, there is nothing it can be returned or assigned to if you write it like an if-expression.
Moreover, it would require braces, but the actual problem is that the body contains operations like LET, UPSERT and RETURN. These are language constructs which can not be used inside of expressions.
If I understand correctly, you want to:
insert a new document if no document with key a-b exists yet in collection xb
if it does exist, then update it, but only if the last update was 45 seconds or longer ago
Does the following query work for you?
FOR id IN [ 'xp/a-b' ]
LET doc = DOCUMENT(id)
LET key = PARSE_IDENTIFIER(id).key
LET now = DATE_NOW()
FILTER doc == null || now - doc.last >= 45e3
LET mult = (doc == null || now - doc.last >= 6e5 ? 1 : doc.multiplier)
LET gained = FLOOR((RAND() * 3 + 3) * mult)
UPSERT { _key: key }
INSERT {
_key: key,
amount: gained,
total: gained,
multiplier: 1.1,
last: now
}
UPDATE {
amount: doc.amount + gained,
total: doc.total + gained,
multiplier: (mult < 4 ? FLOOR((mult + 0.1) * 10) / 10 : 4),
last: now
}
IN xp
RETURN NEW
I added _key to INSERT, otherwise the document will get an auto-generated key, which does not seem intended. Using a FOR loop and a FILTER acts like an IF construct (without ELSE). Because this is a data modification query, it is not necessary to explicitly RETURN anything and in your original query you RETURN null for the ELSE case anyway. While yours would result in [ null ], mine produces [ ] (truly empty result) if you try execute the query in quick succession and nothing gets updated or inserted.
Note that it is necessary to use PARSE_IDENTIFIER() to get the key from the document ID string and do INSERT { _key: key }. With INSERT { _key: doc._key } you would run into an invalid document key error in the insert case, because if there is no document xp/a-b, DOCUMENT() returns null and doc._key is therefore also null, leading to _key: null - which is invalid.

What is the most optimized way to get a set of rows which is present in the middle of the list in java 8?

I've a list of items. I want to process a set of items which are in the middle of the list.
Ex: Assume a list of employees who have id, first name, last name and middle name as attributes.
I want to consider all rows between lastName "xxx" and "yyy" and process them further.
How can this be optimized in Java8? Optimization is my first concern.
Tried using Java8 streams and parallel streams. But termination(break) is not allowed in foreach loop in Java8 streams. Also we cannot use the outside("start" variable below) variables inside foreach.
Below is the code which I need to optimize:
boolean start = false;
for(Employee employee: employees) {
if(employee.getLastname().equals("yyy")) {
break;
}
if(start) {
// My code to process
}
if(employee.getLastname().equals("xxx")) {
start = true;
}
}
What is the best way to handle the above problem in Java8?
That is possible in java-9 via (I've simplified your example):
Stream.of(1, 2, 3, 4, 5, 6)
.dropWhile(x -> x != 2)
.takeWhile(x -> x != 6)
.skip(1)
.forEach(System.out::println);
This will get the values in the range 2 - 6, that is it will print 3,4,5.
Or for your example:
employees.stream()
.dropWhile(e -> e.getLastname().equals("xxx"))
.takeWhile(e -> e.getLastname().equals("yyy"))
.skip(1)
.forEach(....)
There are back-ports for dropWhile and takeWhile, see here and here
EDIT
Or you can get the indexes of those delimiters first and than do a subList (but this assumes that xxx and yyy are unique in the list of employees):
int[] indexes = IntStream.range(0, employees.size())
.filter(x -> list.get(x).getLastname().equals("xxx") || list.get(x).getLastname().equals("yyy"))
.toArray();
employees.subList(indexes[0] + 1, indexes[1])
.forEach(System.out::println);

Index of object in array found with match response[*]

Is it possible to get the index value of a
* match response.Services[*] contains { "Service": "xyz"}
I'm looking to reuse it later for more specific tests on the same Service object.
Theres reference to a __loop variable here but I guess I don't really understand how to apply it.
I love your questions, you are certainly pushing Karate to the limits and I'm having to think hard to answer these as well :)
The match does not allow you to get the "found index" or even the "loop position" which come to think of it - may be actually relevant for a match each - but I digress.
This will take a few extra lines of code, and I really think you need to upgrade to 0.8.0. If it helps, I can confirm that there are no breaking changes (unless you use the stand-alone JAR).
The new karate.forEach() and karate.match() functions allow you to do very complex operations on arrays. Here are 2 examples:
Scenario: karate find index of first match (primitive)
* def list = [1, 2, 3, 4]
* def searchFor = 3
* def foundAt = []
* def fun = function(x, i){ if (x == searchFor) foundAt.add(i) }
* eval karate.forEach(list, fun)
* match foundAt == [2]
Scenario: karate find index of first match (complex)
* def list = [{ a: 1, b: 'x'}, { a: 2, b: 'y'}, { a: 3, b: 'z'}]
* def searchFor = { a: 2, b: '#string'}
* def foundAt = []
* def fun = function(x, i){ if (karate.match(x, searchFor).pass) foundAt.add(i) }
* eval karate.forEach(list, fun)
* match foundAt == [1]
If you really can't upgrade, you can write a function to manually loop over the array and the above examples should get you a solution.

Is there an algorithm for encoding two text and the result will be the same even if change their position?

May be the question is hard to understand, I mean this
Given two sample text
Text1 = "abc" and Text2 = "def"
Which algorithm can do like
encoding(Text1, Text2) == encoding(Text2, Text1)
And I wish the result of the function is unique(not duplicate with encoding(Text3, Text1) like in another checksum algorithm too.
Actually, the root of this is I want to search in my database for the question is there any rows that "Who is a friends of B" or "B is a friends of whom" by searching only one column like
SELECT * FROM relationship WHERE hash = "a039813"
not
SELECT *
FROM relationship
WHERE (personColumn1 = "B" and verb = "friend") OR
(personColumn2 = "B" and verb = "friend")
You can adapt any encoding to ensure encoding(Text1, Text2) == encoding(Text2, Text1) by simply enforcing a particular ordering of the arguments. Since you're dealing with text, maybe use a basic lexical order:
encoding_adapter(t1, t2)
{
if (t1 < t2)
return encoding(t1, t2)
else
return encoding(t2, t1)
}
If you use a simple single-input hash function you're probably tempted to write:
encoding(t1, t2)
{
return hash(t1 + t2)
}
But this can cause collisions: encoding("AA", "B") == encoding("A", "AB"). There are a couple easy solutions:
if you have a character or string that never appears in your input strings then use it as a delimiter:
return hash(t1 + delimiter + t2)
hash the hashes:
return hash(hash(t1) + hash(t2))