using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ModelData.BusinessModel.RDF
{
/// \addtogroup RDF
/// @{
/// \class Queries
///
/// \brief Класс, содержащий текст запросов
/// Методы класса возвращают строку с запросом, при необходимости подставляют параметры
/// \author Denis
/// \date 31.03.2019
public class Queries
{
#region Prefixes
/// \brief Доступное для чтения поле, содержащее краткий префикс (Key) и полное значение (Value) используемого ресурса
public static readonly Dictionary<string, string> Prefixes = new Dictionary<string, string>
{
["rdf"] = "<http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
["rdfs"] = "<http://www.w3.org/2000/01/rdf-schema#>",
["owl"] = "<http://www.w3.org/2002/07/owl#>",
["xsd"] = "<http://www.w3.org/2001/XMLSchema#>",
["geo"] = "<http://www.w3.org/2003/01/geo/wgs84_pos:>",
["geonames"] = "<http://www.geonames.org/ontology#>",
["time"] = "<https://www.w3.org/2006/time#>",
["map"] = "<http://jena.denhome.keenetic.link/map#>",
["foaf"] = "<http://xmlns.com/foaf/0.1/>",
["dc"] = "<http://purl.org/dc/elements/1.1/>",
["dcterms"] = "<http://purl.org/dc/terms/>"
};
#endregion
#region Queries
/// \fn public static string AllSettlementsInInterval(string from, string to)
///
/// \brief Выполняет поиск поселений и изменных поселений в определенном временном интервале.
/// \code
///SELECT DISTINCT (?settlement as ?url) ?title ?lat ?long ?settlementType ?typeName ?region ?regionName (?maxc as ?maxPop)
///WHERE
///{
/// ?settlement a map:Settlement;
/// geonames:name ?title;
/// geo:lat ?lat;
/// geo:long ?long.
/// OPTIONAL {?settlement time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
/// FILTER (!bound(?wasFound) || ?wasFoundYear <= "to"^^xsd:gYear )
/// {
/// SELECT ?settlement (MAX(?populationNotNULL) AS ?maxc)
/// WHERE
/// {
/// ?editedSettlement a map:EditedSettlement; time:before ?settlement; time:hasBeginning ?dateBegin.
/// ?dateBegin time:year ?yearBegin.
/// OPTIONAL {?editedSettlement time:hasEnd ?dateEnd. ?dateEnd time:year ?yearEnd.}
/// ## датаНачало должна попадать в промежуток до верхней временной границы
/// ## а датаКонец либо не указана, либо попадает в промежуток после нижней временно границы
/// FILTER (?yearBegin <= "to"^^xsd:gYear && (!bound(?dateEnd) || ?yearEnd > "from"^^xsd:gYear))
/// OPTIONAL {?editedSettlement geonames:population ?population.}
/// OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
///
/// ## заполни переменную ?populationNotNULL так: если ?population НЕ задан (т.е. NULL), тогда 0, иначе (т.е. задан) ?population
/// BIND (IF (!BOUND(?population), "0"^^xsd:integer, ?population) as ?populationNotNULL)
/// }
/// Group by ?settlement
/// }
/// {
/// SELECT ?settlement ?settlementType ?typeName ?region ?regionName
/// WHERE
/// {
/// ?editedSettlement a map:EditedSettlement; time:before ?settlement; time:hasBeginning ?dateBegin.
/// ?dateBegin time:year ?yearBegin.
/// OPTIONAL {?editedSettlement time:hasEnd ?dateEnd. ?dateEnd time:year ?yearEnd.}
/// ## датаНачало должна попадать в промежуток до верхней временной границы
/// ## а датаКонец либо не указана, либо попадает в промежуток после нижней временно границы
/// FILTER (?yearBegin <= "to"^^xsd:gYear && (!bound(?dateEnd) || ?yearEnd > "from"^^xsd:gYear))
/// OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
/// OPTIONAL {?editedSettlement rdfs:partOf ?region. ?region geonames:name ?regionName}
/// }
/// }
///}
/// \endcode
/// \author Denis
/// \date 31.03.2019
///
/// \param from С какого года выполнять поиск.
/// \param to По какой год выполнять поиск.
///
/// \returns A string.
public static string AllSettlementsInInterval(string from, string to)
{
return GetAllPrefixes() +
@"
SELECT DISTINCT (?settlement as ?url) ?title ?lat ?long ?settlementType ?typeName ?region ?regionName (?maxc as ?maxPop)
WHERE
{
?settlement a map:Settlement;
geonames:name ?title;
geo:lat ?lat;
geo:long ?long.
OPTIONAL {?settlement time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
FILTER (!bound(?wasFound) || ?wasFoundYear <= " + '"' + to + '"' + @"^^xsd:gYear )
{
SELECT ?settlement (MAX(?populationNotNULL) AS ?maxc)
WHERE
{
?editedSettlement a map:EditedSettlement; time:before ?settlement; time:hasBeginning ?dateBegin.
?dateBegin time:year ?yearBegin.
OPTIONAL {?editedSettlement time:hasEnd ?dateEnd. ?dateEnd time:year ?yearEnd.}
## датаНачало должна попадать в промежуток до верхней временной границы
## а датаКонец либо не указана, либо попадает в промежуток после нижней временно границы
FILTER (?yearBegin <= " + '"' + to + '"' + @"^^xsd:gYear && (!bound(?dateEnd) || ?yearEnd > " + '"' + from + '"' + @"^^xsd:gYear))
OPTIONAL {?editedSettlement geonames:population ?population.}
OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
## заполни переменную ?populationNotNULL так: если ?population НЕ задан (т.е. NULL), тогда 0, иначе (т.е. задан) ?population
BIND (IF (!BOUND(?population), " + "\"0\"" + @"^^xsd:integer, ?population) as ?populationNotNULL)
}
Group by ?settlement
}
{
SELECT ?settlement ?settlementType ?typeName ?region ?regionName
WHERE
{
?editedSettlement a map:EditedSettlement; time:before ?settlement; time:hasBeginning ?dateBegin.
?dateBegin time:year ?yearBegin.
OPTIONAL {?editedSettlement time:hasEnd ?dateEnd. ?dateEnd time:year ?yearEnd.}
## датаНачало должна попадать в промежуток до верхней временной границы
## а датаКонец либо не указана, либо попадает в промежуток после нижней временно границы
FILTER (?yearBegin <= " + '"' + to + '"' + @"^^xsd:gYear && (!bound(?dateEnd) || ?yearEnd > " + '"' + from + '"' + @"^^xsd:gYear))
OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
OPTIONAL {?editedSettlement rdfs:partOf ?region. ?region geonames:name ?regionName}
}
}
}
";
}
/// \fn public static string InfoAboutSettlement(string URL)
///
/// \brief Полная информация о поселении
/// \code
///SELECT DISTINCT ?title ?lat ?long ?wasFoundYear ?legend ?founder ?person ?foundersRegion ?editedSettlement ?sourceURL ?sourceTitle ?sourceCreator ?year ?settlementType ?typeName ?region ?regionName ?editedName ?alternateName ?population ?populationFemales ?populationMales
///WHERE
///{
/// {
/// SELECT * WHERE
/// {
/// url_ geonames:name ?title;
/// geo:lat ?lat; geo:long ?long.
///
/// OPTIONAL {url_ time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
/// OPTIONAL {url_ map:legend ?legend.}
/// OPTIONAL
/// {
/// url_ map:founder ?founder.
/// OPTIONAL {?founder foaf:Person ?person}
/// OPTIONAL {?founder map:foundersRegion ?foundersRegion}
/// }
/// }
/// }
/// UNION
/// {
/// SELECT * WHERE
/// {
/// ?editedSettlement a map:EditedSettlement;
/// time:before url_;
/// dc:source ?source;
/// time:hasBeginning ?date.
/// ?date time:year ?year.
/// ?source a dcterms:bibliographicResource; dc:identifier ?sourceURL; dc:title ?sourceTitle.
/// OPTIONAL {?source dc:creator? sourceCreator.}
/// OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
/// OPTIONAL {?editedSettlement rdfs:partOf ?region. ?region geonames:name ?regionName}
/// OPTIONAL {?editedSettlement geonames:name ?editedName.}
/// OPTIONAL {?editedSettlement geonames:alternateName ?alternateName.}
/// OPTIONAL {?editedSettlement geonames:population ?population.}
/// OPTIONAL {?editedSettlement map:populationFemalesCount ?populationFemales.}
/// OPTIONAL {?editedSettlement map:populationMalesCount ?populationMales.}
/// OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
/// }
/// }
///}
/// \endcode
/// \author Denis
/// \date 31.03.2019
///
/// \param URL URL искомого поселения.
///
/// \returns A string.
public static string InfoAboutSettlement(string URL)
{
string url_ = GetObjFromURL(URL);
return GetAllPrefixes() +
@"SELECT DISTINCT ?title ?lat ?long ?wasFoundYear ?legend ?founder ?person ?foundersRegion ?editedSettlement ?sourceURL ?sourceTitle ?sourceCreator ?year ?settlementType ?typeName ?region ?regionName ?editedName ?alternateName ?population ?populationFemales ?populationMales
WHERE
{
{
SELECT * WHERE
{
" + url_ + @" geonames:name ?title;
geo:lat ?lat; geo:long ?long.
OPTIONAL {" + url_ + @" time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
OPTIONAL {" + url_ + @" map:legend ?legend.}
OPTIONAL
{
" + url_ + @" map:founder ?founder.
OPTIONAL {?founder foaf:Person ?person}
OPTIONAL {?founder map:foundersRegion ?foundersRegion}
}
}
}
UNION
{
SELECT * WHERE
{
?editedSettlement a map:EditedSettlement;
time:before " + url_ + @";
dc:source ?source;
time:hasBeginning ?date.
?date time:year ?year.
?source a dcterms:bibliographicResource; dc:identifier ?sourceURL; dc:title ?sourceTitle.
OPTIONAL {?source dc:creator ?sourceCreator.}
OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
OPTIONAL {?editedSettlement rdfs:partOf ?region. ?region geonames:name ?regionName}
OPTIONAL {?editedSettlement geonames:name ?editedName.}
OPTIONAL {?editedSettlement geonames:alternateName ?alternateName.}
OPTIONAL {?editedSettlement geonames:population ?population.}
OPTIONAL {?editedSettlement map:populationFemalesCount ?populationFemales.}
OPTIONAL {?editedSettlement map:populationMalesCount ?populationMales.}
OPTIONAL {?editedSettlement map:settlementType ?settlementType. ?settlementType geonames:name ?typeName}
}
}
}";
}
/// \fn public static string Years()
///
/// \brief Запрос наименьшего и наибольшего упоминаего годов
/// \code
///SELECT ?min ?max
///WHERE
///{
/// {
/// SELECT (MIN(?year) AS ?minEditedYear) (MAX(?year) AS ?maxEditedYear)
/// WHERE
/// {
/// ?editedSettlement a map:EditedSettlement; time:hasBeginning ?date.
/// ?date time:year ?year.
/// }
/// }
/// {
/// SELECT (MIN(?minIndSetYear) AS ?minSetYear) (MAX(?maxIndSetYear) AS ?maxSetYear)
/// WHERE
/// {
/// ?settlement a map:Settlement.
/// OPTIONAL {?settlement time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
///
/// ## костыль
/// ## если поля не существовали, MIN возвращал 1 вместо NULL, поэтому нарочно кастую к невозможным значениям, чтобы потом сравнить с существующими (см. другие два BIND)
/// BIND (IF (!BOUND(?wasFound), "3000"^^xsd:gYear, ?wasFoundYear) as ?minIndSetYear)
/// BIND (IF (!BOUND(?wasFound), "-3000"^^xsd:gYear, ?wasFoundYear) as ?maxIndSetYear)
/// }
/// }
/// ## выбрать наименьшую упоминаемую дату среди *наименьшей даты основания* и *наименьшей даты изменения*
/// BIND (IF (?minSetYear < ?minEditedYear, ?minSetYear, ?minEditedYear) as ?min)
/// BIND (IF (?maxSetYear > ?maxEditedYear, ?maxSetYear, ?maxEditedYear) as ?max)
///}
/// \endcode
/// \author Denis
/// \date 31.03.2019
///
/// \returns A string.
public static string Years()
{
return GetAllPrefixes() +
@"SELECT ?min ?max
WHERE
{
{
SELECT (MIN(?year) AS ?minEditedYear) (MAX(?year) AS ?maxEditedYear)
WHERE
{
?editedSettlement a map:EditedSettlement; time:hasBeginning ?date.
?date time:year ?year.
}
}
{
SELECT (MIN(?minIndSetYear) AS ?minSetYear) (MAX(?maxIndSetYear) AS ?maxSetYear)
WHERE
{
?settlement a map:Settlement.
OPTIONAL {?settlement time:hasBeginning ?wasFound. ?wasFound time:year ?wasFoundYear}
## костыль
## если поля не существовали, MIN возвращал 1 вместо NULL, поэтому нарочно кастую к невозможным значениям, чтобы потом сравнить с существующими (см. другие два BIND)
BIND (IF (!BOUND(?wasFound), " + "\"3000\"" + @"^^xsd:gYear, ?wasFoundYear) as ?minIndSetYear)
BIND (IF (!BOUND(?wasFound), " + "\"-3000\"" + @"^^xsd:gYear, ?wasFoundYear) as ?maxIndSetYear)
}
}
## выбрать наименьшую упоминаемую дату среди *наименьшей даты основания* и *наименьшей даты изменения*
BIND (IF (?minSetYear < ?minEditedYear, ?minSetYear, ?minEditedYear) as ?min)
BIND (IF (?maxSetYear > ?maxEditedYear, ?maxSetYear, ?maxEditedYear) as ?max)
}
";
}
/// \fn public static string AllSettlementTypes()
///
/// \brief Запрос всех возможных типов поселений за все периоды
/// \code
/// SELECT ?settlementType ?typeName WHERE
/// {
/// ?settlementType a map:SettlementTypes; geonames:name ?typeName.
/// }
/// \endcode
/// \author Denis
/// \date 17.05.2019
///
/// \returns A string.
public static string AllSettlementTypes()
{
return GetAllPrefixes() +
@"SELECT ?settlementType ?typeName WHERE
{
?settlementType a map:SettlementTypes; geonames:name ?typeName.
}
";
}
/// \fn public static string AllRegions()
///
/// \brief Запрос всех возможных административных единиц (регионов) за все периоды
/// \code
/// SELECT ?region ?regionName WHERE
/// {
/// ?region a map:Region; geonames:name ?regionName.
/// }
/// \endcode
/// \author Denis
/// \date 17.05.2019
///
/// \returns A string.
public static string AllRegions()
{
return GetAllPrefixes() +
@"SELECT ?region ?regionName WHERE
{
?region a map:Region; geonames:name ?regionName.
}
";
}
/// \fn public static string AllSettlementsName(string subName)
///
/// \brief Поиск всех поселений, чьи названия включают переданную подстроку
/// \code
///select distinct ?name
///where
///{
/// ?settlement a map:Settlement; geonames:name ?name.
/// FILTER regex(?name, "subName", "i")
///}
/// \endcode
/// \author Denis
/// \date 31.03.2019
///
/// \param subName Подстрока для поиска.
///
/// \returns A string.
public static string AllSettlementsName(string subName)
{
return GetPrefix("map") + GetPrefix("geonames") +
@"
select distinct ?name
where
{
?settlement a map:Settlement; geonames:name ?name.
FILTER regex(?name, " + '"' + subName + "\", \"i\"" + @")
}
";
}
#endregion
#region Tools
/// \fn private static string GetObjFromURL(string URL)
///
/// \brief Получение URL, отделенного от префикса
/// Выполняет отделение значимой части полного URL от постоянного префикса
/// Например, полный URL: "http://jena.denhome.keenetic.link/map#Saratov_btjzkyfa_origin", где "http://jena.denhome.keenetic.link/map#" - префикс, а "Saratov_btjzkyfa_origin" - значимая часть.
/// \warning Если префикса нет в списке всех префиксов, отделить части не получится и будет возвращен переданный полный URL.
/// \author Denis
/// \date 31.03.2019
///
/// \param URL Полный URL сущности.
///
/// \returns The object from URL.
private static string GetObjFromURL(string URL)
{
string[] input = URL.Split('#');
string url_ = "";
foreach (var item in Prefixes)
if (item.Value.Contains(input[0]))
{
url_ = item.Key + ":" + input[1];
break;
}
return (url_ != "") ? url_ : URL;
}
/// \fn private static string GetPrefix(string key)
///
/// \brief Составляет строку из префикса и его значения
///
/// \author Denis
/// \date 31.03.2019
///
/// \param key Ключ - краткое наименование префикса.
///
/// \returns The prefix.
private static string GetPrefix(string key)
{
return "PREFIX " + key + ": " + Prefixes[key] + " ";
}
/// \fn private static string GetAllPrefixes()
///
/// \brief Составляет строку из всех префиксов
///
/// \author Denis
/// \date 31.03.2019
///
/// \returns all prefixes.
private static string GetAllPrefixes()
{
string t = "";
foreach (var item in Prefixes)
{
t += "PREFIX " + item.Key + ": " + Prefixes[item.Key] + " ";
}
return t;
}
#endregion
}
/// @}
}
/*
insert data
{
map:TatishchevskijRegion a map:Region; geonames:name "Татищевский район".
}
*/
/*
FILTER regex(?name, "^ali", "i")
^ дает указаение, что искомое стоит в начале слова == "Alice"
FILTER regex(str(?mbox), "@work.example")
@ дает указаение, что искомое стоит в конце слова == <mailto:alice@work.example>
i - параметр, указывающий что регистр поиска не важен
ПОИСК (по regex) здесь: https://www.w3.org/TR/rdf-sparql-query/
*/