6

Given this query (from here)

  let pAfollowers =
        client.Cypher
            .Match("n<-[:follows]-e")
            .Where(fun n -> n.Twitter = "tA")
            .Return<Person>("e")
            .Results
            .Select(fun x -> x.Name)

I would like to tweak it and have it return multiple values packaged together. Not sure about how the type should look:

let pAfollowers =
        client.Cypher
            .Match("n<-[r:follows]-e")
            .Where(fun n -> n.Twitter = "tA")
            .Return<???>("n, r, e")

Secondly I was wondering if it is possible to have a return statement after a CreateUnique. I am trying to tweak this query:

let knows target (details : Knows) source =
        client.Cypher
            .Match("(s:Person)", "(t:Person)")
            .Where(fun s -> s.Twitter = source.Twitter)
            .AndWhere(fun t -> t.Twitter = target.Twitter)
            .CreateUnique("s-[:knows {knowsData}]->t")
            .WithParam("knowsData", details)
            .ExecuteWithoutResults()

to have it return s, t and the details.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
NoIdeaHowToFixThis
  • 4,484
  • 2
  • 34
  • 69

2 Answers2

3

OK, good news / bad news - though in practice the good is tempered with bad :(

Good first:

You can return after a CreateUnique, something like:

.CreateUnique("s-[:Knows {knowsData}]-t")
.WithParam("knowsData", details)
.Returns<???>( "s,t" )
.Results;

Bad news:

The bad news is that you probably can't do it in F#. Neo4jClient requires you to use either object initializers, or anonymous types to cast the data, so you could try something like:

type FollowingResults = { Follower : Person; Followed : Person;}

let createExpression quotationExpression = LeafExpressionConverter.QuotationToLambdaExpression quotationExpression

let pAfollowers =
    client.Cypher
        .Match("n<-[:follows]-e")
        .Where(fun n -> n.Twitter = "tA")
        .Return(createExpression <@ Func<ICypherResultItem, ICypherResultItem, FollowingResults>(fun (e : Cypher.ICypherResultItem) (n : Cypher.ICypherResultItem) -> {Follower = e.As<Person>(); Followed = n.As<Person>()}) @>)
        .Results
        .Select(fun x -> x)

for follower in pAfollowers do
    printfn "%s followed %s" follower.Follower.Name follower.Followed.Name

For which the F# compiler will have no problems at all. However, Neo4jClient will throw an Argument exception with the following message:

The expression must be constructed as either an object initializer (for example: n => new MyResultType { Foo = n.Bar }), an anonymous type initializer (for example: n => new { Foo = n.Bar }), a method call (for example: n => n.Count()), or a member accessor (for example: n => n.As().Bar). You cannot supply blocks of code (for example: n => { var a = n + 1; return a; }) or use constructors with arguments (for example: n => new Foo(n)).

The problem being, F# doesn't have object initializers, nor anonymous types, you can wrangle with the F# stuff for ages and not get anywhere, as the C# doesn't recognize the F# initialization.

Charlotte Skardon
  • 6,220
  • 2
  • 31
  • 42
  • "*F# doesn't have object initializers*" If I understand what you mean correctly, then it indeed does. See "Assigning Values to Properties at Initialization" [here](http://msdn.microsoft.com/en-us/library/dd233192.aspx). – ildjarn Apr 01 '14 at 17:38
  • I have to say that I am very confused. _In F# 3.0, we’ve added CLIMutableAttribute. If you attach this attribute to an F# record type, ... , then the F# compiler emits a default constructor and property setters into the generated IL for this type (though those features are not exposed to F# code)_ [link](http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx) – NoIdeaHowToFixThis Apr 02 '14 at 07:31
  • 1
    @ildjarn & NoIdeaHowToFixThis - I mean as far as the C# code (in the neo4jclient library) is concerned - this isn't an object initializer (I'm no F# developer really - so more than happy (in fact very happy) to be proved wrong on this). The problem is how the C# library reads the code. It is equivalent in function, but *not* the same when reflecting over the type - which is what the client does. – Charlotte Skardon Apr 02 '14 at 07:44
  • OK, thank. I see. I can't find a workaround. This issue is for me a show stopper. Wondering if I should close this question and open a more generic question on the use of `LeafExpressionConverter.QuotationToLambdaExpression` in a similar scenario. – NoIdeaHowToFixThis Apr 02 '14 at 10:47
  • I can't add much more - I've tried with a struct, F#'s initializers etc and no joy at my end - as I said - I'm not strong with F# so you could hold and see if an F# guru can help - remember though - if you ask the question with the idea of using it in neo4jclient you need to specify it will be consumed by a C# library, this Gist: https://gist.github.com/cskardon/9935002 should help - if you can get F# to call this and return true you *should* be good. – Charlotte Skardon Apr 02 '14 at 14:15
1

I have somewhat good news to both. This code will compile just fine using tuples and can be used with a modified Neo4jClient that supports F# Tuples: https://github.com/n074v41l4bl34u/Neo4jClient Solution is based on: https://fsharppowerpack.codeplex.com/workitem/4572

  let knows target (details : Knows) source =
    client.Cypher
      .Match("(s:Person)", "(t:Person)")
      .Where(fun s -> s.Twitter = source.Twitter)
      .AndWhere(fun t -> t.Twitter = target.Twitter)
      .CreateUnique("s-[:knows {knowsData}]->t")
      .WithParam("knowsData", details)
      .Return(fun s t -> s.As<Person>(),t.As<Person>())

  let pAfollowers =
    client.Cypher
      .Match("n<-[:follows]-e")
      .Where(fun n -> n.Twitter = "tA")
      .Return(fun (e : Cypher.ICypherResultItem) n -> e.As<Person>().Name,n.As<Person>().Name)

The type annotation on '(e : Cypher.ICypherResultItem)' can be omited when using more than one argument in fun.

However, when using a single argument, this gets rid off the ugly createExpression <@ Func(...) @>) syntax. For details on why look on the bottom of this page: https://gist.github.com/cskardon/8300420

TermoTux
  • 588
  • 4
  • 17
  • Has this ever been migrated to the master and is available in the package? – Tomas Jansson Jan 21 '16 at 14:52
  • According to https://github.com/Readify/Neo4jClient/commit/ca62689faf6aa962c7d6a6e6c9d323b7b1ef6a8d it was pulled back into master branch on May 26, 2014 so it should be available. – TermoTux Feb 04 '16 at 17:56