is there a way to distinguish between a nil because of (optional ...) and an explicit nil?
By joining to a statement that has that nil
There’s no way in a filter though
One day. I need to filter on patterns soon because I need an “exists” operation for SPARQL
i'm somehow even more confused.. an example:
(d/transact conn {:tx-triples [[1 :foo 1]
[1 :bar nil]
[2 :foo 2]]})
(d/q '[:find ?foo ?bar
:where
(optional [?e :foo ?foo])
(optional [?e :bar ?bar])]
conn)
that query returns two tuples with the same value, but I'd like to distinguish between the two entitiesack... that query is breaking my head
what are you trying to do with it???????
a full outer join I think
it definitely won't do that
it'll try to match on the ?e values
oh, sorry... I was thinking you meant outer product
ummm, let me think
So... the first one will probably return ^{:cols [?e ?foo]} [[1 1] [2 2]]
Then joining this with optional will match on the ?e. So then for ?e=1 it will return ^{:cols [?e ?foo ?bar]} [[1 1 nil]] as a full binding. For ?e=2 it will return ^{:cols [?e ?foo ?bar]} [[2 2 nil]] where it extrapolates to fill in that binding because it's optional.
So the result would be ^{:cols [?e ?foo ?bar]} [[1 1 nil] [2 2 nil]] .
Is that what you get?
what's the syntax you're using? the raw output is ([1 nil] [2 nil])
^{:cols [?e ?foo ?bar]} is the metadata
I can't recall if it survives the projection
ah, yeah it's there
but that's how I keep track of columns
though i don't query for ?e but looks right yeah
projection is where I take the find clause and pull out just the columns that were asked for
If you want a full outer product you'll want to union between two optionals
just the outer join. I did try
(d/q '[:find ?foo ?bar
:where
(or (optional [?e :foo ?foo])
[(identity :undefined) ?foo])
(or (optional [?e :bar ?bar])
[(identity :undefined) ?bar])]
conn)
but i don't know what that ended up doingideally, you'd have something that selected the object types you wanted, then you could have something like:
:find ?foo ?bar
:where [?e :type :a_type]
(or (optional [?e :foo ?foo]) (optional [?e :bar ?bar]))I think.
I think that binds the variable (`?foo` or ?bar) first, then looks for a matching ?e, which it doesn't find, so returns nil for each value. Then it tries to join on the 2, and fails to find a match
I think
optional was designed to expand your already existing bindings. So if you've selected people, then it can be used to add their email, but return nil if they don't have one. You're using optional when no binding exits. That's going to return everything that matches the binding. At least for the first one.
after that, it would go back to the usual behavior
what are you actually trying to do?
pretty sure I just want a full outer join 😛
or you mean higher level than that? it's a bit compilcated
but I use asami as a backend for odoyle rules
OK, that's a SQL operation. I tend to think of these operations in terms of what you're actually trying to do
so, do you know what you're trying to do?
odoyle rules can be added dynamically. a rule fires when all its clauses match, but if we add a rule dynamically, we can run into a problem where the state it would match on was added before the rule was added. so we need to hydrate from somewhere
if the full match can be hydrated, the default inner join works. but we actually need partial hydration. consider if the fact [1 :foo "foo"] comes in, then the subscription [:what [_ :foo foo] [_ :bar bar]] queries asami, it doesn't fully match, so it would get hydrated nothing! so now [1 :bar "bar"] would not cause the subscription to fire even though asami knows enough facts to satisfy the match
so wrapping everything in optional solves partial hydration but also can give wrong hydration, since we have to distinguish between facts with nil values and facts that were never asserted
like i said, bit complicated.. but you asked
higher level than that, this enables reactive datalog-driven UI
all that said the easy workaround is to do two queries with/without optional and diff them, was just wondering if there's a better way to do it
2 queries was my first thought 🙂
The only other thing that comes to mind is updating Asami to return an "unbound" value for optional.
The thing about optional was that I was trying to copy SPARQL, and SPARQL doesn't allow for nil values.
really? how do you affirm absence
https://www.wikidata.org/wiki/Wikidata:Requests_for_comment/Unknown_Value_vs_Null_Value I guess there's a random proposal here for wd
In RDF? Typically with a predicate that indicates that something is not present
There's also rdf:nil
would it be more correct to reify nil instead of unbound then
though that's actually a list
well, nil is current allowed to be stored
that's true
I guess the user could do it
and you'll see it in normal results. Only unions and optionals can have unbound. And I really should split that apart
in fact, now that I say it, I will need this when I bring in Twylyte
(that's a SPARQL wrapper)
(def unbound (Object.))
Then project will need to convert these to nil, and isBound can test for equality.Yay! Raphael 0.3.0 is done! Now I can integrate it into Asami!
That was a LOT of work
The commit says: Showing 8 changed files with 994 additions and 883 deletions.
Thank goodness I had a lot of tests