Ania David
Ania David

Reputation: 1198

sparql is there any way to make simplify this query

I build a query, it is very simple, but my problem is that i find myself repeating the same triples again and again.

I have a class called TemporalContext, each instance of it has:

Either the property canBeRecommendedFrom, (range datetime)

or the property canBeRecommendedUntil (range datetime)

or both

Each instance could have as well:

Either the property weightIfContextMatched

or the property weightIfContextDoesNotMatch

or both

What I want to do is:

  1. If an instance of TemporalContext has both canBeRecommendedFrom and canBeRecommendedUtill, then if it today is inside that range, select the value of weightIfContextMatched, if not, select the value of weightIfontextDoesNotFound. However, maybe the values weightIfContextMatched and weightIfContextDoesNotMatch are not set, so then I use a default value.
  2. the same thing if the instance of TemporalContext has just canBeRecommendFrom and doesn't have canBeRecommendUntil
  3. The same thing is the instance of TemporalContext has just canBeRecommendedUntill and doesn't have canBeRecommendFrom

I did a query for the case in which the instance of TemporalContext has both canBeRecommendedFrom and canBeRecommendedUntil with all the variation of weightIfContextMatched and weightIfContextDoesNotMatch

Here you go the query

select ?item ?finalTemporalWeight where
{
  values ?user {bo:ania} 
  #the items that have temporal context(s)
  {
    select ?item (SUM(?temporalWeight) as ?finalTemporalWeight) {
   ?item a rs:RecommendableClass .
      #to be sure that the item belongs to the class
      filter exists {
        ?item a ?itemClass
      }

  #the items that has both from and until temporal context
  {

    {
      #this block is if today is inside the range
      ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
      #to be sure that both until and from were set.
      ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .



      #check if today is before the until limit
      filter (now () < ?untilLimit)

      #check if today is after the from limit
      filter (now () > ?fromLimit)

     optional
    {
      ?tempoalContext rs:hasWeightIfContextMatched ?weight
    }
    bind((if(bound(?weight), ?weight, 1)) as ?temporalWeight)
    }
    union
    {
      #this block is if today is after the until limit
      ?tempoalContext a rs:TemporalContext .
    ?tempoalContext rs:appliedOnItems ?itemClass .
          #to be sure that both until and from were set.
    ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .



       #check if today is after the until limit
    filter (now () > ?untilLimit)

    optional
    {
     ?tempoalContext rs:hasWeightIfContextDoesNotMatch ?weight
    }
    bind((if(bound(?weight), ?weight, 0.1)) as ?temporalWeight)
    }
    union
    {
      #this block is if today is before the from limit
     ?tempoalContext a rs:TemporalContext .
    ?tempoalContext rs:appliedOnItems ?itemClass .
          #to be sure that both until and from were set.
    ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .



       #check if today is before the from limit
    filter (now () < ?fromLimit)

    optional
    {
      ?tempoalContext rs:hasWeightIfContextDoesNotMatch ?weight
    }
    bind((if(bound(?weight), ?weight, 0.3)) as ?temporalWeight)

    }

  }
  union
  #the items that has just from temporal context
  {

    {
    #this block is if today is before the from limit
      ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
      #to be sure that the from limit is set.
      ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .



      #to be sure that the until limit is not set.
      filter not exists {
        ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .
      }
      #check if today is before the from limit
      filter (now () < ?fromLimit)

     optional
    {
      ?tempoalContext rs:hasWeightIfContextDoesNotMatch ?weight
    }
    bind((if(bound(?weight), ?weight, 0.15)) as ?temporalWeight)

    }
    union
    {
      #this block is if today is after the from limit
      ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
      #to be sure that the from limit is set.
      ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .



      #to be sure that the until limit is not set.
      filter not exists {
        ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .
      }
      #check if today is before the from limit
      filter (now () > ?fromLimit)

     optional
    {
      ?tempoalContext rs:hasWeightIfContextMatched ?weight
    }
    bind((if(bound(?weight), ?weight, 0.25)) as ?temporalWeight)
    }
  }
  union
  #the items that has just until temporal context
  {
     {
    #this block is if today is before the until limit
      ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
      #to be sure that the until limit is set.

      ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .
      #to be sure that the from limit is not set.



      filter not exists {
        ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      }
      #check if today is before the until limit
      filter (now () < ?untilLimit)

     optional
    {
      ?tempoalContext rs:hasWeightIfContextMatched ?weight
    }
    bind((if(bound(?weight), ?weight, 0.35)) as ?temporalWeight)

    }
    union
    {
      #this block is if today is after the until limit
      ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
      #to be sure that the until limit is set.
      ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .


      #to be sure that the from limit is not set.
      filter not exists {
        ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      }
      #check if today is after the until limit
      filter (now () > ?untilLimit)

     optional
    {
      ?tempoalContext rs:hasWeightIfContextDoesNotMatch ?weight
    }
    bind((if(bound(?weight), ?weight, 0.45)) as ?temporalWeight)
    }
  }


      union
      #the items that have temporal context, but that temporal context has no canBeRecommendedFrom nor canBeRecommendedUntil
      {
    select distinct ?item ?temporalWeight {
      ?item a rs:RecommendableClass .
        ?tempoalContext a rs:TemporalContext .
      ?tempoalContext rs:appliedOnItems ?itemClass .
       ?item a ?itemClass .

        #to be sure that neight canBeRecommendFrom nor canBeRecommendedUntil is set
      filter not exists {
        ?tempoalContext rs:canBeRecommendedFrom ?fromLimit .
      }
        filter not exists {
                 ?tempoalContext rs:canBeRecommendedUntil ?untilLimit .
        }
        bind (4.4 as ?temporalWeight)
      }
} 

  }group by ?item
  }
  union
  #the items that don't have a temporal context, we will use the defalut value:
  {
    select distinct ?item ?finalTemporalWeight {
  ?item a rs:RecommendableClass .
    ?tempoalContext a rs:TemporalContext .
     ?tempoalContext rs:appliedOnItems ?itemClass .
    filter not exists {
    ?item a ?itemClass
    }
    bind (2.3 as ?finalTemporalWeight)
  }
  }

}
order by ?item

the query is so easy but long, and i find myself repeating the same thing inside each union. and now i have to reapeat the same thing for the second and third case,

I thought before continuing in this long-but-easy-query i ask you first maybe you know a better way to reduce the size of it.

the query is working very well, i tested it

Update 1

After the appreciate reply from @Joshua Taylor here is the result:

enter image description here

However, this is the result of my code: enter image description here

You can see that the row number 6, the item doesn't have any value, the case of that item is: it is before the from limit. so the correct union is #this block is if today is before the from limit. but there is no value for hasWeightIfContextDoenNotMatch so we should use the default value, which is 0.3, my code does that, my the code in the answer doesn't.

Update 2

Now I finished (and updated the query) developing the boring-so-so-so boring query but i handled all the scenarios, I will tell you the scenarios:

  1. The item belongs to a temporalContext class:

1.1. the temporalClass has both canBeRecommendFrom and canBeRecommendUntill

1.1.1. the item is inside the range

1.1.1.1. the weightIfContextMatched exists.

1.1.1.2. the weightIfContextMatched doesn't exist.

1.1.2. the item is before the from limit.

1.1.2.1 the weightIfContextDoesNotMatch exists.

1.1.2.2. the weightIfContextDoesNotMatch doesn't exist.

1.1.3. the item is after the until limit.

1.1.3.1 the weightIfContextDoesNotMatch exists.

1.1.3.2. the weightIfContextDoesNotMatch doesn't exist.

1.2. the temporalClass has just canBeRecommendFrom

1.2.1 the item is after the from limit

1.2.1.1. the weightIfContextMatched exists.

1.2.1.2. the weightIfContextMatched doesn't exist.

1.2.2 the item is before the from limit

1.2.2.1 the weightIfContextDoesNotMatch exists.

1.2.2.2. the weightIfContextDoesNotMatch doesn't exist.

1.3. the temporalClass has just canBeRecommednFrom

1.3.1 the item is before the from limit

1.3.1.1 the weightIfContextDoesNotMatch exists.

1.3.1.2. the weightIfContextDoesNotMatch doesn't exist.

1.3.2 the item is after the from limit

1.3.2.1. the weightIfContextMatched exists.

1.3.2.2. the weightIfContextMatched doesn't exist.

1.4. the temporalClass has neither canBeRecommendFrom nor canBeRecommendUntil

use default value (be careful of duplicated)

  1. the item doesn't belong to a temporalClass

use default value (be careful of duplicated)

Upvotes: 1

Views: 124

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85913

You're always looking for a temporal context that is a TemporalContext, that is applied on an item class, and that has a from and until limit, so that can come out of the query. Then you're interested in optional weights to use depending on whether now() before, during, or after the interval. Then you want to set temporal weight as the matching weight (or 1) if we're in the interval, or else the unmatched weight (or 0.1) if we're after it, or else the unmatched weight (or 0.3) if we're before it. I'd do that with a bind that checks those conditions, and uses coalesce to select the optional value or the default:

SELECT  ?item ?temporalContext ?itemClass ?weight ?temporalWeight
WHERE
  { VALUES ?user { bo:ania }
    ?item a rs:RecommendableClass .

    ?temporalContext a rs:TemporalContext ;
      rs:appliedOnItems ?itemClass ;
      rs:canBeRecommendedFrom ?fromLimit ;
      rs:canBeRecommendedUntil ?untilLimit .

      OPTIONAL {
         ?temporalContext rs:hasWeightIfContextMatched ?matchedWeight
      }
      OPTIONAL {
        ?temporalContext rs:hasWeightIfContextDoesNotMatch ?unmatchedWeight
      }

      bind(if(now() < ?fromLimit,
              coalesce(?unmatchedWeight, 0.1),
              if(?fromLimit <= now() && now() <= ?untilLimit,
                 coalesce(?matchedWeight, 1.0),
                 if(?untilLimit < now(),
                    coalesce(?unmatchedWeight, 0.3),
                    0.0)))                           #-- default here?
           as ?temporalWeight)
  }
ORDER BY ?item

I've used <= in checking whether now() is in the duration, since before if now() had been exactly equal to one of them, you might not have caught it. It it possible that you get to the "default here?" line, though? I think it's still possible if the data doesn't actually have comparable times there. as I see it, you can either use a default value there (like 0.0), or you can just assume that if the first two cases didn't match, then you just want the third, in which case you can skip the check altogether and just do:

  bind(if(now() < ?fromLimit,
          coalesce(?unmatchedWeight, 0.1),
          if(?fromLimit <= now() && now() <= ?untilLimit,
             coalesce(?matchedWeight, 1.0),
             coalesce(?unmatchedWeight, 0.3)))
      as ?temporalWeight)

Upvotes: 4

Related Questions