Wolfgang-Schuetzelhofer / jcypher

Java access to Neo4J graph databases at multiple levels of abstraction
Apache License 2.0
86 stars 15 forks source link

Is it possible to retrieve relationship types from collection? #44

Open DonCziken opened 6 years ago

DonCziken commented 6 years ago

Hi Wolfgang,

say I'd like to do following:

        JcPath p;
        [..]
        WHERE.valueOf(p.relations().head().type()).EQUALS("someType")

but the problem is that p.relations().head() returns ValueElementwhich I can't find a way to convert to Relation using available API. Another example of such a case maybe following:

MATCH.node(n0).label("JcTestPerson")
                .relation(r0).out().minHops(0)
                .node(n1),
            RETURN.value(n0.name)
            RETURN.value((r[i - 1].asCollection().head().type()).as(new JcString("type"))
            RETURN.value(n1.name)

so Question to you Wolfgang - is it possible to retrieve type from relation picked from JcCollection? If so how can I do that? Just FYI in mean time I had prepared a workaround for above doing following, but its very dirty :)

public class JcStringExtension {

    public static JcString create(Object val, ValueElement predecessor, IOperatorOrFunction opf) {
        try {
            Constructor<?> constructor  = JcString.class.getDeclaredConstructor(Object.class,
                ValueElement.class, IOperatorOrFunction.class);
            constructor.setAccessible(true);
            return (JcString) constructor.newInstance(val, predecessor, opf);
        } catch (NoSuchMethodException | InstantiationException
            | IllegalAccessException | InvocationTargetException e) {
            EigenLog.errorLog(e.getMessage());
            throw new DomainApiException("Internal error - please check logs");
        }
    }

}

public class FunctionInstanceExtension {

    public static FunctionInstance create(Function function, int numArgs) {

        try {
            Constructor<?> constructor = FunctionInstance.class
                .getDeclaredConstructor(Function.class,
                    int.class);
            constructor.setAccessible(true);
            return (FunctionInstance) constructor.newInstance(function, numArgs);
        } catch (NoSuchMethodException | InstantiationException
            | IllegalAccessException | InvocationTargetException e) {
            EigenLog.errorLog(e.getMessage());
            throw new DomainApiException("Internal error - please check logs");
        }
    }

}

//usage:
    @SuppressWarnings("unchecked")
    private JcString typeOfValueElement(ValueElement v) {
        return JcStringExtension.create(null, v,
            FunctionInstanceExtension.create(FUNCTION.Relation.TYPE, 1));
    }

RETURN.value(typeOfValueElement((r[i - 1].asCollection().head())))
                .AS(r[i - 1])

cheers, Krzysztof

Wolfgang-Schuetzelhofer commented 6 years ago

Hi Krzysztov,

I see your problem. Could you send me a CYPHER expression that would do the job? Then I will try to find a solution.

Best regards, Wolfgang ,

On Tue, Sep 4, 2018 at 1:34 AM Krzysztof Gasior notifications@github.com wrote:

say I'd like to do following:

    JcPath p;
    [..]
    WHERE.valueOf(p.relations().head().type()).EQUALS("someType")

but the problem is that p.relations().head() returns ValueElementwhich I can't find a way to convert to Relation using available API. Another example of usch a case maybe following:

MATCH.node(n0).label("JcTestPerson") .relation(r0).out().minHops(0) .node(n1), RETURN.value(n0.name) RETURN.value((r[i - 1].asCollection().head().type()).as(new JcString("type")) RETURN.value(n1.name)

so Question to you Wolfgang - is it possible to retrieve type from relation picked from JcCollection? If so how can I do that? Just FYI in mean time I had prepared a workaround for above doing following, but its very dirty :)

public class JcStringExtension {

public static JcString create(Object val, ValueElement predecessor, IOperatorOrFunction opf) {
    try {
        Constructor<?> constructor  = JcString.class.getDeclaredConstructor(Object.class,
            ValueElement.class, IOperatorOrFunction.class);
        constructor.setAccessible(true);
        return (JcString) constructor.newInstance(val, predecessor, opf);
    } catch (NoSuchMethodException | InstantiationException
        | IllegalAccessException | InvocationTargetException e) {
        EigenLog.errorLog(e.getMessage());
        throw new DomainApiException("Internal error - please check logs");
    }
}

}

public class FunctionInstanceExtension {

public static FunctionInstance create(Function function, int numArgs) {

    try {
        Constructor<?> constructor = FunctionInstance.class
            .getDeclaredConstructor(Function.class,
                int.class);
        constructor.setAccessible(true);
        return (FunctionInstance) constructor.newInstance(function, numArgs);
    } catch (NoSuchMethodException | InstantiationException
        | IllegalAccessException | InvocationTargetException e) {
        EigenLog.errorLog(e.getMessage());
        throw new DomainApiException("Internal error - please check logs");
    }
}

}

//usage: @SuppressWarnings("unchecked") private JcString typeOfValueElement(ValueElement v) { return JcStringExtension.create(null, v, FunctionInstanceExtension.create(FUNCTION.Relation.TYPE, 1)); }

RETURN.value(typeOfValueElement((r[i - 1].asCollection().head()))) .AS(r[i - 1])

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Wolfgang-Schuetzelhofer/jcypher/issues/44, or mute the thread https://github.com/notifications/unsubscribe-auth/AHI-33d4lrfbP4-LjKIo7WwqHmIO1m_Uks5uXbyJgaJpZM4WYHRA .

DonCziken commented 6 years ago

Hi Wolfgang,

i.e.

MATCH (n0)-[r0*0..1]->(n1)-[r1*0..1]->(n2) 
WHERE id(n0)=560725 
RETURN DISTINCT id(n0), n0.name, type(r0[0]), id(n1), n1.name, type(r1[0]), id(n2), n2.name

note: type(r0[0]) and type(r1[0]) in RETURN statement

Wolfgang-Schuetzelhofer commented 6 years ago

Hi Krzysztov,

the quick solution: For cypher statements which are not or not yet supported in JCypher, you can always use JCypher's NATIVE clause. By means of the NATIVE clause you can write parts of a query or even an entire query in cypher. For your example please see below:

clauses = new IClause[]{
  MATCH.node(n0).relation(r0).minHops(0).maxHops(1).out()
    .node(n1).relation(r1).minHops(0).maxHops(1).out().node(n2),
  WHERE.valueOf(n0.id()).EQUALS(560725),
  NATIVE.cypher("RETURN DISTINCT id(n0), n0.name, type(r0[0]), id(n1), n1.name, type(r1[0]), id(n2), n2.name")
};

This leads to the cypher query:

MATCH (n0)-[r0*0..1]->(n1)-[r1*0..1]->(n2)
WHERE id(n0) = 560725
RETURN DISTINCT id(n0), n0.name, type(r0[0]), id(n1), n1.name, type(r1[0]), id(n2), n2.name

I will extend JCypher' API to support your scenario with the next release. Sorry if I take some time to answer your mails, that is because I am currently engaged in a customer project which takes almost all of my time. So currently I don't have much time to work on JCypher.

Best regards, Wolfgang

DonCziken commented 6 years ago

Hi Wolfgang,

thanks for reply, its good to know that it is possible to combine Query DSL with NATIVE.cypher this would help in such cases :).

Let me know when you got a fix for that within DSL, and for the moment I i will stick with my dirty hack using JcStringExtension and FunctionInstanceExtension.

cheers, Krzysztof

Wolfgang-Schuetzelhofer commented 5 years ago

Hi Krzysztov, JCypher 4.1.0 is released, now collections in the Query API are aware of their component type. You should now be able to do what you were looking for:

    JcPath p;
    [..]
    WHERE.valueOf(p.relations().head().type()).EQUALS("someType")

cheers, Wolfgang

DonCziken commented 5 years ago

Hi Wolfgang,

much appreciate!

cheers, Krzysztof