You can make use of UNION in your queries. In either case, you have a set of patterns you are looking for, and you want to collect information from a UNION of those patterns.
For the first set, getting all triples containing a list item in either subject or object,
SELECT ?s ?p ?o # result triples
WHERE
{
# get a term bound to ?term
GRAPH <urn:termsList/>
{ ?term a <urn:types/word> } # or however the terms are stored
# match ?term against the basic patterns
GRAPH <urn:associations/>
{
{
?term ?p ?o . # basic pattern #1
BIND(?term AS ?s) # so that ?term shows up in the results
}
UNION # take ?term as either subject or object
{
?s ?p ?term . # basic pattern #2
BIND(?term AS ?o)
}
}
}
First get a binding of all the terms (?term a …).
Then match it against the basic patterns:
?term ?p ?o
and
?s ?p ?term.
After each pattern match, use a binding to place ?term in its proper place among the results. For example, the first pattern has just bound ?p and ?o, so their corresponding ?s needs to be bound next, otherwise it will just show up blank.
For the second set, first we get two words from the list. We want a many-to-many matching:
?term1 a … .
?term2 a … .
The basic patterns:
?term1 ?p1 ?term2
?term1 ?p1 ?term .
?term2 ?p2 ?term .
?term1 ?p1 ?term .
?term ?p2 ?term2 .
?term ?p1 ?term1 .
?term ?p2 ?term2 .
Add a filter on each of the last three to ensure ?term1 and ?term2 are not the same:
FILTER(!SAMETERM(?term1, ?term2))
(We could put these filters outside of all the unions, but it is more efficient to filter variables locally, before further using them.)
Finally UNION the results together:
SELECT ?s1 ?p1 ?o1 ?s2 ?p2 ?o2
WHERE
{
GRAPH <urn:termsList/>
{
?term1 a <urn:types/word> . # outer loop variable
?term2 a <urn:types/word> . # inner loop variable
}
GRAPH <urn:associations/>
{
{
# Only need to check one direction; either end gets
# matched into ?term1 at some point
?term1 ?p1 ?term2 .
BIND (?term1 AS ?s1) .
BIND (?term2 AS ?o1) . # Note we leave ?s2, ?p2, ?o2 unbound here
}
UNION
{
?term1 ?p1 ?term .
?term2 ?p2 ?term .
FILTER(!SAMETERM(?term1, ?term2))
BIND(?term1 AS ?s1) .
BIND(?term AS ?o1) .
BIND(?term2 AS ?s2) .
BIND(?term AS ?o2)
}
UNION
{
?term1 ?p1 ?term .
?term ?p2 ?term2 .
FILTER(!SAMETERM(?term1, ?term2))
BIND(?term1 AS ?s1) .
BIND(?term AS ?o1) .
BIND(?term AS ?s2) .
BIND(?term2 AS ?o2)
}
UNION
{
?term ?p1 ?term1 .
?term ?p2 ?term2 .
FILTER(!SAMETERM(?term1, ?term2))
BIND(?term AS ?s1) .
BIND(?term1 AS ?o1) .
BIND(?term AS ?s2) .
BIND(?term2 AS ?o2)
}
}
}
We will test the queries on the following texts:
For a word list --
# For God so loved the world, that he gave his only begotten Son, that
# whosoever believeth in him should not perish, but have everlasting life.
# John 3:16
@prefix : <urn:terms/> .
@prefix t: <urn:types/> .
:For a t:word .
:God a t:word .
:so a t:word .
:loved a t:word .
:the a t:word .
:world a t:word .
:that a t:word .
:he a t:word .
:gave a t:word .
:his a t:word .
:only a t:word .
:begotten a t:word .
:Son a t:word .
:that a t:word .
:whosoever a t:word .
:believeth a t:word .
:in a t:word .
:him a t:word .
:should a t:word .
:not a t:word .
:perish a t:word .
:but a t:word .
:have a t:word .
:everlasting a t:word .
:life a t:word .
And an association list:
# For the wages of sin is death; but the gift of God is eternal life through
# Jesus Christ our Lord.
# Romans 6:23
@prefix : <urn:terms/> .
@prefix g: <urn:grammar/> .
:For g:clauseAt :wages ;
g:nextClauseHeadAt :but .
:the g:describes :wages .
:wages g:predicate :is .
:of g:describes :wages ;
g:nominative :sin .
:is g:object :death .
:but g:clauseAt :gift .
:the g:describes :gift .
:gift g:predicate :is .
:of g:describes :gift ;
g:nominative :God .
:is g:object :life .
:eternal g:describes :life .
:through g:describes :is ;
g:nominative :Jesus .
:Christ g:describes :Jesus .
:our g:describes :Lord .
:Lord g:describes :Jesus .
Query 1:
----------------------------------------------------------------------------
| s | p | o |
============================================================================
| <urn:terms/For> | <urn:grammar/nextClauseHeadAt> | <urn:terms/but> |
| <urn:terms/For> | <urn:grammar/clauseAt> | <urn:terms/wages> |
| <urn:terms/of> | <urn:grammar/nominative> | <urn:terms/God> |
| <urn:terms/is> | <urn:grammar/object> | <urn:terms/life> |
| <urn:terms/eternal> | <urn:grammar/describes> | <urn:terms/life> |
| <urn:terms/but> | <urn:grammar/clauseAt> | <urn:terms/gift> |
| <urn:terms/For> | <urn:grammar/nextClauseHeadAt> | <urn:terms/but> |
| <urn:terms/the> | <urn:grammar/describes> | <urn:terms/gift> |
| <urn:terms/the> | <urn:grammar/describes> | <urn:terms/wages> |
----------------------------------------------------------------------------
Query 2:
----------------------------------------------------------------------------------------------------------------------------------------
| s1 | p1 | o1 | s2 | p2 | o2 |
========================================================================================================================================
| <urn:terms/For> | <urn:grammar/nextClauseHeadAt> | <urn:terms/but> | | | |
| <urn:terms/For> | <urn:grammar/clauseAt> | <urn:terms/wages> | <urn:terms/the> | <urn:grammar/describes> | <urn:terms/wages> |
| <urn:terms/but> | <urn:grammar/clauseAt> | <urn:terms/gift> | <urn:terms/the> | <urn:grammar/describes> | <urn:terms/gift> |
| <urn:terms/the> | <urn:grammar/describes> | <urn:terms/wages> | <urn:terms/For> | <urn:grammar/clauseAt> | <urn:terms/wages> |
| <urn:terms/the> | <urn:grammar/describes> | <urn:terms/gift> | <urn:terms/but> | <urn:grammar/clauseAt> | <urn:terms/gift> |
----------------------------------------------------------------------------------------------------------------------------------------
Note that there is some redundancy here. That is due to the double-loop nature of how we bind values to ?term1 and ?term2, so that ?term1 becomes ?term2 and vice versa. If this is unacceptable, you can simply change line 1 to only
SELECT DISTINCT ?s1 ?p1 ?o1
This, of course, renders the BINDings for ?s2 and ?o2 unecessary, since they are bound only for the SELECT.
"For if we have been united with [Christ] in a death like his, we shall certainly be united with him in a resurrection like his" (Romans 6:5 ESV).