File: base.py

package info (click to toggle)
rdflib-sqlalchemy 0.5.4%2Bgit99f4689-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,544 kB
  • sloc: python: 2,418; sh: 20; makefile: 3
file content (223 lines) | stat: -rw-r--r-- 8,328 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
"""Base classes for the store."""
from rdflib import Literal
from rdflib.graph import Graph, QuotedGraph
from rdflib.plugins.stores.regexmatching import REGEXTerm
from sqlalchemy.sql import expression

from rdflib_sqlalchemy.termutils import (
    type_to_term_combination,
    statement_to_term_combination,
)


class SQLGeneratorMixin(object):
    """SQL statement generator mixin for the SQLAlchemy store."""

    def _build_type_sql_command(self, member, klass, context):
        """Build an insert command for a type table."""
        # columns: member,klass,context
        rt = self.tables["type_statements"].insert()
        return rt, {
            "member": member,
            "klass": klass,
            "context": context.identifier,
            "termComb": int(type_to_term_combination(member, klass, context))
        }

    def _build_literal_triple_sql_command(self, subject, predicate, obj, context):
        """
        Build an insert command for literal triples.

        These triples correspond to RDF statements where the object is a Literal,
        e.g. `rdflib.Literal`.

        """
        triple_pattern = int(
            statement_to_term_combination(subject, predicate, obj, context)
        )

        command = self.tables["literal_statements"].insert()
        values = {
            "subject": subject,
            "predicate": predicate,
            "object": obj,
            "context": context.identifier,
            "termComb": triple_pattern,
            "objLanguage": isinstance(obj, Literal) and obj.language or None,
            "objDatatype": isinstance(obj, Literal) and obj.datatype or None,
        }
        return command, values

    def _build_triple_sql_command(self, subject, predicate, obj, context, quoted):
        """
        Build an insert command for regular triple table.

        """
        stmt_table = (
            self.tables["quoted_statements"]
            if quoted
            else self.tables["asserted_statements"]
        )

        triple_pattern = statement_to_term_combination(
            subject,
            predicate,
            obj,
            context,
        )
        command = stmt_table.insert()

        if quoted:
            params = {
                "subject": subject,
                "predicate": predicate,
                "object": obj,
                "context": context.identifier,
                "termComb": triple_pattern,
                "objLanguage": isinstance(obj, Literal) and obj.language or None,
                "objDatatype": isinstance(obj, Literal) and obj.datatype or None
            }
        else:
            params = {
                "subject": subject,
                "predicate": predicate,
                "object": obj,
                "context": context.identifier,
                "termComb": triple_pattern,
            }
        return command, params

    def build_clause(self, table, subject, predicate, obj, context=None, typeTable=False):
        """Build WHERE clauses for the supplied terms and, context."""
        if typeTable:
            clauseList = [
                self.build_type_member_clause(subject, table),
                self.build_type_class_clause(obj, table),
                self.build_context_clause(context, table)
            ]
        else:
            clauseList = [
                self.build_subject_clause(subject, table),
                self.build_predicate_clause(predicate, table),
                self.build_object_clause(obj, table),
                self.build_context_clause(context, table),
                self.build_literal_datatype_clause(obj, table),
                self.build_literal_language_clause(obj, table)
            ]

        clauseList = [clause for clause in clauseList if clause is not None]
        if clauseList:
            return expression.and_(*clauseList)
        else:
            return None

    def build_literal_datatype_clause(self, obj, table):
        """Build Literal and datatype clause."""
        if isinstance(obj, Literal) and obj.datatype is not None:
            return table.c.objDatatype == obj.datatype
        else:
            return None

    def build_literal_language_clause(self, obj, table):
        """Build Literal and language clause."""
        if isinstance(obj, Literal) and obj.language is not None:
            return table.c.objLanguage == obj.language
        else:
            return None

    # Where Clause  utility Functions
    # The predicate and object clause builders are modified in order
    # to optimize subjects and objects utility functions which can
    # take lists as their last argument (object, predicate - respectively)

    def build_subject_clause(self, subject, table):
        """Build Subject clause."""
        if isinstance(subject, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.subject.op("REGEXP")(subject)
        elif isinstance(subject, list):
            # clauseStrings = [] --- unused
            return expression.or_(
                *[self.build_subject_clause(s, table) for s in subject if s])
        elif isinstance(subject, (QuotedGraph, Graph)):
            return table.c.subject == subject.identifier
        elif subject is not None:
            return table.c.subject == subject
        else:
            return None

    def build_predicate_clause(self, predicate, table):
        """
        Build Predicate clause.

        Capable of taking a list of predicates as well, in which case
        subclauses are joined with 'OR'.

        """
        if isinstance(predicate, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.predicate.op("REGEXP")(predicate)
        elif isinstance(predicate, list):
            return expression.or_(
                *[self.build_predicate_clause(p, table) for p in predicate if p])
        elif predicate is not None:
            return table.c.predicate == predicate
        else:
            return None

    def build_object_clause(self, obj, table):
        """
        Build Object clause.

        Capable of taking a list of objects as well, in which case subclauses
        are joined with 'OR'.

        """
        if isinstance(obj, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.object.op("REGEXP")(obj)
        elif isinstance(obj, list):
            return expression.or_(
                *[self.build_object_clause(o, table) for o in obj if o])
        elif isinstance(obj, (QuotedGraph, Graph)):
            return table.c.object == obj.identifier
        elif obj is not None:
            return table.c.object == obj
        else:
            return None

    def build_context_clause(self, context, table):
        """Build Context clause."""
        if isinstance(context, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.context.op("regexp")(context.identifier)
        elif context is not None and context.identifier is not None:
            return table.c.context == context.identifier
        else:
            return None

    def build_type_member_clause(self, subject, table):
        """Build Type Member clause."""
        if isinstance(subject, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.member.op("regexp")(subject)
        elif isinstance(subject, list):
            return expression.or_(
                *[self.build_type_member_clause(s, table) for s in subject if s])
        elif subject is not None:
            return table.c.member == subject
        else:
            return None

    def build_type_class_clause(self, obj, table):
        """Build Type Class clause."""
        if isinstance(obj, REGEXTerm):
            # TODO: this work only in mysql. Must adapt for postgres and sqlite
            return table.c.klass.op("regexp")(obj)
        elif isinstance(obj, list):
            return expression.or_(
                *[self.build_type_class_clause(o, table) for o in obj if o])
        elif obj is not None:
            return obj and table.c.klass == obj
        else:
            return None