How to query RavenDB using complex query in F# - f#

I am using RavenDB 2.0 unstable.
Is it possible to query RavenDB 2.0 with a lambda or similar technique (linq?) from F#?
Here is intent example code. I am attempting to get all the posts in last 72 hours. Its wrong, but it gets the idea across.
static member public GetTopPosts _ =
use store = (new DocumentStore(Url = Docs.serverUrl, DefaultDatabase = Docs.dbName )).Initialize()
use session = store.OpenSession()
let result = session.Query<Post>().Where(fun x -> x.Date > DateTime.Now - new TimeSpan(72,0,0))
...
How can something like this be accomplished?

Yes you can do this.
You can find an example of this in FAKE
In your case your query would look something like this
let getTopPostsAsOf date =
use session = docStore.OpenSession()
query {
for post in session.Query<Post>() do
where (post.Date > date)
select post
} |> Seq.toArray
this would then be called with
let result = getTopPostsAsOf (DateTime.Now - TimeSpan.FromHours(72))
A couple of things to note.
The DocumentStore should be created once and only once at the startup of the application.
Also typically only a small set of library calls can be used within the query expressions, hence why I pass in the already computed date. The reason for this is because these expression trees end up getting serialized and executed within raven db engine.

Related

How do I get an F# fsx script to re-execute and re-pull SQL data each time it's called from C#?

I have written a simple C# web app that allows the user to input some data and then upon button click:
the data is saved to a local SQL db
an F# script is called to retrieve that data using SqlCommandProvider
that data is passed back to C#
the passed back data is used in some calculations
the calc results are displayed onto the screen of the web page
Everything works fine except that when the button is clicked more than once, the same data is sent back from F# from the very first execution.
It appears that the script is not re-executing as would be expected, but if it is, it seems that the SqlCommandProvider might be locked to the first set of results that it initially returned.
It is obviously unnecessary to send the data to the db and back in order to perform these calcs. This app is being built for demonstration purposes of F#/C# usage together in a solution, not actual efficient production usage of the app.
#I "../packages/FSharp.Data.3.0.0/lib/net45"
#r "FSharp.Data.dll"
open FSharp.Data
#I "../packages/FSharp.Data.SqlClient.2.0.1/lib/net40"
#r "FSharp.Data.SqlClient.dll"
open FSharp.Data.SqlClient
[<Literal>]
let ConnectionString =
#"server=(local); database=CostPriceCalc; user id=MyId; password=MyPassword;"
[<Literal>]
let SqlQuery = "SELECT SharesSold, PricePerShare, SellDate, CostMethod FROM [CalcInputs] WHERE Id = (SELECT MAX(Id) FROM [CalcInputs])"
let cmd = new SqlCommandProvider<SqlQuery, ConnectionString>(ConnectionString)
let result = cmd.Execute() |> Seq.toArray
Additionally, I am not yet handling the data properly coming back from F#, instead I'm very inelegantly just converting to string and parsing through it to get what I need. That however, seems irrelevant (other than it's massively incorrect) because the C# variables are clear each time before pulling from F# and the same data just keeps coming back each time from F#.
public static List<NewData.Values> ParseFsharpData()
{
var parsedList = new List<NewData.Values>();
foreach (var item in CalculateCostPrice.result)
{
var parsedData = item.ToString().Replace(";", ",").
Replace("{ ", "").Replace(" }", "").Replace("Some ", "").
Replace("M,", ",").Replace("\"", "").
Split(',').ToList();
parsedList = (from value in parsedData
select new NewData.Values
{
Value1 = value.Split('=')[0].Trim(),
Value2 = value.Split('=')[1].Trim()
}).ToList();
}
return parsedList;
}
Lastly, I have confirmed that the new data IS being written correctly to the db. The issue seems confined to either the F# fsx script itself (named CalculateCostPrice) not re-executing as expected, OR the SqlCommandProvider caching the data.
I haven't tried to compile .fsx scripts yet, but my experience with using modules in F# projects makes me think that:
let result = cmd.Execute() |> Seq.toArray
compiles to a static variable on the CalculateCostPrice class. This would mean it'll only get executed once (when it's first used, if not earlier), and the result would be stored in the "result" variable.
Adding a parameter of type "unit" should change it to a method (again, not tested yet):
let result() = cmd.Execute() |> Seq.toArray
And you would call it from C# as:
foreach (var item in CalculateCostPrice.result())
In this case, the execution will happen when you call the method, not when the class gets initialized. I might rename it from "result" to "executeQuery" or something along those lines.

Dynamic Cypher Search Query

I know this may sound nonsense but I will try my best to explain the problem that I am facing with neo4jClient.
I developing a Social TaskManagment Software. and for the server side code and data Storage I choose to have neo4j (neo4jClient) and C# in place.
in Our software Imagine a User : (mhs) Post a Task of "#somebody please help me on #Neo4j #cypher" the task would be decorated with some fancy character including (# # + /) the obove post will be save in neo4j as graph like this :
(post:Post {Text : #somebody please help me on #Neo4j})-[Has_HashTag]-(neo4j:HashTag)
(post:Post)-[Has_HashTag]-(Cypher: HashTag)
(post:Post)-[Has_Author)-[mhs:User]
(post:Post)-[Has_MentionedUser)-[Somebody:User]
Now Imagine User #mhs is trying to search the Post that Have HashTag of #Neo4j and mentioned to #somebody
Here I am building (hand Craft) a Cypher Query Including the 2 seach Paramerts in Cypher with Some Fancy C# code which result the blow Cypher Query:
MATCH (nodes)-[r]-(post:Post),
(post:Post)-[:HAS_MentionedUsers]->(assignee1307989068:User),
(nodes)-[r]-(post:Post)-[:HAS_HashTags]->(Hashtag1482870844:HashTag)
WHERE (assignee1307989068.UserName = "somebody") AND (Hashtag1482870844.Value = "neo4j")
RETURN post AS Post, Collect(nodes) as nodes
ORDER BY post.creationDate
the above cypher will return a post with just all the nodes of the post that is not included in Where clause.
my question is how to include all the Nodes related to the Targeted (post) without including them in Return part of the cypher. Something like return (*).
The Other problem is How can I deserialize the result set into C# without knowing what shape it may have.
The Search method that I am producing the mentioned Cypher is as fallow:
public List<PostNode> Search(string searchterm)
{
List<string> where = new List<string>();
var tokenizedstring = searchterm.Split(' ');
var querystring = new StringBuilder();
var relatedNodes = new List<string>();
var q = new CypherFluentQuery(_graphClient) as ICypherFluentQuery;
foreach (var t in tokenizedstring)
{
_commandService.BuildPostQueystring(t, ref querystring, ref where, ref relatedNodes);
}
if (querystring[querystring.Length - 1] == ',')
querystring = querystring.Remove(querystring.Length - 1, 1);
q = q.Match(querystring.ToString());
int i = 1;
if (where.Count > 0)
q = q.Where(where[0]);
while (i < where.Count)
{
q = q.AndWhere(where[i]);
i++;
}
var rq = q.Return(
(post, nodes) => new PostNode
{
Post = post.As<Node<string>>(),
Nodes = nodes.CollectAs<string>()
})
.OrderBy("post.creationDate");
var results = rq.Results.ToList();
//foreach (var result in results)
//{
// //dynamic p = JsonConvert.DeserializeObject<dynamic>(result.Post.Data);
// //dynamic d = JsonConvert.DeserializeObject<dynamic>(result.Nodes.Data);
//}
return results;
}
}
//Some Helper Class just to cast the result.
public class PostNode
{
public Node<string> Post { get; set; }
public IEnumerable<Node<string>> Nodes { get; set; }
}
But as you may noticed it does not have the nodes that is included in the search term via Where Clause in Cypher Query.
I am really stopped here for a while, as I can not provide any decent solution for this. so your help may save me a lot.
may be i am totally in a wrong direction so please help in any way you can think of.
It appears that a list of UNKNOWN related nodes are being provisioned, so one of this:
Scenario A
It doesn't matter what EXACTLY those related nodes are, I just want them.
Question: What is intention of retrieving those unknown but related nodes? By answering this chances are this query could be improved for good.
Scenario B
These unknown related nodes are actually known! It's just they are not fully known at time of query execution however down the road somewhere in C# code we will have things like this
if (nodes.Any(_ => _ is HashTag) {...}
Question:
This type of behaviour requires to KNOW the type. Even with reflection or C# dynamic stuff that requirement is still there because Neo4jClient has no way of correlating a bag of JSON data coming from Neo4j into any local type. When Neo4jClient receives bulk of data over wire somehow it should know what type would YOU prefer to represent. This is why queries are always like this:
Return((a, p) => new
{
Author = a.As<Author>(), //we expect node content to be represented as Author
Post = p.As<Post>()
})
Neo4jClient does NOT preserve C# types inside your Neo4j database. It would have been nasty to do so. However, idea behind it is that you shouldn't find yourself desperate for it and if you do so then I would recommend looking for problem somewhere else i.e. why would you rely on your client library to describe your domain for you?

FSharp.Data.JsonProvider - Getting json from types

The FSharp.Data.JsonProvider provides a means to go from json to an F# type. Is it possible to go in the reverse direction i.e. declare an instance of one of the types created by FSharp.Data.JsonProvider, set the field values to be what I need, and then get the equivalent json?
I've tried things like this,
type Simple = JsonProvider<""" { "name":"John", "age":94 } """>
let fred = Simple(
Age = 5, // no argument or settable property 'Age'
Name = "Fred")
The latest version of F# Data now supports this. See the last example in http://fsharp.github.io/FSharp.Data/library/JsonProvider.html.
Your example would be:
type Simple = JsonProvider<""" { "name":"John", "age":94 } """>
let fred = Simple.Root(age = 5, name = "Fred")
This is one area where C# has an edge over F#, at least in Visual Studio. You can copy your JSON example code into the clipboard and in Visual Studio use the Edit -> Paste Special -> Paste JSON As Classes and it will create a class to match the JSON example. From there you can easily use the class in F#.
More details on paste special here
Hopefully a matching feature will come for F# soon too.

Getting only what is needed with Entity Framework

With a plain connection to SQL Server, you can specify what columns to return in a simple SELECT statement.
With EF:
Dim who = context.Doctors.Find(3) ' Primary key is an integer
The above returns all data that entity has... BUT... I would only like to do what you can with SQL and get only what I need.
Doing this:
Dim who= (From d In contect.Doctors
Where d.Regeneration = 3
Select New Doctor With {.Actor = d.Actor}).Single
Gives me this error:
The entity or complex type XXXXX cannot be constructed in a LINQ to Entities query.
So... How do I return only selected data from only one entity?
Basically, I'm not sure why, but Linq can't create the complex type. It would work if you were creating a anonymous type like (sorry c# code)
var who = (from x in contect.Doctors
where x.Regeneration == 3
select new { Actor = x.Actor }).Single();
you can then go
var doctor = new Doctor() {
Actor = who.Actor
};
but it can't build it as a strongly typed or complex type like you're trying to do with
var who = (from x in contect.Doctors
where x.Regeneration == 3
select new Doctor { Actor = x.Actor }).Single();
also you may want to be careful with the use of single, if there is no doctor with the regeneration number or there are more than one it will throw a exception, singleordefault is safer but it will throw a exception if there is more than one match. First or Firstordefault are much better options First will throw a exception only if none exist and Firstordefault can handle pretty much anything
The best way to do this is by setting the wanted properties in ViewModel "or DTO if you're dealing with upper levels"
Then as your example the ViewModel will be:
public class DoctorViewModel{
public string Actor {get;set;}
// You can add as many properties as you want
}
then the query will be:
var who = (From d In contect.Doctors
Where d.Regeneration = 3
Select New DoctorViewModel {Actor = d.Actor}).Single();
Sorry i wrote the code with C# but i think the idea is clear :)
You can just simply do this:
Dim who= (From d In contect.Doctors
Where d.Regeneration = 3
Select d.Actor).Single
Try this
Dim who = contect.Doctors.SingleOrDefault(Function(d) d.Regeneration = 3).Actor

how do i create a computational expression that takes parameters?

I want to create a couple of computational expressions that would be used to access the database and return a list of items like so (I also have questions in the code comments):
let foo x y z = proc "foo" {
let! cmd = proc.CreateCommand() // can I do this?
do! In "x" DbType.Int32 // would i gain anything by replacing DbType with a union
// type since the names would match actual data types?
do! In "y" DbType.String 15;
cmd?x <- x
cmd?y <- y
use! r = cmd.ExecuteReader() // would this be bad form for creating a workflow builder?
return! r {
let item = MyItem()
do! item.a <- r.GetInt32("a")
do! item.a <- r.GetString("b")
do! item.c <- r.GetDateTime("c")
yield! item
}
}
How can I create a workflow builder such that an instance of it takes a parameter?
let proc name = ProcedureBuilder(connStr, factory) // how do I do this?
Yes, you can do this. You can use computation expression syntax after any expression with a type statically known to expose the right methods. So the following code works (but doesn't do anything particularly interesting):
let f x = async
let v = f "test" { return 1 }
Here, f has type 'a -> AsyncBuilder, so f "test" has type AsyncBuilder and can be followed with computation expression syntax. Your example of let proc name = ProcedureBuilder(connStr, factory) is perfectly fine, assuming that ProcedureBuilder is defined appropriately, though you presumably want name to appear somewhere in the constructor arguments.
The answer from Keith (kvb) is correct - you can use parameterized computation builders. The syntax of computation expressions is:
<expr> { <cexpr> }
So, the builder can be created by any expression. Usually, it is some value (e.g. async) but it can be a function call or even a constructor call. When using this, you would typically define a parameterized builder and then pass the argument to a constructor using a function (as #kvb suggests).
I actually wrote an example of this, not a long time ago, so I can share an example where - I think - this is quite useful. You can find it on F# snippets: http://fssnip.net/4z
The example creates a "special" asynchronous computation builder for ASP.NET MVC that behaves just like standard async. The only difference is that it adds Run member that uses AsyncManager (provided by ASP.NET) to execute the workflow.
Here are some relevant parts from the snippet:
/// A computation builder that is almost the same as stnadard F# 'async'.
/// The differnece is that it takes an ASP.NET MVC 'AsyncManager' as an
/// argumnet and implements 'Run' opration, so that the workflow is
/// automatically executed after it is created (using the AsyncManager)
type AsyncActionBuilder(asyncMgr:Async.AsyncManager) =
// (Omitted: Lots of boilerplate code)
/// Run the workflow automatically using ASP.NET AsyncManager
member x.Run(workflow) =
// Use 'asyncMgr' to execute the 'workflow'
The snippet wraps the construction in a base class, but you could define a function:
let asyncAction mgr = new AsyncActionBuilder(mgr)
And then use it to define asynchronous action in ASP.NET MVC:
member x.LengthAsync(url:string) = asyncAction x.AsyncManager {
let wc = new WebClient()
let! html = wc.AsyncDownloadString(url)
return html.Length }

Resources