~ubuntu-branches/ubuntu/precise/kde4libs/precise

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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/*
   This file is part of the Nepomuk KDE project.
   Copyright (C) 2009-2010 Sebastian Trueg <trueg@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) version 3, or any
   later version accepted by the membership of KDE e.V. (or its
   successor approved by the membership of KDE e.V.), which shall
   act as a proxy defined in Section 6 of version 3 of the license.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _NEPOMUK_QUERY_QUERY_BUILDER_DATA_H_
#define _NEPOMUK_QUERY_QUERY_BUILDER_DATA_H_

#include <QtCore/QString>
#include <QtCore/QLatin1String>
#include <QtCore/QSet>
#include <QtCore/QStack>

#include "literal.h"
#include "query.h"
#include "query_p.h"
#include "groupterm_p.h"

namespace {
/// A hack to avoid passing extended chars to the bif:search_excerpts method which cannot handle
/// utf8 chars which use more than one char, ie. wide chars.
/// Thus, we simply truncate each term at the first wide char.
QStringList stripExtendedCharsHack(const QStringList& terms) {
    QStringList newTerms;
    foreach(const QString& term, terms) {
        int i = 0;
        while(i < term.length()) {
            if(term[i].unicode() > 0x7f) {
                break;
            }
            ++i;
        }
        if(i > 0) {
            newTerms.append(term.left(i));
        }
    }
    return newTerms;
}
}

namespace Nepomuk {
    namespace Query {
        class QueryBuilderData
        {
        private:
            /// the query we are working for
            const QueryPrivate* m_query;

            struct OrderVariable {
                OrderVariable(int w, const QString& n, Qt::SortOrder o)
                    : weight(w),
                      name(n),
                      sortOrder(o) {
                }
                int weight;
                QString name;
                Qt::SortOrder sortOrder;
            };

            /// see uniqueVarName()
            struct GroupTermPropertyCache {
                const GroupTermPrivate* term;
                QHash<Types::Property, QString> variableNameHash;
            };

            /// a running counter for unique variable names
            int m_varNameCnt;

            /// copy of the flags as set in Query::toSparqlQuery
            const Query::SparqlFlags m_flags;

            /// custom variables that have been added via ComparisonTerm::setVariableName
            QSet<QString> m_customVariables;

            /// variables that are used for sorting set via ComparisonTerm::setSortWeight
            QList<OrderVariable> m_orderVariables;

            /// all full-text matching scoring variables
            QHash<QString, int> m_scoreVariables;

            /// The depth of a term in the query. This is only changed by ComparisonTerm
            int m_depth;

            /// a stack of the group terms, the top item is the group the currently handled term is in
            QStack<GroupTermPropertyCache> m_groupTermStack;

            /// full text search terms with depth 0 for which bif:search_excerpt will be used
            QHash<QString, QStringList> m_fullTextSearchTerms;

        public:
            inline QueryBuilderData( const QueryPrivate* query, Query::SparqlFlags flags )
                : m_query( query ),
                  m_varNameCnt( 0 ),
                  m_flags( flags ),
                  m_depth( 0 ) {
            }

            inline const QueryPrivate* query() const {
                return m_query;
            }

            inline Query::SparqlFlags flags() const {
                return m_flags;
            }

            inline int depth() const {
                return m_depth;
            }

            inline void increaseDepth() {
                ++m_depth;
            }

            inline void decreaseDepth() {
                --m_depth;
            }

            /// used by ComparisonTerm to register var names set via ComparisonTerm::setVariableName
            /// to allow them to be reused in the same way auto-generated variables are reused in
            /// uniqueVarName()
            inline void registerVarName( const Types::Property& property, const QString& varName ) {
                if( property.isValid() &&
                    property.maxCardinality() == 1 &&
                    !m_groupTermStack.isEmpty() ) {
                    GroupTermPropertyCache& gpc = m_groupTermStack.top();
                    gpc.variableNameHash[property] = varName;
                }
            }

            /// used by different implementations of TermPrivate::toSparqlGraphPattern and Query::toSparqlQuery
            ///
            /// we use a simple query optimization trick here that Virtuoso cannot pull itself since it does
            /// not know about NRL cardinalities:
            /// In one group term we can use the same variable name for comparisons on the same property with a
            /// cardinality of 1. This reduces the number of match candidates for Virtuoso, thus, significantly
            /// speeding up the query.
            inline QString uniqueVarName( const Types::Property& property = Types::Property(), bool* firstUse = 0 ) {
                if( property.isValid() &&
                    property.maxCardinality() == 1 &&
                    !m_groupTermStack.isEmpty() ) {
                    // use only one variable name for all occurrences of this property
                    GroupTermPropertyCache& gpc = m_groupTermStack.top();
                    QHash<Types::Property, QString>::const_iterator it = gpc.variableNameHash.constFind( property );
                    if( it == gpc.variableNameHash.constEnd() ) {
                        const QString v = QLatin1String( "?v" ) + QString::number( ++m_varNameCnt );
                        gpc.variableNameHash.insert( property, v );
                        if(firstUse)
                            *firstUse = true;
                        return v;
                    }
                    else {
                        if(firstUse)
                            *firstUse = false;
                        return *it;
                    }
                }
                else {
                    if(firstUse)
                        *firstUse = true;
                    return QLatin1String( "?v" ) + QString::number( ++m_varNameCnt );
                }
            }

            /// used by ComparisonTerm to add variable names set via ComparisonTerm::setVariableName
            inline void addCustomVariable( const QString& name ) {
                m_customVariables << name;
            }

            /// used by Query::toSparqlQuery
            inline QStringList customVariables() const {
                return m_customVariables.toList();
            }

            /// used by ComparisonTerm to add sorting variables (names include the '?')
            inline void addOrderVariable( const QString& name, int weight, Qt::SortOrder order ) {
                int i = 0;
                while( i < m_orderVariables.count() &&
                       m_orderVariables[i].weight > weight )
                    ++i;
                m_orderVariables.insert( i, OrderVariable( weight, name, order ) );
            }

            /// used by LiteralTerm and ComparisonTerm
            /// states that "varName" is a value matching the given full text search terms
            inline void addFullTextSearchTerms( const QString& varName, const QStringList& terms ) {
                m_fullTextSearchTerms.insert( varName, terms );
            }

            /// used by AndTermPrivate and OrTermPrivate in toSparqlGraphPattern
            inline void pushGroupTerm( const GroupTermPrivate* group ) {
                GroupTermPropertyCache gpc;
                gpc.term = group;
                m_groupTermStack.push( gpc );
            }

            /// used by AndTermPrivate and OrTermPrivate in toSparqlGraphPattern
            inline void popGroupTerm() {
                m_groupTermStack.pop();
            }

            /// used by Query::toSparqlQuery
            inline QString buildOrderString() const {
                QList<OrderVariable> orderVars = m_orderVariables;
                if( m_query->m_fullTextScoringEnabled &&
                    !m_fullTextSearchTerms.isEmpty() ) {
                    orderVars.append( OrderVariable( 0, QLatin1String("?_n_f_t_m_s_"), m_query->m_fullTextScoringSortOrder ) );
                }

                if( !orderVars.isEmpty() ) {
                    QStringList s;
                    Q_FOREACH( const OrderVariable& orderVar, orderVars ) {
                        if( orderVar.sortOrder == Qt::DescendingOrder )
                            s += QLatin1String("DESC ");
                        else
                            s += QLatin1String("ASC ");
                        s += QString::fromLatin1("( %1 )").arg(orderVar.name);
                    }
                    return QLatin1String(" ORDER BY ") + s.join( QLatin1String(" ") );
                }
                else {
                    return QString();
                }
            }

            /// create and remember a scoring variable for full text matching
            inline QString createScoringVariable() {
                QString v = uniqueVarName();
                m_scoreVariables.insert(v, m_depth);
                return v;
            }

            inline QString buildScoringExpression() const {
                QStringList scores;
                for( QHash<QString, int>::const_iterator it = m_scoreVariables.constBegin();
                     it != m_scoreVariables.constEnd(); ++it ) {
                    const QString var = it.key();
                    int depth = it.value();
                    if( depth > 0 )
                        scores += QString::fromLatin1("(%1/%2)").arg(var).arg(depth+1);
                    else
                        scores += var;
                }
                if( !scores.isEmpty() )
                    return QLatin1String("max(") + scores.join(QLatin1String("+")) + QLatin1String(") as ?_n_f_t_m_s_");
                else
                    return QString();
            }

            inline QString buildSearchExcerptExpression() const {
                if( !m_fullTextSearchTerms.isEmpty() ) {
                    QStringList excerptParts;
                    for( QHash<QString, QStringList>::const_iterator it = m_fullTextSearchTerms.constBegin();
                         it != m_fullTextSearchTerms.constEnd(); ++it ) {
                        const QString& varName = it.key();
                        const QStringList terms = stripExtendedCharsHack(it.value());
                        if(terms.count()) {
                            // bif:search_excerpt wants a vector of all search terms
                            excerptParts
                                    << QString::fromLatin1("bif:search_excerpt(bif:vector('%1'), %2)")
                                       .arg( terms.join(QLatin1String("','")),
                                             varName );
                        }
                    }

                    if(excerptParts.count()) {
                        return QString::fromLatin1("(bif:concat(%1)) as ?_n_f_t_m_ex_")
                                .arg(excerptParts.join(QLatin1String(",")));
                    }
                    else {
                        return QString();
                    }
                }
                else {
                    return QString();
                }
            }
        };
    }
}

#endif