这是我的最少数据:
@prefix : <http://example.org/rs#>
:item :hasContext [:weight 0.1 ; :doNotRecommend true] , [:weight 0.2 ] .
:anotherItem :hasContext [:weight 0.4] , [ :weight 0.5 ] .
如你所见,每个 item
都有 一个或多个 hasContext
,那个 hasContext
的对象是一个可能有一个doNotRecommed
谓词的实例。
我想要的是,如果这些实例之一(即 hasContext 的对象)包含 donNotRecommed,我希望总和为零。 ** 总和是指权重的总和**,换句话说,如果该属性存在,则忽略所有权重(无论它们是否存在),只需将其置零
我的查询
select ?item (SUM(?finalWeight) as ?summedFinalWeight) {
?item :hasContext ?context .
optional
{
?context :doNotRecommend true .
bind( 0 as ?cutWeight)
}
optional
{
?context :weight ?weight .
}
bind ( if(bound(?cutWeight), ?cutWeight , if(bound(?weight), ?weight, 0.1) ) as ?finalWeight )
}
group by ?item
结果
看:item
的值,是0.2
(我知道原因,是因为0.2加上0(这个0是因为doNotRecommend是那里)但我不知道解决方案,我想要的是在 :item
(提示,我知道我总是可以在此查询的上层运行另一个查询并解决它,或者我可以使用不存在的过滤器来解决它,但我希望在同一个查询中解决它,因为我应该u 是一个最小数据,而在我的本体中,获取那个权重和这些对象是一个很长的查询
更新一
这是我的真实查询,第一部分(联合之前)检查用户是否确认上下文,第二部分(联合之后)检查用户是否不符合上下文这里我想检查该上下文是否有 doNotRecommendOrNot 。请确保两部分一起验证是 imporisslbe
SELECT ?item (SUM(?finalWeightFinal) AS ?userContextWeight)
WHERE
{ VALUES ?user { bo:ania }
?item rdf:type rs:RecommendableClass
OPTIONAL
{ { FILTER EXISTS { ?item rdf:type ?itemClass }
?item rdf:type rs:RecommendableClass .
?userContext rdf:type rs:UserContext ;
rs:appliedOnItems ?itemClass ;
rs:appliedOnUsers ?userClass
FILTER EXISTS { ?user rdf:type ?userClass }
OPTIONAL
{ ?userContext rs:hasWeightIfContextMatched ?weight }
BIND(if(bound(?weight), ?weight, 0.2) AS ?finalWeight)
}
UNION
{ ?item rdf:type rs:RecommendableClass .
?userContext rdf:type rs:UserContext ;
rs:appliedOnItems ?itemClass ;
rs:appliedOnUsers ?userClass
FILTER EXISTS { ?item rdf:type ?itemClass }
FILTER NOT EXISTS { ?user rdf:type ?userClass }
OPTIONAL
#Here is the skip
{ ?userContext rs:doNotRecommendInCaseNotMatch true
BIND(0 AS ?skip)
}
OPTIONAL
{ ?userContext rs:hasWeightIfContextDoesNotMatch ?weight }
BIND(if(bound(?weight), ?weight, 0.1) AS ?finalWeight)
}
}
BIND(if(bound(?finalWeight), ?finalWeight, 1) AS ?finalWeightFinal)
}
GROUP BY ?item
更新2
在@Joshua Taylor 的赞赏回答后,我尝试将他的方法应用到实际案例中,但这次添加了 filter !bound(?skip)
这里是查询
SELECT ?item ?itemClass ?userContext ?skip ?finalWeight
WHERE
{ #{
in this block i just select the items that i want to calculate the user context to.
} #
OPTIONAL
{ FILTER EXISTS { ?item rdf:type ?itemClass }
?userContext rdf:type rs:UserContext ;
rs:appliedOnItems ?itemClass ;
rs:appliedOnUsers ?userClass
OPTIONAL
{ ?userContext rs:hasWeightIfContextMatched ?weightMatched }
OPTIONAL
{ ?userContext rs:hasWeightIfContextDoesNotMatch ?weightNotMatched }
OPTIONAL
{ ?userContext rs:doNotRecommendInCaseNotMatch true
BIND(1 AS ?skip)
}
BIND(if(EXISTS { ?user rdf:type ?userClass }, coalesce(?weightMatched, "default User Matched"), coalesce(?weightNotMatched, "default User not matched")) AS ?weight)
}
BIND(if(bound(?weight), ?weight, "no user context found for this item") AS ?finalWeight)
FILTER ( ! bound(?skip) )
}
它适用于我拥有的数据,但我现在只有一个测试数据,所以我想问你它是否正确
更新3
我的查询生成这些字段:
项目跳过...
并且过滤器会删除确实具有 skip 绑定(bind)的行,但假设一个项目有两行,如下所示:
项目跳过
A 1
A
A
所以在我的例子中,我将只删除第一行,我需要知道我是否可以删除该项目的所有行。
最佳答案
有很多方法可以做到这一点;这是获取每个项目的总重量的方法,然后检查该项目是否有不推荐标志,如果有,则使用 0 作为总重量:
select ?item (if(bound(?skip), 0.0, ?sumWeight_) as ?sumWeight) {
{ select ?item (sum(?weight) as ?sumWeight_) where {
?item :hasContext/:weight ?weight .
}
group by ?item
}
bind(exists { ?item :hasContext/:doNotRecommend true } as ?skip)
}
----------------------------
| item | sumWeight |
============================
| :item | 0.0 |
| :anotherItem | 0.0 |
----------------------------
从概念上讲,此查询会为每个项目检查一次,是否有任何上下文将其标记为不推荐。我认为这样比较有效。
在bind(exists { … } as ?skip)
请注意 bind 和 exists 的组合。您已经知道 bind 的工作原理,因为您已经使用了很多次。 bind(expr as ?variable) 计算表达式 expr 并将其分配给变量 ?variable。您之前可能在 filter 表达式中使用过 exists 和 (not exists)。 exists { … } 如果大括号内的模式在图中匹配则为真,否则为假。 not exists { … } 类似,但相反。图案
?item :hasContext/:doNotRecommend true
只是简写,使用属性路径,用于模式:
?item :hasContext ?something .
?something :doNotrecommend true .
在这种情况下,如果该模式存在,那么我们要跳过项目的总重量并使用零代替。
备选
如果您愿意计算所有项目的总和,然后排除那些至少具有不推荐上下文的项目,您也可以这样做。诀窍只是弄清楚如何计算跳过的次数:
select ?item (sum(?weight_) as ?weight){
?item :hasContext ?context .
?context :weight ?weight_ .
bind(exists { ?context :doNotRecommend true } as ?skip)
}
group by ?item
having (sum(if(?skip,1,0)) = 0)
注意事项
你提到过
i know that i can always run another query in an upper level of this query and solve it or i can solve it using filter not exist but i am looking to solve it in the same query, because what i should u is a minimal data, while in my ontology, getting that weight and these objects is a very long query
上面的解决方案首先计算总和权重,然后决定使用哪个和丢弃哪个。这意味着有一些不必要的计算。您的解决方案执行类似的操作:它计算不具有 :doNotRecommend 属性的上下文的权重,即使同一项目的某些其他上下文具有 doNotRecommend 属性也是如此。如果你真的想避免不必要的计算,那么你应该先找出哪些项目是可推荐的,然后计算那些项目的分数,找出哪些项目是不可推荐的,并为那些项目返回零。
很容易得到哪些项目是哪些的列表:类似
select distinct ?item ?skip {
?item :hasContext ?anything .
bind(exists{ :hasContext/:doNotRecommend true} as ?skip)
}
会做的很好。但是,由于您想要对可跳过值和不可跳过值执行不同的操作,并且这可能采用两种选择的并集形式,因此您遇到的问题是您必须重复每个子查询都相同。 (或者在一个中使用 exists 而在另一个中使用 not exists,这实际上是在重复相同的查询。)它很快就会变得丑陋。它可能看起来像这样:
select ?item ?weight {
{
#-- get non recommendable items and
#-- set their weights to 0.0.
select distinct ?item (0.0 as ?weight) {
?item :hasContext/:doNotRecommend true #-- (*)
}
}
union
{
#-- get recommendable items and
#-- their aggregate weights
select ?item (sum(?weight_) as ?weight) {
#-- find the recommendable items
{ select distinct ?item {
?item :hasContext ?x .
filter not exists { ?item :hasContext/:doNotRecommend true } #-- (*)
}
}
#-- and get their context's weights.
?item :hasContext/:weight ?weight_
}
group by ?item
}
}
-------------------------
| item | weight |
=========================
| :item | 0.0 |
| :anotherItem | 0.9 |
-------------------------
在我看来,问题在于标有(*) 的行实际上在做同样的事情。其他计算不会发生多次,这很好,但我们仍在为每个项目检查两次是否值得推荐。
关于sparql 检查属性的存在并给答案零,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36621269/