Search code examples
rdfsparql

SPARQL query RDF ontology


Im trying this sparql query, but Im having trouble to find out whats wrong. I would like to take all the receipes that has those ingredients or less. ie: receipe 1: Tomato receipe 2: Tomato and salt receipe 3: Tomato, salt, onion.

And I have the following query:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rec:<http://www.receta.org#>

SELECT reduced ?r (count (?i) as ?cuantos) WHERE {

    ?x rdf:type rec:Receta .
    ?x rdfs:label ?r.

    filter not exists {
        ?x rec:Ingrediente ?i
        filter( ?i not in (rec:Tomato, rec:Salt) )
    }
}
GROUP BY ?r

I would to show only the first one. Not the second one and not the last one.

Its just an example but I actually want to do it with more that 3 receipes, lets say a huge database, I want to take the receipes that have less ingredients than tomato and salt for example. So any ideas of how can I fix my query to do so?

Im going to try to explain myself:

For example:

Lets say I have a huge database with hundreds recipes. Some of those are: Recipe1: tomato and salt Receipe2: Onion and meat Receipe3: Tomato and vinegar Receipe4: chocolate and milk

And in my query I say that I want the recipes with tomato, salt and vinegar. So in that case I would like to get the Recipe1 and the Recipe3, because the recipe1 has tomato and salt and the recipe3 has tomato and vinegar, one ingredient less than what I say. Lets say now that I add more recipes:

Recipe4: Tomato Recipe5: Salt Recipe6: Tomato, vinegar, salt and onion Recipe6: Tomato, vinegar, salt

If I execute my query again I would like to have the recipe1, recipe3, recipe4 and recipe5. Cause those recipes are those that has those ingredients or less. I dont want to have the recipe6 cause has more ingredients than what I asked and not even the recipe7.

Thank you!!!


Solution

  • If I understand you correctly, you're trying to specify a set of ingredients, and then select the recipes whose ingredients are a property subset of that set of ingredients (or maybe those whose ingredients are just a subset; the examples in your question don't make this clear). At any rate, rather than writing examples out in natural language where it's easy to make mistakes, it's better to provide data we can work with, and in this case it's not hard at all to do that. For instance, here's a small Turtle file with nine recipes, each of which has some ingredients:

    @prefix : <urn:ex:> .
    
    :recipe1 :ingredient :tomato, :salt .
    :recipe2 :ingredient :onion, :meat .
    :recipe3 :ingredient :tomato, :vinegar .
    :recipe4 :ingredient :chocolate, :milk .
    :recipe5 :ingredient :tomato .
    :recipe6 :ingredient :salt .
    :recipe7 :ingredient :tomato, :vinegar, :salt, :onion .
    :recipe8 :ingredient :tomato, :vinegar, :salt .
    :recipe9 :ingredient :vinegar, :salt .
    

    Now, we can write a query that retrieves recipes all of whose ingredients are in a specified set, and keeps only those that have less than (or less than or equal to) some specific number of distinct ingredients. (You don't have to include the group_concat stuff; I just did that to get an ingredient list for each recipe.)

    prefix : <urn:ex:>
    
    select ?recipe (group_concat(?ingredient;separator=', ') as ?ingredients) {
      ?recipe :ingredient ?ingredient
      filter not exists {
        ?recipe :ingredient ?other_ingredient
        filter( ?other_ingredient not in ( :salt, :tomato, :vinegar ) )
      }
    }
    group by ?recipe
    having (count(distinct ?ingredient) < 3)
    
    ----------------------------------------------
    | recipe   | ingredients                     |
    ==============================================
    | :recipe9 | "urn:ex:salt, urn:ex:vinegar"   |
    | :recipe5 | "urn:ex:tomato"                 |
    | :recipe6 | "urn:ex:salt"                   |
    | :recipe3 | "urn:ex:tomato, urn:ex:vinegar" |
    | :recipe1 | "urn:ex:tomato, urn:ex:salt"    |
    ----------------------------------------------
    

    If you wanted to allow recipes with all three ingredients, you could just change the line

    having (count(distinct ?ingredient) < 3)
    

    to either of:

    having (count(distinct ?ingredient) <= 3)
    

    having (count(distinct ?ingredient) < 4)