Skip to content

Instantly share code, notes, and snippets.

@OkayX6
Last active January 23, 2016 16:08
Show Gist options
  • Select an option

  • Save OkayX6/c89c2c499940d99d2767 to your computer and use it in GitHub Desktop.

Select an option

Save OkayX6/c89c2c499940d99d2767 to your computer and use it in GitHub Desktop.
Neo4j simplified query AST in F# - Prototype - Non compileable!!
open Neo4jClient
let client = new GraphClient(Uri("http://localhost:7474/db/data"), "login", "password")
client.Connect()
// Using Neo4jKey attribute to identify Node "primary key"
let getUserNodeUsingPredefinedKey (id: string) =
let userVar = Var.Make<UserNode>(Some "user", id) // This is how to define it
let query =
ReadQuery.Create(
// Matches
MatchClause.Create([Node userVar]),
// Optional Matches
MatchClause.Create([]),
// Return expr
<@ fun (user: ICypherResultItem) -> user.As<UserNode>() @>)
client.Cypher
|> ReadQuery.Execute query
|> Seq.item 0
let getUserNodeUsingWhereClause (id: string) =
let userVar = Var.Make<UserNode>("user")
let query =
ReadQuery.Create(
// Matches
MatchClause.Create(
[Node userVar],
<@ fun (user: UserNode) -> user.FacebookId = id @>), // WHERE clause of the MATCH
// Optional Matches
MatchClause.Create([]),
// Return expr
<@ fun (user: ICypherResultItem) -> user.As<UserNode>() @>)
client.Cypher
|> ReadQuery.Execute query
|> Seq.item 0
type Neo4jKeyAttribute() =
inherit Attribute()
[<CLIMutable>]
type UserNode =
{ [<Neo4jKey>] FacebookId : string }
type Var =
{ Name: Option<string>
Type: Option<Type>
Key: Option<string * obj> }
static member Make<'T>() =
{ Name = None
Type = Some typeof<'T>
Key = None }
static member Make<'T>(name: string) =
{ Name = Some name
Type = Some typeof<'T>
Key = None }
static member Make<'T>(name: Option<string>, keyValue: obj) =
{ Name = name
Type = Some typeof<'T>
Key = Some (Cypher.getNeo4jKeyName(typeof<'T>), keyValue) }
member x.GenerateMatchExpr() =
[|
x.Name |> Option.defaultOrGet ""
x.Type |> Option.defaultOrApply (fun typ -> (+) ":" typ.Name) ""
x.Key |> Option.defaultOrApply (fun (name, value) -> sprintf " {%s:%A}" name value) ""
|]
|> String.concat ""
type GraphElt =
| Node of node:Var
| RelToRight of src:Var * relation:Var * dest:Var
| RelToLeft of dest:Var * relation:Var * src:Var
member x.GenerateMatchExpr() =
match x with
| Node v -> sprintf "(%s)" (v.GenerateMatchExpr())
| RelToRight(v1, rel, v2) ->
sprintf "(%s)-[%s]->(%s)" (v1.GenerateMatchExpr()) (rel.GenerateMatchExpr()) (v2.GenerateMatchExpr())
| RelToLeft(v1, rel, v2) ->
sprintf "(%s)<-[%s]-(%s)" (v1.GenerateMatchExpr()) (rel.GenerateMatchExpr()) (v2.GenerateMatchExpr())
type MatchClause private
(matches: GraphElt list, whereGenerator: (ICypherFluentQuery -> ICypherFluentQuery) option) =
member val Matches = matches
member val WhereGenerator = whereGenerator
static member Create(matches: GraphElt list) =
MatchClause(matches, None)
static member Create(matches: GraphElt list, whereMatches: Expr<'T1 -> bool>) =
MatchClause(matches, Some (fun q -> q.Where(toLambdaExpression<Func<'T1,_>> whereMatches)))
static member Create(matches: GraphElt list, whereMatches: Expr<'T1 -> 'T2 -> bool>) =
MatchClause(matches, Some (fun q -> q.Where(toLambdaExpression<Func<'T1,'T2,_>> whereMatches)))
static member Create(matches: GraphElt list, whereMatches: Expr<'T1 -> 'T2 -> 'T3 -> bool>) =
MatchClause(matches, Some (fun q -> q.Where(toLambdaExpression<Func<'T1,'T2,'T3,_>> whereMatches)))
static member Create(matches: GraphElt list, whereMatches: Expr<'T1 -> 'T2 -> 'T3 -> 'T4 -> bool>) =
MatchClause(matches, Some (fun q -> q.Where(toLambdaExpression<Func<'T1,'T2,'T3,'T4,_>> whereMatches)))
type ReadQuery<'TRes> =
// [MATCH WHERE]
// [OPTIONAL MATCH WHERE]
// [WITH [ORDER BY] [SKIP] [LIMIT]]
// RETURN [ORDER BY] [SKIP] [LIMIT]
private Query of
matchClause: MatchClause *
optionalMatchClause: MatchClause *
returnExpr: Expr
with
static member Create(matchClause, optMatchClause, returnExpr: Expr<ICypherResultItem -> 'TRes>) =
ReadQuery<'TRes>.Query(matchClause, optMatchClause, returnExpr)
static member Create(matchClause, optMatchClause, returnExpr: Expr<ICypherResultItem -> ICypherResultItem -> 'TRes>) =
ReadQuery<'TRes>.Query(matchClause, optMatchClause, returnExpr)
static member Create(matchClause, optMatchClause, returnExpr: Expr<ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> 'TRes>) =
ReadQuery<'TRes>.Query(matchClause, optMatchClause, returnExpr)
static member Create(matchClause, optMatchClause, returnExpr: Expr<ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> 'TRes>) =
ReadQuery<'TRes>.Query(matchClause, optMatchClause, returnExpr)
static member Create(matchClause, optMatchClause, returnExpr: Expr<ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> ICypherResultItem -> 'TRes>) =
ReadQuery<'TRes>.Query(matchClause, optMatchClause, returnExpr)
static member Execute (query: ReadQuery<'TRes>) (cypher: ICypherFluentQuery) =
let (Query(matchClause, optMatchClause, returnExpr)) = query
let argNames = getReturnExprArgNames returnExpr
let argCount = argNames.Length
let generateReturnExpr = (returnOverloads<'TRes>()).[argCount]
let allMatchVarNames: Set<string> =
Set.union
(getAllMatchVarNames matchClause)
(getAllMatchVarNames optMatchClause)
if not <| Set.isSubset (Set.ofArray argNames) allMatchVarNames then
invalidArg "query" "The return expression contains variables that are not in your match clause"
cypher
|> generateMatchExpr matchClause
|> generateWhereExpr matchClause
|> generateMatchExpr optMatchClause
|> generateWhereExpr optMatchClause
|> generateReturnExpr returnExpr
|> Cypher.getResults
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment