Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A more functional way of including related documents #31

Closed
sheridanchris opened this issue Oct 19, 2022 · 3 comments · Fixed by #34
Closed

A more functional way of including related documents #31

sheridanchris opened this issue Oct 19, 2022 · 3 comments · Fixed by #34

Comments

@sheridanchris
Copy link
Contributor

sheridanchris commented Oct 19, 2022

As you can see here including related documents requires a separate mutable binding.

I wonder if there's a way to accomplish the same effect while giving the consumer a tuple value of 'entity * 'included
(or something similar)

@sheridanchris sheridanchris changed the title More functional Include's A more functional way of including related documents Oct 19, 2022
@sheridanchris
Copy link
Contributor Author

sheridanchris commented Oct 19, 2022

Maybe something like this?

let includeSingle<'entity, 'included> (selector: Quotations.Expr<'entity -> obj>) (q: IQueryable<'entity>) =
    let mutable single: 'included = Unchecked.defaultof<_>
    let q = q.Include(selector |> Lambda.ofArity1, (fun x -> single <- x))
    q, single // ???

// includeList
// includeDictionary

@TheAngryByrd
Copy link
Owner

I think that's fine. However you do have to force the query to start somehow (their example uses .Single() or this probably wont work.

If this is required for strictly building our a query pipeline, maybe TaskCompletionSource and TrySetResult make sense here?

@sheridanchris
Copy link
Contributor Author

sheridanchris commented Oct 19, 2022

My original idea was after you call includeSingle you would call q.Single yourself with a predicate.
Although my code snippet above wouldn't work well in a pipeline.
I'll have to think about it some more, thanks for the feedback.

EDIT:

Another option is to simply create wrapper functions for q.Include(...) so you can call it in a pipeline (with quotations)... while not wrapping the mutability and returning some composite value.

let includeSingle<'a, 'b> (selector: Quotations.Expr<'a -> obj>) (action: 'b -> unit) (q: IQueryable<'a>) =
    q.Include(selector |> Lambda.ofArity1, action)

let includeList<'a, 'b>
    (selector: Quotations.Expr<'a -> obj>)
    (seq: Collections.Generic.IList<'b>)
    (q: IQueryable<'a>)
    =
    q.Include(selector |> Lambda.ofArity1, seq)

let includeDict<'a, 'b, 'c>
    (selector: Quotations.Expr<'a -> obj>)
    (dict: Collections.Generic.Dictionary<'b, 'c>)
    (q: IQueryable<'a>)
    =
    q.Include(selector |> Lambda.ofArity1, dict)

// example (not tested)
let authors = Collections.Generic.Dictionary<Guid, Author>()

let! books =
  session
  |> Session.query
  |> includeDict (<@ fun book -> book.AuthorId @>) authors
  |> orderByDescending <@ fun book -> book.Published @>
  |> toListTask

A pretty wild option would be to have a continueWith HOF that creates a composite of the include and the result of the continuation.

let includeSingle<'a, 'b, 'c>
    (selector: Quotations.Expr<'a -> obj>)
    (continueWith: IQueryable<'a> -> 'c)
    (q: IQueryable<'a>)
    =
    let mutable value: 'b = Unchecked.defaultof<_>
    let q = q.Include(selector |> Lambda.ofArity1, (fun x -> value <- x))
    let result = continueWith q
    result, value

// example (not tested)
let queryPost queryable = 
    queryable |> Queryable.exactlyOne 

let (post, author) : Post * Author =
  session.Query<Post>
  |> includeSingle <@ fun post -> post.AuthorId @> queryPost

^^ The ergonomics of continueWith is rough especially when you consider multiple includes.
I think I'll go with the top version instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants