Reputation: 474
Consider having a list of persons(parent) and their posts(children).
Note that I have simplified the example but it could be parent-child-childofChild-etc, so some entities with a tree of children.
I would like to have a query that returns a union of some filtered(on some criteria) persons id's and their post id's like so:
person1Id
person2Id
person1Post1Id
person1Post2Id
.......
Given a concrete situation with 3 persons(with properties age,name) where Person1 has 2 posts and the other persons have no posts I managed to do this in 2 ways but both have limitations. So inside the where clause we have:
?entityId ?p ?o.
{
#persons/posts graph pattern
}
filter (?entityId =?person || ?entityId=?post) # || otherChildFitler
This works but uses filter so I want to avoid by using union:
{
SELECT (?person as ?s) WHERE{ #criteria}
order by ...
limit 1
}
UNION
{
{
SELECT ?person WHERE{ #criteria}
order by ...
limit 1
}
?s a <http://www.example.org/schema/Post> .
?s <http://www.example.org/schema/postedBy> ?person .
}
This also works but I have to duplicate the person inner query for each child. So I tried to somehow use bind like so: (Assume the inner query returns 1 person that has 2 posts.)
select ?s ?person ?projectedOutPerson
where
{
#tag 1
{
#returns 1 person with 2 posts
SELECT ?person WHERE
{
?person a <http://www.example.org/schema/Person> .
optional{?person <http://www.example.org/schema/age> ?age.}
}
order by desc(?age)
limit 1
}
#tag 2
bind(?person as ?projectedOutPerson)
#tag 3
{
bind(?projectedOutPerson as ?s)
}
#tag 4
UNION
{
?s a <http://www.example.org/schema/Post> .
?s <http://www.example.org/schema/postedBy> ?projectedOutPerson.
}
#tag 5
}
This retuns posts id's but doesn't add personIds and it has some curious behaviour.
I'm testing this on bigdata 1.3 and stardog 2.
?projectedOutPerson is bound ok(tag 2-3) if i select a person with no posts no posts are returned.
In both db the bind between tag3-4 is not done/joined so ?s is not bound to a person(returns an empty result).
if i remove portion tag 4-5 bigdata displays selected person(so now bind in tag 3-4 works) but stardog returns ( empty result, person1, person1) so in stardog chaining the binding doesn't work
bind(?person as ?p1) #only this is bound
bind(?p2 as ?p3)
bind(?p3 as ?s)
In sparql1.1 docs "The variable introduced by the BIND clause must not have been used in the group graph pattern up to the point of use in BIND. " but each time a new variable is introduced, so I think it should work.
So how can I solve this without duplicating the subquery or use filter.
Upvotes: 0
Views: 113
Reputation: 85863
It's much easier to answer this kind of question if you provide some data. If I understand your question correctly, you have some data like the following. I'm providing it in Turtle because it's fairly human readable.
@prefix : <http://stackoverflow.com/q/21115947/1281433/> .
# person1 has two posts, person2 has one post, and
# person3 has no posts at all.
:person1 a :Person ;
:hasPost :person1post1 , :person1post2 .
:person2 a :Person ;
:hasPost :person2post1 .
:person3 a :Person .
Now, if you want to select each person, as well as all their posts, you can do it with a query like this:
prefix : <http://stackoverflow.com/q/21115947/1281433/>
select ?id where {
?person a :Person ;
:hasPost? ?id .
}
-----------------
| id |
=================
| :person3 |
| :person2 |
| :person2post1 |
| :person1 |
| :person1post2 |
| :person1post1 |
-----------------
The trick here is that in the query pattern
?person a :Person ;
:hasPost? ?id .
the ?person a :Person
ensures that ?person
is a Person. Then, the pattern ?person :hasPost? ?id
finds ?id
s such that there's a path from ?person
to ?id
of length zero or one. The length zero case means that ?id
can be bound to the same value of ?person
. The case of length one means that you'll get every x
such that ?person :hasPost x
.
Now, in the case of persons and posts, it doesn't make a lot of sense (I think) to talk about posts having other posts, but if you're just looking for descendants in a tree structure, you can use *
instead of ?
in your property path. For instance, if you had this data:
@prefix : <http://stackoverflow.com/q/21115947/1281433/> .
# 1
# / \
# / \
# 2 3
# / \ / \
# 4 5 6 7
# \ \ \
# 8 9 10
:node1 :hasChild :node2 , :node3 .
:node2 :hasChild :node4 , :node5 .
:node3 :hasChild :node6 , :node7 .
:node4 :hasChild :node8 .
:node6 :hasChild :node9 .
:node7 :hasChild :node10 .
You could get :node3
and all its descendants with a query like this:
prefix : <http://stackoverflow.com/q/21115947/1281433/>
select ?node where {
:node3 :hasChild* ?node
}
-----------
| node |
===========
| :node3 |
| :node7 |
| :node10 |
| :node6 |
| :node9 |
-----------
Upvotes: 1