Source code for franz.openrdf.query.query

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103

################################################################################
# Copyright (c) 2006-2017 Franz Inc.  
# All rights reserved. This program and the accompanying materials are
# made available under the terms of the MIT License which accompanies
# this distribution, and is available at http://opensource.org/licenses/MIT
################################################################################

from __future__ import absolute_import
from __future__ import unicode_literals
from future.builtins import object
from past.builtins import basestring

from future.utils import iteritems, python_2_unicode_compatible

from franz.openrdf.rio.rdfformat import RDFFormat
from franz.openrdf.rio.tupleformat import TupleFormat
from franz.openrdf.util.contexts import output_to
from ..exceptions import IllegalOptionException, QueryMissingFeatureException
from .dataset import ALL_CONTEXTS, Dataset
from .queryresult import GraphQueryResult, TupleQueryResult


[docs]@python_2_unicode_compatible class QueryLanguage(object): registered_languages = [] SPARQL = None PROLOG = None
[docs] def __init__(self, name): self.name = name QueryLanguage.registered_languages.append(self)
def __str__(self): return self.name def __repr__(self): return 'QueryLanguage.' + self.name
[docs] def getName(self): return self.name
[docs] @staticmethod def values(): return QueryLanguage.registered_languages[:]
[docs] @staticmethod def valueOf(name): for ql in QueryLanguage.registered_languages: if ql.name.lower() == name.lower(): return ql return None
QueryLanguage.SPARQL = QueryLanguage('SPARQL') QueryLanguage.PROLOG = QueryLanguage('PROLOG') ############################################################################# ## #############################################################################
[docs]class Query(object): """ A query on a {@link Repository} that can be formulated in one of the supported query languages (for example SeRQL or SPARQL). It allows one to predefine bindings in the query to be able to reuse the same query with different bindings. """
[docs] def __init__(self, queryLanguage=QueryLanguage.SPARQL, query=None, baseURI=None, queryString=None): """ Initialize a query object. :param queryLanguage: Query syntax - the default is SPARQL. :type queryLanguage: QueryLanguage :param query: Query text :type query: string :param baseURI: Prefix used to relsove relative URIs. Default: chosen by the server. :type baseURI: URI :param queryString: Legacy name of the 'query' parameter. :type queryString: string """ if query is None: raise TypeError('query argument is missing.') self.queryLanguage = Query._check_language(queryLanguage) self.queryString = query or queryString self.baseURI = baseURI self.dataset = None self.includeInferred = False self.bindings = {} self.connection = None self.checkVariables = False ## CommonLogic parameters: self.preferred_execution_language = None self.actual_execution_language = None self.subject_comes_first = False
[docs] def setBinding(self, name, value): """ Binds the specified variable to the supplied value. Any value that was previously bound to the specified value will be overwritten. :param name: Variable name. :type name: string :param value: New value for the variable. If a string is passed it will be converted to a literal. :type value: Value|basestring """ if isinstance(value, basestring): value = self._get_connection().createLiteral(value) self.bindings[name] = value
[docs] def setBindings(self, dict): """ Set multiple variable bindings. :param dict: A dictionary of bindings. :type dict: dict[string, Value] """ if not dict: return for key, value in iteritems(dict): self.setBinding(key, value)
[docs] def removeBinding(self, name): """ Removes a previously set binding on the supplied variable. Calling this method with an unbound variable name has no effect. :param name: Variable name. :type name: string """ self.bindings.pop(name)
[docs] def getBindings(self): """ Retrieve the bindings that have been set on this query. :return: A dictionary of bindings. :rtype: dict[string, Value] """ return self.bindings
[docs] def setDataset(self, dataset): """ Select the dataset against which to evaluate this query, overriding any dataset that is specified in the query itself. :param dataset: A dataset object. :type dataset: Dataset """ self.dataset = dataset
[docs] def getDataset(self): """ Get the dataset against which this query will operate. :return: Dataset. :rtype: Dataset """ return self.dataset
[docs] def setContexts(self, contexts): """ Assert a set of contexts (named graphs) that filter all triples. :param contexts: List of graph URIs. :type contexts: list[URI|string] """ if not contexts: return ds = Dataset() for cxt in contexts: if isinstance(cxt, basestring): cxt = self._get_connection().createURI(cxt) ds.addNamedGraph(cxt) self.setDataset(ds)
[docs] def setIncludeInferred(self, includeInferred): """ Determine whether evaluation results of this query should include inferred statements. The default setting is ``True``. :param includeInferred: If ``True`` include inferred triples in query results. :type includeInferred: bool """ self.includeInferred = includeInferred
[docs] def getIncludeInferred(self): """ Check whether this query will return inferred statements. :return: ``True`` if inferred triples are included, ``False`` otherwise. :rtype: bool """ return self.includeInferred
[docs] def setCheckVariables(self, setting): """ Determine whether the presence of variables in the select clause not referenced in a triple is flagged. :param setting: ``True`` if variables should be checked, ``False`` otherwise. :type setting: bool """ self.checkVariables = setting
[docs] def setConnection(self, connection): """ Internal call to embed the connection into the query. """ self.connection = connection
def _get_connection(self): return self.connection
[docs] def evaluate_generic_query(self, count=False, accept=None, callback=None, analyze=False, analysisTechnique=None, analysisTimeout=None, update=False): """ Evaluate a SPARQL or PROLOG query, which may be a 'select', 'construct', 'describe' or 'ask' query (in the SPARQL case). Return an appropriate response. :param count: If ``True`` return only the count of the query results. :type count: bool :param accept: Set to ``'application/sparql-results+xml'`` or ``'application/sparql-results+json'`` to retrieve the result as a string in the specified format. The default is to return a :class:`QueryResult` object. :type accept: string :param analyze: If ``True`` perform query analysis for SPARQL queries. :type analyze: bool :param analysisTechnique: Control the method of analysis. Can be either `"executed"` (default). meaning that the query will be executed to perform dynamic analysis, or ``"static"``. :type analysisTechnique: string :param analysisTimeout: Number of second to run the query if executed for analysis. :type analysisTimeout: float :param update: If ``True`` this is an update query and the result will be a boolean. :type update: bool :return: The result can be either: - A boolean (for update queries). - A dictionary containing the results. - A string (if ``accept`` was passed). - An integer (if ``count=True``). :rtype: dict|string|int|bool """ conn = self._get_connection() namedContexts = conn._contexts_to_ntriple_contexts( self.dataset.getNamedGraphs() if self.dataset else None) regularContexts = conn._contexts_to_ntriple_contexts( self.dataset.getDefaultGraphs() if self.dataset else ALL_CONTEXTS) bindings = None if self.bindings: bindings = {} for vbl, val in list(self.bindings.items()): bindings[vbl] = conn._convert_term_to_mini_term(val) mini = conn._get_mini_repository() if self.queryLanguage == QueryLanguage.SPARQL: response = mini.evalSparqlQuery(self.queryString, context=regularContexts, namedContext=namedContexts, infer=self.includeInferred, bindings=bindings, checkVariables=self.checkVariables, count=count, accept=accept, callback=callback, analyze=analyze, analysisTechnique=analysisTechnique, analysisTimeout=analysisTimeout, update=update) elif self.queryLanguage == QueryLanguage.PROLOG: if namedContexts: raise QueryMissingFeatureException("Prolog queries do not support the datasets (named graphs) option.") if analyze: raise QueryMissingFeatureException("Prolog queries do not support analysis.") # evalPrologQuery is already always done as if update=True response = mini.evalPrologQuery(self.queryString, infer=self.includeInferred, count=count, accept=accept, callback=callback) else: raise ValueError('Unsupported query language: %s' % self.queryLanguage) return response
@staticmethod def _check_language(queryLanguage): if queryLanguage == 'SPARQL': return QueryLanguage.SPARQL if queryLanguage == 'PROLOG': return QueryLanguage.PROLOG if not queryLanguage in [QueryLanguage.SPARQL, QueryLanguage.PROLOG]: raise IllegalOptionException("Can't evaluate the query language '%s'. Options are: SPARQL and PROLOG." % queryLanguage) return queryLanguage
[docs]class TupleQuery(Query): """ A query that returns tuples (i.e. sets of variable bindings). """
[docs] def evaluate(self, count=False, output=None, output_format=TupleFormat.TABLE): """Execute the embedded query against the RDF store. Return an iterator that produces for each step a tuple of values (resources and literals) corresponding to the variables or expressions in a 'select' clause (or its equivalent). :param count: If ``True`` return the number of result rows instead of the usual iterator. :type count: bool :param output: A file name, file descriptor or a file-like object to save the results to (optional). ``True`` can be used as a synonym for stdout. :type output: str|file|int|bool :param output_format: Serialization format for ``output``. The default is TABLE. :type output_format: RDFFormat :return: Either an iterator over results, an integer (the number of results, if ``count`` is used) or ``None`` (if ``output`` is used). :rtype: TupleQueryResult|int """ with output_to(output) as out: callback = None if output is None else out.write accept = None if output is None else TupleFormat.mime_type_for_format(output_format) response = self.evaluate_generic_query( count=count, accept=accept, callback=callback) if count: return response if output is not None: return None return TupleQueryResult(response['names'], response['values'])
[docs] def analyze(self, analysisTechnique=None, analysisTimeout=None): """ Analyze the query. Analysis is only available for SPARQL queries. :param analysisTechnique: Control the method of analysis. Can be either `"executed"` (default). meaning that the query will be executed to perform dynamic analysis, or ``"static"``. :type analysisTechnique: string :param analysisTimeout: Number of second to run the query if executed for analysis. :type analysisTimeout: float :return: Analysis result as a dictionary. :rtype: dict """ response = self.evaluate_generic_query(analyze=True, analysisTechnique=analysisTechnique, analysisTimeout=analysisTimeout) return response
[docs]class UpdateQuery(Query): """ An update query. """
[docs] def evaluate(self): """ Execute the embedded update against the RDF store. :return: ``True`` if the query changed the store, ``False`` otherwise. :rtype: bool """ return self.evaluate_generic_query(update=True)
[docs]class GraphQuery(Query): """ A query that returns statements (see :class:`Statement`). """
[docs] def evaluate(self, output=None, output_format=RDFFormat.TABLE): """ Execute the embedded query against the RDF store. :param output: A file name, file descriptor or a file-like object to save the results to (optional). ``True`` can be used as a synonym for stdout. :type output: str|file|int|bool :param output_format: Serialization format for ``output``. The default is TABLE. :type output_format: RDFFormat :return: An iterator over statements or None (if ``output`` is used).. :rtype: GraphQueryResult """ with output_to(output) as out: callback = None if output is None else out.write accept = None if output is None else RDFFormat.mime_type_for_format(output_format) response = self.evaluate_generic_query(accept=accept, callback=callback) if output is None: return GraphQueryResult(response) else: return None
[docs]class BooleanQuery(Query): """ A query that returns a boolean. """
[docs] def evaluate(self): """ Execute the embedded query against the RDF store. :return: A boolean. :rtype: bool """ return self.evaluate_generic_query()