I'd like to select data property values using sparql with some restrictions on their languages:
I have an ordered set of preferred languages ("ru", "en", ... etc )
If an item have more than one language for value, I'd like to have only one value restricted by my set of languages (if ru is available - I want to see ru value, else if en available I want to see en else if ... etc if no lang available - no lang value).
Current query is:
select distinct ?dataProperty ?dpropertyValue where {
<http://dbpedia.org/resource/Blackmore's_Night> ?dataProperty ?dpropertyValue.
?dataProperty a owl:DatatypeProperty.
FILTER ( langmatches(lang(?dpropertyValue),"ru") || langmatches(lang(? dpropertyValue),"en") || lang(?dpropertyValue)="" )
}
The problem with it: results contain two rows for abstract (ru+en). I want only one row, which should contain ru. In case when ru is not available I'd like to get en etc.
How?
Suppose you have data like this:
#prefix : <http://stackoverflow.com/q/21531063/1281433/> .
:a a :resource;
:p "a in english"#en, "a in russian"#ru .
:b a :resource ;
:p "b in english"#en .
Then you're hoping to get results like this:
--------------------------------
| resource | label |
================================
| :b | "b in english"#en |
| :a | "a in russian"#ru |
--------------------------------
Here are two ways of doing this.
Associate language tags with ranks, find the rank of the best label, then find the label with that rank
This way uses SPARQL 1.1 subqueries, aggregates, and data provided with values. The idea is to use values to associate each language tag with a rank. Then you use a subquery to pull out the optimal rank over all the labels that the resource has). Then in the outer query, you have access to the optimal rank, and you just retrieve the label with the language corresponding to that rank.
prefix : <http://stackoverflow.com/q/21531063/1281433/>
select ?resource ?label where {
# for each resource, find the rank of the
# language of the most preferred label.
{
select ?resource (min(?rank) as ?langRank) where {
values (?lang ?rank) { ("ru" 1) ("en" 2) }
?resource :p ?label .
filter(langMatches(lang(?label),?lang))
}
group by ?resource
}
# ?langRank from the subquery is, for each
# resource, the best preference. With the
# values clause, we get just the language
# that we want.
values (?lang ?langRank) { ("ru" 1) ("en" 2) }
?resource a :resource ; :p ?label .
filter(langMatches(lang(?label),?lang))
}
Select the labels separately and coalesce in the order that you want
You can select an optional label for each of the languages you're considering, and then coalesce them into (so you get the first one that's bound) in the order of your preference. This is kind of verbose, but if you need to do anything else with the labels in various languages other than the most preferred, you'll have access to them.
prefix : <http://stackoverflow.com/q/21531063/1281433/>
select ?resource ?label where {
# find resources
?resource a :resource .
# grab a russian label, if available
optional {
?resource :p ?rulabel .
filter( langMatches(lang(?rulabel),"ru") )
}
# grab an english label, if available
optional {
?resource :p ?enlabel .
filter( langMatches(lang(?enlabel),"en") )
}
# take either as the label, but russian over english
bind( coalesce( ?rulabel, ?enlabel ) as ?label )
}
Related
I am trying to list all entries whose names start with the letter 'R' and are older than 20 but It is not working - Can you please give me a hint ?
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT DISTINCT ?givenname ?Age ?firstName
WHERE {
?Person foaf:givenname ?firstName
{FILTER (?Age > '20')}
UNION
{FILTER regex(?givename, "^(R)")}
}
To find names that start with "R", you can use STRSTARTS():
FILTER( STRSTARTS(?givename, "R") ) .
To filter based on the age, you first have to add/bind this variable in a triple pattern, e.g.:
?Person ex:yourAgeProperty ?Age .
Your age FILTER compares strings (as you use quotation marks and don’t specify a datatype, which defaults to xsd:string). In case the age is given as xsd:integer in your data, you could use:
FILTER( ?Age > "20"^^xsd:integer ) .
And if both filters need to apply, simply list them one after the other (without {} and without UNION). Or you could combine them:
FILTER( STRSTARTS(?givename, "R") && ?Age > "20"^^xsd:integer ) .
In my application I am doing a scope/search on :title for a search/filter of my records. The search itself works fine, only thing is that user need to write exactly the title & they can't search word within the :title.
For instance If the title is: This search is cool, user need to start the search and have the complete sentence: This search to search and they can't write is cool and get records that have is cool in the title.
My scope looks like:
class Post < ActiveRecord::Base
scope :search_query, lambda { |query|
return nil if query.blank?
# condition query, parse into individual keywords
terms = query.downcase.split(/\s+/)
# replace "*" with "%" for wildcard searches,
# append '%', remove duplicate '%'s
terms = terms.map { |e|
(e.gsub('*', '%') + '%').gsub(/%+/, '%')
}
# configure number of OR conditions for provision
# of interpolation arguments. Adjust this if you
# change the number of OR conditions.
num_or_conditions = 1
where(
terms.map {
or_clauses = [
"LOWER(posts.title) LIKE ?"
].join(' OR ')
"(#{ or_clauses })"
}.join(' AND '),
*terms.map { |e| [e] * num_or_conditions }.flatten
)
}
How can I make my scope/query so user can search words within the title and get records that has words they have searched for?
I tried with ILIKE, but then the search stop working in development, I think its because of sqlite can't have ILIKE, but in productionthe search worked but still can't search for words within titles.
When I use LIKE, the sql query was:
SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "posts" WHERE ((LOWER(posts.title) LIKE 'rails%')) LIMIT 50 OFFSET 0) subquery_for_count
While when I used ILIKE, the query was:
SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "posts" WHERE ((LOWER(posts.title) ILIKE 'rails%')) LIMIT 50 OFFSET 0) subquery_for_count
SQLite3::SQLException: near "ILIKE": syntax error: SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "posts" WHERE ((LOWER(posts.title) ILIKE 'rails%')) LIMIT 50 OFFSET 0) subquery_for_count
ps: Im using Filterrific gem
I use pg gem for Production ENV & sqlite3 for Development ENV
As its described in this w3schools article, LIKE works as:
WHERE CustomerName LIKE 'a%' => Finds any values that starts with "a"
WHERE CustomerName LIKE '%a' => Finds any values that ends with "a"
WHERE CustomerName LIKE '%or%' => Finds any values that have "or" in any position
WHERE CustomerName LIKE '_r%' => Finds any values that have "r" in the second position
WHERE CustomerName LIKE 'a_%_%' => Finds any values that starts with "a" and are at least 3 characters in length
WHERE ContactName LIKE 'a%o' => Finds any values that starts with "a" and ends with "o"
I needed to change (e.gsub('*', '%') + '%').gsub(/%+/, '%'), to: ('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%') .
When searching with (e.gsub('*', '%') + '%').gsub(/%+/, '%'), result would be (LOWER(posts.title) ILIKE 'keyword%'), where as ('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%'), would give (LOWER(posts.title) ILIKE '%keyword%')
I have an ontology Model. I am inserting integer data in one of the class instance through Sparql update. The model is storing the data randomly with out any order. Now when I want to extract this data through Sparql Query I want it in order of the time of insertion. How could i achieve this? Any idea?
P.S: My ontology Model is made in Protege software.
My Query for inserting Data is below one.
PREFIX test:<http://www.semanticweb.org/muhammad/ontologies/2017/2/untitled-ontology-14#>
INSERT {
?KPI_Variables test:hasValue_ROB1 10
} WHERE {
?KPI_Variables test:hasValue_ROB1 ?Newvalue
FILTER(?KPI_Variables= test:Actual_Production_Time)
}
And For Getting the data I am using the following Query:
PREFIX test:<http://www.semanticweb.org/muhammad/ontologies/2017/2/untitled-ontology-14#>
SELECT ?KPI_Variables ?Newvalue WHERE {
?KPI_Variables test:hasValue_ROB1 ?Newvalue
FILTER(?KPI_Variables = test:Actual_Production_Time)
} LIMIT 25
Data in RDF is simply triples. There's no notion of when a triple is added to a graph. If you want that kind of information, you'll need to make it explicit in your data model. SPARQL does include a now function that lets you get a timestamp for when a query is run. That means that you could do something like this:
prefix : <urn:ex:>
insert {
[] :hasSubject ?s ;
:hasPredicate ?p ;
:hasObject ?o ;
:hasTime ?now .
}
where {
#-- Fake a couple of triples
values (?s ?p ?o) {
(:a :p :b)
(:c :q :d)
}
#-- Get the current time
bind (now() as ?now)
}
Now your graph contains data like:
#prefix : <urn:ex:> .
[ :hasObject :d ;
:hasPredicate :q ;
:hasSubject :c ;
:hasTime "2017-04-28T13:32:11.482+00:00"^^<http://www.w3.org/2001/XMLSchema#dateTime>
] .
[ :hasObject :b ;
:hasPredicate :p ;
:hasSubject :a ;
:hasTime "2017-04-28T13:32:11.482+00:00"^^<http://www.w3.org/2001/XMLSchema#dateTime>
] .
Which you can query like:
prefix : <urn:ex:>
select ?s ?p ?o ?time {
[] :hasSubject ?s ;
:hasPredicate ?p ;
:hasObject ?o ;
:hasTime ?time
}
order by ?time
s,p,o,time
urn:ex:c,urn:ex:q,urn:ex:d,2017-04-28T13:32:11.482+00:00
urn:ex:a,urn:ex:p,urn:ex:b,2017-04-28T13:32:11.482+00:00
Once you've inserted some things at different times, you'd have different time values, so sorting would be meaningful. I'd suggest that you don't just reify the triples like I did (and if you are going to go with a straightfoward reification, you should probably use the standard vocabulary for it), but rather have some meaningful structure that actually has timestamps as part of it.
I have two entries in my database
Obj1 is tagged with "hello, world, planet"
Obj2 is tagged with "hello"
if I do modelName.tagged_with(["hello", "world", "planet", "earth"], :any=>true)
I want to sort the returned objects in order of highest to lowest number of tags matched.
so in this case i'd like the order to be Obj1, Obj2
how can I do this? is there a way to get number of tags matched for each of the returned results?
You can call tag_list on the objects and use that to figure out how many tags there are:
tags = %w{hello world planet earth}
objs = ModelName.taggedWith(tags, :any => true)
objs.sort_by! { |o| -(tags & o.tag_list).length }
The tags & o.tag_list yields the intersection of the tags you're looking for and the tags found, then we negate the size of the intersection to tell sort_by (which sorts in ascending order) to put larger intersections at the front, negating the result is an easy way to reverse the usual sort order.
Posting this here if someone else is looking for a way to query a model by tags and order by the number of matches. This solution also allows for the usage of any "equality" operator like the % from pg_trgm.
query = <<-SQL
SELECT users.*, COUNT(DISTINCT taggings.id) AS ct
FROM users
INNER JOIN taggings ON taggings.taggable_type = 'User'
AND taggings.context = 'skills'
AND taggings.taggable_id = users.id
AND taggings.tag_id IN
(SELECT tags.id FROM tags
WHERE (LOWER(tags.name) % 'ruby'
OR LOWER(tags.name) % 'java'
OR LOWER(tags.name) % 'sa-c'
OR LOWER(tags.name) % 'c--'
OR LOWER(tags.name) % 'gnu e'
OR LOWER(tags.name) % 'lite-c'
))
GROUP BY users.id
ORDER BY ct DESC;
SQL
User.find_by_sql(query)
Note that the code above will only work if you have pg_trgm enabled. You can also simply replace % with ILIKE.
EDIT: With ActiveRecord and eager loading:
This could be in a scope or class method and can be chained with other ActiveRecord methods.
ActiveRecord::Base.connection
.execute('SET pg_trgm.similarity_threshold = 0.5')
matches = skills.map do
'LOWER(tags.name) % ?'
end.join(' OR ')
select('users.*, COUNT(DISTINCT taggings.id) AS ct')
.joins(sanitize_sql_array(["INNER JOIN taggings
ON taggings.taggable_type = 'User'
AND taggings.context = 'skills'
AND taggings.taggable_id = users.id
AND taggings.tag_id IN
(SELECT tags.id FROM tags WHERE (#{matches}))", *skills]))
.group('users.id')
.order('ct DESC')
.includes(:skills)
Override skill_list from acts-as-taggable-on in the model:
def skill_list
skills.collect(&:name)
end
and proceed normally.
I felt like the following should be possible I'm just not sure what approach to take.
What I'd like to do is use the include method to shape my results, ie define how far along the object graph to traverse. but... I'd like that traversal to be conditional.
something like...
dealerships
.include( d => d.parts.where(p => p.price < 100.00))
.include( d => d.parts.suppliers.where(s => s.country == "brazil"));
I understand that this is not valid linq, in fact, that it is horribly wrong, but essentially I'm looking for some way to build an expression tree that will return shaped results, equivalent to...
select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
and s.country = 'brazil'
with an emphasis on the join conditions.
I feel like this would be fairly straight forward with esql but my preference would be to build expression trees on the fly.
as always, grateful for any advice or guidance
This should do the trick:
using (TestEntities db = new TestEntities())
{
var query = from d in db.Dealership
select new
{
Dealer = d,
Parts = d.Part.Where
(
p => p.Price < 100.0
&& p.Supplier.Country == "Brazil"
),
Suppliers = d.Part.Select(p => p.Supplier)
};
var dealers = query.ToArray().Select(o => o.Dealer);
foreach (var dealer in dealers)
{
Console.WriteLine(dealer.Name);
foreach (var part in dealer.Part)
{
Console.WriteLine(" " + part.PartId + ", " + part.Price);
Console.WriteLine
(
" "
+ part.Supplier.Name
+ ", "
+ part.Supplier.Country
);
}
}
}
This code will give you a list of Dealerships each containing a filtered list of parts. Each part references a Supplier. The interesting part is that you have to create the anonymous types in the select in the way shown. Otherwise the Part property of the Dealership objects will be empty.
Also, you have to execute the SQL statement before selecting the dealers from the query. Otherwise the Part property of the dealers will again be empty. That is why I put the ToArray() call in the following line:
var dealers = query.ToArray().Select(o => o.Dealer);
But I agree with Darren that this may not be what the users of your library are expecting.
Are you sure this is what you want? The only reason I ask is, once you add the filter on Parts off of Dealerships, your results are no longer Dealerships. You're dealing in special objects that are, for the most part, very close to Dealerships (with the same properties), but the meaning of the "Parts" property is different. Instead of being a relationship between Dealerships and Parts, it's a filtered relationship.
Or to put it another way, if I pull a dealership out of your results and passed to a method I wrote, and then in my method I call:
var count = dealership.Parts.Count();
I'm expecting to get the parts, not the filtered parts from Brazil where the price is less than $100.
If you don't use the dealership object to pass the filtered data, it becomes very easy. It becomes as simple as:
var query = from d in dealerships
select new { DealershipName = d.Name,
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };
If I just had to get the filtered sets like you asked, I'd probably use the technique I mentioned above, and then use a tool like Automapper to copy the filtered results from my anonymous class to the real class. It's not incredibly elegant, but it should work.
I hope that helps! It was an interesting problem.
I know this can work with one single Include. Never test with two includes, but worth the try:
dealerships
.Include( d => d.parts)
.Include( d => d.parts.suppliers)
.Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))
Am I missing something, or aren't you just looking for the Any keyword?
var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) ||
d.parts.suppliers.Any(s => s.country == "brazil"));
Yes that's what I wanted to do I think the next realease of Data Services will have the possiblity to do just that LINQ to REST queries that would be great in the mean time I just switched to load the inverse and Include the related entity that will be loaded multiple times but in theory it just have to load once in the first Include like in this code
return this.Context.SearchHistories.Include("Handle")
.Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);
before I tried to load for any Handle the searchHistories that matched the logic but don't know how using the Include logic you posted so in the mean time I think a reverse lookup would be a not so dirty solution