~ubuntu-branches/ubuntu/oneiric/strigi/oneiric

« back to all changes in this revision

Viewing changes to libstreamanalyzer/plugins/indexers/cluceneindexer/cluceneindexreader.cpp

  • Committer: Package Import Robot
  • Author(s): Fathi Boudra
  • Date: 2011-09-20 08:50:25 UTC
  • mto: (1.1.20 upstream) (5.1.6 sid)
  • mto: This revision was merged to the branch mainline in revision 44.
  • Revision ID: package-import@ubuntu.com-20110920085025-wszfu6x8rshrjq0e
ImportĀ upstreamĀ versionĀ 0.7.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of Strigi Desktop Search
 
2
 *
 
3
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU Library General Public
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Library General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Library General Public License
 
16
 * along with this library; see the file COPYING.LIB.  If not, write to
 
17
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
18
 * Boston, MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#include "cluceneindexreader.h"
 
22
#include <strigi/strigiconfig.h>
 
23
#include <strigi/query.h>
 
24
#include <strigi/queryparser.h>
 
25
#include <strigi/variant.h>
 
26
#include <strigi/textutils.h>
 
27
#include "cluceneindexmanager.h"
 
28
#include "timeofday.h"
 
29
#include "tcharutils.h"
 
30
#include <CLucene/search/QueryFilter.h>
 
31
#include <CLucene/index/Terms.h>
 
32
#include <CLucene/store/RAMDirectory.h>
 
33
#include <strigi/fieldtypes.h>
 
34
#include <sstream>
 
35
#include <iostream>
 
36
#include <cassert>
 
37
#include <climits>
 
38
 
 
39
using lucene::search::Hits;
 
40
using lucene::search::IndexSearcher;
 
41
using lucene::document::Document;
 
42
using lucene::document::Field;
 
43
using lucene::index::Term;
 
44
using lucene::index::TermDocs;
 
45
using lucene::index::TermEnum;
 
46
using lucene::search::TermQuery;
 
47
using lucene::search::WildcardQuery;
 
48
using lucene::search::BooleanQuery;
 
49
using lucene::search::Query;
 
50
using lucene::search::RangeQuery;
 
51
using lucene::search::QueryFilter;
 
52
using lucene::search::HitCollector;
 
53
using lucene::util::BitSet;
 
54
using lucene::document::DocumentFieldEnumeration;
 
55
using Strigi::IndexedDocument;
 
56
using Strigi::Variant;
 
57
using Strigi::FieldRegister;
 
58
 
 
59
using namespace std;
 
60
 
 
61
class HitCounter : public HitCollector {
 
62
private:
 
63
    int32_t m_count;
 
64
    void collect (const int32_t doc, const float_t score) { m_count++; }
 
65
public:
 
66
    HitCounter() :m_count(0) {}
 
67
    int32_t count() const { return m_count; }
 
68
};
 
69
 
 
70
class CLuceneIndexReader::Private {
 
71
public:
 
72
    static const wchar_t* systemlocation();
 
73
    static const wchar_t* mtime();
 
74
    static const wchar_t* mimetype();
 
75
    static const wchar_t* size();
 
76
    static const wchar_t* content();
 
77
    static const wchar_t* parentlocation();
 
78
    CLuceneIndexReader& reader;
 
79
    Private(CLuceneIndexReader& r) :reader(r) {}
 
80
 
 
81
    static Term* createTerm(const wchar_t* name, const string& value);
 
82
    static Term* createKeywordTerm(const wchar_t* name, const string& value);
 
83
    static Term* createWildCardTerm(const wchar_t* name, const string& value);
 
84
    Query* createQuery(const Strigi::Query& query);
 
85
    Query* createSimpleQuery(const Strigi::Query& query);
 
86
    static Query* createSingleFieldQuery(const string& field,
 
87
        const Strigi::Query& query);
 
88
    Query* createNoFieldQuery(const Strigi::Query& query);
 
89
    Query* createMultiFieldQuery(const Strigi::Query& query);
 
90
    BooleanQuery* createBooleanQuery(const Strigi::Query& query);
 
91
    static void addField(lucene::document::Field* field, IndexedDocument&);
 
92
    Variant getFieldValue(lucene::document::Field* field, Variant::Type) const;
 
93
 
 
94
    vector<IndexedDocument> strigiSpecial(const string& command);
 
95
};
 
96
const wchar_t*
 
97
CLuceneIndexReader::Private::systemlocation() {
 
98
    const static wstring s(utf8toucs2(FieldRegister::pathFieldName));
 
99
    return s.c_str();
 
100
}
 
101
const wchar_t*
 
102
CLuceneIndexReader::Private::mtime() {
 
103
    const static wstring s(utf8toucs2(FieldRegister::mtimeFieldName));
 
104
    return s.c_str();
 
105
}
 
106
const wchar_t*
 
107
CLuceneIndexReader::Private::mimetype() {
 
108
    const static wstring s(utf8toucs2(FieldRegister::mimetypeFieldName));
 
109
    return s.c_str();
 
110
}
 
111
const wchar_t*
 
112
CLuceneIndexReader::Private::size() {
 
113
    const static wstring s(utf8toucs2(FieldRegister::sizeFieldName));
 
114
    return s.c_str();
 
115
}
 
116
const wchar_t*
 
117
CLuceneIndexReader::Private::parentlocation() {
 
118
    const static wstring s(utf8toucs2(FieldRegister::parentLocationFieldName));
 
119
    return s.c_str();
 
120
}
 
121
const wchar_t*
 
122
CLuceneIndexReader::Private::content() {
 
123
    const static wstring s(utf8toucs2(FieldRegister::contentFieldName));
 
124
    return s.c_str();
 
125
}
 
126
 
 
127
CLuceneIndexReader::CLuceneIndexReader(CLuceneIndexManager* m,
 
128
    const string& dir) :manager(m), p(new Private(*this)), dbdir(dir),
 
129
        reader(0) {
 
130
    otime.tv_sec = 0;
 
131
    otime.tv_usec = 0;
 
132
    openReader();
 
133
}
 
134
 
 
135
CLuceneIndexReader::~CLuceneIndexReader() {
 
136
    closeReader();
 
137
    delete p;
 
138
}
 
139
void
 
140
CLuceneIndexReader::openReader() {
 
141
    closeReader();
 
142
    doccount = -1;
 
143
    wordcount = -1;
 
144
    try {
 
145
        reader = lucene::index::IndexReader::open(manager->directory);
 
146
        // fprintf(stderr,
 
147
        // "READER at %s: %i\n", dbdir.c_str(), reader->numDocs());
 
148
    } catch (CLuceneError& err) {
 
149
        fprintf(stderr, "could not create reader %s: %s\n", dbdir.c_str(),
 
150
            err.what());
 
151
        reader = 0;
 
152
    }
 
153
}
 
154
void
 
155
CLuceneIndexReader::closeReader() {
 
156
    if (reader == 0) return;
 
157
    try {
 
158
        reader->close();
 
159
    } catch (CLuceneError& err) {
 
160
        fprintf(stderr, "could not close clucene: %s\n", err.what());
 
161
    }
 
162
    delete reader;
 
163
    reader = 0;
 
164
}
 
165
bool
 
166
CLuceneIndexReader::checkReader(bool enforceCurrent) {
 
167
    struct timeval mtime = manager->indexMTime();
 
168
    if (mtime.tv_sec != otime.tv_sec || mtime.tv_usec != otime.tv_usec) {
 
169
        if (enforceCurrent) {
 
170
            otime = mtime;
 
171
            closeReader();
 
172
        } else {
 
173
            struct timeval now;
 
174
            gettimeofday(&now, 0);
 
175
            if (now.tv_sec - otime.tv_sec > 60) {
 
176
                otime = mtime;
 
177
                closeReader();
 
178
            }
 
179
        }
 
180
    }
 
181
    if (reader == 0) {
 
182
        openReader();
 
183
    }
 
184
    return reader != NULL;
 
185
}
 
186
 
 
187
#ifdef _UCS2
 
188
typedef map<wstring, wstring> CLuceneIndexReaderFieldMapType;
 
189
#else
 
190
typedef map<string, string> CLuceneIndexReaderFieldMapType;
 
191
#endif
 
192
CLuceneIndexReaderFieldMapType CLuceneIndexReaderFieldMap;
 
193
 
 
194
void CLuceneIndexReader::addMapping(const TCHAR* from, const TCHAR* to){
 
195
    CLuceneIndexReaderFieldMap[from] = to;
 
196
}
 
197
const TCHAR*
 
198
CLuceneIndexReader::mapId(const TCHAR* id) {
 
199
    if (CLuceneIndexReaderFieldMap.size() == 0) {
 
200
        string contentID(FieldRegister::contentFieldName.c_str());
 
201
        wstring cID(utf8toucs2(contentID));
 
202
        addMapping(_T(""), cID.c_str());
 
203
    }
 
204
    if (id == 0) {
 
205
        id = _T("");
 
206
    }
 
207
    CLuceneIndexReaderFieldMapType::iterator itr
 
208
        = CLuceneIndexReaderFieldMap.find(id);
 
209
    if (itr == CLuceneIndexReaderFieldMap.end()) {
 
210
        return id;
 
211
    } else {
 
212
        return itr->second.c_str();
 
213
    }
 
214
}
 
215
#ifdef _UCS2
 
216
wstring
 
217
CLuceneIndexReader::mapId(const char* id) {
 
218
    wstring tid = utf8toucs2(id);
 
219
    return mapId(tid.c_str());
 
220
}
 
221
#endif
 
222
 
 
223
Term*
 
224
CLuceneIndexReader::Private::createWildCardTerm(const wchar_t* name,
 
225
        const string& value) {
 
226
    wstring v = utf8toucs2(value);
 
227
    return _CLNEW Term(name, v.c_str());
 
228
}
 
229
Term*
 
230
CLuceneIndexReader::Private::createTerm(const wchar_t* name,
 
231
        const string& value) {
 
232
    wstring v = utf8toucs2(value);
 
233
    lucene::util::StringReader sr(v.c_str());
 
234
    lucene::analysis::standard::StandardAnalyzer a;
 
235
    lucene::analysis::TokenStream* ts = a.tokenStream(name, &sr);
 
236
    lucene::analysis::Token* to = ts->next();
 
237
    const wchar_t *tv;
 
238
    if (to) {
 
239
        tv = to->termText();
 
240
    } else {
 
241
        tv = v.c_str();
 
242
    }
 
243
    Term* t = _CLNEW Term(name, tv);
 
244
    if (to) {
 
245
        _CLDELETE(to);
 
246
    }
 
247
    _CLDELETE(ts);
 
248
    return t;
 
249
}
 
250
Term*
 
251
CLuceneIndexReader::Private::createKeywordTerm(const wchar_t* name,
 
252
        const string& value) {
 
253
    wstring v = utf8toucs2(value);
 
254
    Term* t = _CLNEW Term(name, v.c_str());
 
255
    return t;
 
256
}
 
257
BooleanQuery*
 
258
CLuceneIndexReader::Private::createBooleanQuery(const Strigi::Query& query) {
 
259
    BooleanQuery* bq = _CLNEW BooleanQuery();
 
260
    bool isAnd = query.type() == Strigi::Query::And;
 
261
    const vector<Strigi::Query>& sub = query.subQueries();
 
262
    for (vector<Strigi::Query>::const_iterator i = sub.begin(); i != sub.end();
 
263
            ++i) {
 
264
        Query* q = createQuery(*i);
 
265
        bq->add(q, true, isAnd, i->negate());
 
266
    }
 
267
    return bq;
 
268
}
 
269
Query*
 
270
CLuceneIndexReader::Private::createQuery(const Strigi::Query& query) {
 
271
    return query.subQueries().size()
 
272
        ? createBooleanQuery(query)
 
273
        : createSimpleQuery(query);
 
274
}
 
275
Query*
 
276
CLuceneIndexReader::Private::createSimpleQuery(const Strigi::Query& query) {
 
277
    switch (query.fields().size()) {
 
278
    case 0:  return createSingleFieldQuery(FieldRegister::contentFieldName,
 
279
        query);//return createNoFieldQuery(query);
 
280
    case 1:  return createSingleFieldQuery(query.fields()[0], query);
 
281
    default: return createMultiFieldQuery(query);
 
282
    }
 
283
}
 
284
Query*
 
285
CLuceneIndexReader::Private::createSingleFieldQuery(const string& field,
 
286
        const Strigi::Query& query) {
 
287
    wstring fieldname = mapId(field.c_str());
 
288
    Query* q;
 
289
    Term* t;
 
290
    const string& val = query.term().string();
 
291
    switch (query.type()) {
 
292
    case Strigi::Query::LessThan:
 
293
          t = createTerm(fieldname.c_str(), val.c_str());
 
294
          q = _CLNEW RangeQuery(0, t, false);
 
295
          break;
 
296
    case Strigi::Query::LessThanEquals:
 
297
          t = createTerm(fieldname.c_str(), query.term().string());
 
298
          q = _CLNEW RangeQuery(0, t, true);
 
299
          break;
 
300
    case Strigi::Query::GreaterThan:
 
301
          t = createTerm(fieldname.c_str(), query.term().string());
 
302
          q = _CLNEW RangeQuery(t, 0, false);
 
303
          break;
 
304
    case Strigi::Query::GreaterThanEquals:
 
305
          t = createTerm(fieldname.c_str(), query.term().string());
 
306
          q = _CLNEW RangeQuery(t, 0, true);
 
307
          break;
 
308
    case Strigi::Query::Keyword:
 
309
          t = createKeywordTerm(fieldname.c_str(), query.term().string());
 
310
          q = _CLNEW TermQuery(t);
 
311
          break;
 
312
    case Strigi::Query::Contains:
 
313
          t = createWildCardTerm(fieldname.c_str(), "*" + val + "*");
 
314
          q = _CLNEW WildcardQuery(t);
 
315
          break;
 
316
    case Strigi::Query::StartsWith:
 
317
          t = createWildCardTerm(fieldname.c_str(), val + "*");
 
318
          q = _CLNEW WildcardQuery(t);
 
319
          break;
 
320
    case Strigi::Query::Equals:
 
321
    default:
 
322
          if (strpbrk(val.c_str(), "*?")) {
 
323
               t = createWildCardTerm(fieldname.c_str(), val);
 
324
               q = _CLNEW WildcardQuery(t);
 
325
          } else {
 
326
               t = createTerm(fieldname.c_str(), val);
 
327
               q = _CLNEW TermQuery(t);
 
328
          }
 
329
    }
 
330
    _CLDECDELETE(t);
 
331
    return q;
 
332
}
 
333
Query*
 
334
CLuceneIndexReader::Private::createMultiFieldQuery(const Strigi::Query& query) {
 
335
    BooleanQuery* bq = _CLNEW BooleanQuery();
 
336
    for (vector<string>::const_iterator i = query.fields().begin();
 
337
            i != query.fields().end(); ++i) {
 
338
        Query* q = createSingleFieldQuery(*i, query);
 
339
        bq->add(q, true, false, false);
 
340
    }
 
341
    return bq;
 
342
}
 
343
Query*
 
344
CLuceneIndexReader::Private::createNoFieldQuery(const Strigi::Query& query) {
 
345
    vector<string> fields = reader.fieldNames();
 
346
    BooleanQuery* bq = _CLNEW BooleanQuery();
 
347
    for (vector<string>::const_iterator i = fields.begin(); i != fields.end();
 
348
            ++i) {
 
349
        Query* q = createSingleFieldQuery(*i, query);
 
350
        bq->add(q, true, false, false);
 
351
    }
 
352
    return bq;
 
353
}
 
354
void
 
355
CLuceneIndexReader::Private::addField(lucene::document::Field* field,
 
356
        IndexedDocument& doc) {
 
357
    if (field->stringValue() == 0) return;
 
358
    string value(wchartoutf8(field->stringValue()));
 
359
    const TCHAR* name = field->name();
 
360
    if (wcscmp(name, content()) == 0) {
 
361
        doc.fragment = value;
 
362
    } else if (wcscmp(name, systemlocation()) == 0) {
 
363
        doc.uri = value;
 
364
    } else if (wcscmp(name, mimetype()) == 0) {
 
365
        doc.mimetype = value;
 
366
    } else if (wcscmp(name, mtime()) == 0) {
 
367
        doc.mtime=atol(value.c_str());
 
368
    } else if (wcscmp(name, size()) == 0) {
 
369
        string size = value;
 
370
        doc.size = atoi(size.c_str());
 
371
    } else {
 
372
        doc.properties.insert(make_pair<const string, string>(
 
373
            wchartoutf8(name), value));
 
374
    }
 
375
}
 
376
Variant
 
377
CLuceneIndexReader::Private::getFieldValue(lucene::document::Field* field,
 
378
        Variant::Type type) const {
 
379
    if (field->stringValue() == 0) return Variant();
 
380
    Variant v(wchartoutf8(field->stringValue()));
 
381
    if (type == Variant::b_val) {
 
382
         v = v.b();
 
383
    } else if (type == Variant::i_val) {
 
384
         v = v.i();
 
385
    } else if (type == Variant::as_val) {
 
386
         v = v.as();
 
387
    }
 
388
    return v;
 
389
}
 
390
int32_t
 
391
CLuceneIndexReader::countHits(const Strigi::Query& q) {
 
392
    if (!checkReader()) return -1;
 
393
    // if the query is empty, we return the number of files in the index
 
394
    if (q.term().string().size() == 0 && q.subQueries().size() == 0) {
 
395
        return countDocuments();
 
396
    }
 
397
 
 
398
    Query* bq = p->createQuery(q);
 
399
    if (reader == 0) {
 
400
        return 0;
 
401
    }
 
402
    IndexSearcher searcher(reader);
 
403
    vector<IndexedDocument> results;
 
404
    Hits* hits = 0;
 
405
    int s = 0;
 
406
    try {
 
407
        hits = searcher.search(bq);
 
408
        s = hits->length();
 
409
    } catch (CLuceneError& err) {
 
410
/*        HitCounter counter;
 
411
        QueryFilter* filter = _CLNEW QueryFilter(&bq);
 
412
        try {
 
413
        BitSet* bits = filter->bits(reader);
 
414
        int32_t n = bits->size();
 
415
        for (int32_t i=0; i<n; ++i) {
 
416
            if (bits->get(i)) s++;
 
417
        }
 
418
        } catch (CLuceneError& err2) {
 
419
            printf("ccould not query: %s\n", err.what());
 
420
        }
 
421
        try {
 
422
            searcher._search(0, filter, &counter);
 
423
        } catch (CLuceneError& err2) {
 
424
            printf("ccould not query: %s\n", err.what());
 
425
        }
 
426
        s = counter.count();
 
427
 
 
428
        printf("counted %i hits\n", count);
 
429
        // try to do a constant score query
 
430
        //QueryFilter* filter = _CLNEW QueryFilter(&bq);
 
431
        ConstantScoreQuery csq(filter);*/
 
432
        fprintf(stderr, "could not query: %s\n", err.what());
 
433
    }
 
434
    delete hits;
 
435
    searcher.close();
 
436
    _CLDELETE(bq);
 
437
    return s;
 
438
}
 
439
vector<IndexedDocument>
 
440
CLuceneIndexReader::query(const Strigi::Query& q, int off, int max) {
 
441
    vector<IndexedDocument> results;
 
442
    if (!checkReader()) {
 
443
        return results;
 
444
    }
 
445
    // handle special commands
 
446
    if (q.fields().size() == 1 && q.fields()[0].empty()
 
447
            && q.term().string().substr(0, 14) == "strigispecial:") {
 
448
        return p->strigiSpecial(q.term().string());
 
449
    }
 
450
 
 
451
    Query* bq = p->createQuery(q);
 
452
    IndexSearcher searcher(reader);
 
453
    Hits* hits = 0;
 
454
    int s = 0;
 
455
    try {
 
456
        hits = searcher.search(bq);
 
457
        s = hits->length();
 
458
    } catch (CLuceneError& err) {
 
459
        fprintf(stderr, "could not query: %s\n", err.what());
 
460
    }
 
461
    if (off < 0) off = 0;
 
462
    max += off;
 
463
    if (max < 0) max = s;
 
464
    if (max > s) max = s;
 
465
    if (max > off) {
 
466
        results.reserve(max-off);
 
467
    }
 
468
    for (int i = off; i < max; ++i) {
 
469
        Document *d = &hits->doc(i);
 
470
        IndexedDocument doc;
 
471
        doc.score = hits->score(i);
 
472
        DocumentFieldEnumeration* e = d->fields();
 
473
        while (e->hasMoreElements()) {
 
474
            Field* f = e->nextElement();
 
475
            Private::addField(f, doc);
 
476
        }
 
477
        results.push_back(doc);
 
478
        _CLDELETE(e);
 
479
    }
 
480
    if (hits) {
 
481
        _CLDELETE(hits);
 
482
    }
 
483
    searcher.close();
 
484
    _CLDELETE(bq);
 
485
    return results;
 
486
}
 
487
void
 
488
CLuceneIndexReader::getDocuments(const std::vector<std::string>& fullFields,
 
489
        const std::vector<Strigi::Variant::Type>& types,
 
490
        std::vector<std::vector<Strigi::Variant> >& result, int off, int max) {
 
491
    int pos = 0;
 
492
    int maxDoc = reader->maxDoc();
 
493
    for (int i=0; i<off; i++) {
 
494
        while (pos < maxDoc && reader->isDeleted(pos)) pos++;
 
495
        if (pos == maxDoc) return;
 
496
        pos++;
 
497
    }
 
498
    if (max < 0) max = 0;
 
499
    result.resize(max);
 
500
    Document* d = new Document();
 
501
    for (int i = 0; i < max && pos < maxDoc; ++i) {
 
502
        while (pos < maxDoc && reader->isDeleted(pos)) pos++;
 
503
        d->clear();
 
504
        if (pos == maxDoc || !reader->document(pos++, d)) {
 
505
            continue;
 
506
        }
 
507
        
 
508
        vector<Variant>& doc = result[i];
 
509
        doc.clear();
 
510
        doc.resize(fullFields.size());
 
511
 
 
512
        DocumentFieldEnumeration* e = d->fields();
 
513
        while (e->hasMoreElements()) {
 
514
            Field* field = e->nextElement();
 
515
            string name(wchartoutf8(field->name()));
 
516
            for (uint j = 0; j < fullFields.size(); ++j) {
 
517
                if (fullFields[j] == name) {
 
518
                    doc[j] = p->getFieldValue(field, types[j]);
 
519
                }
 
520
            }
 
521
        }
 
522
        _CLDELETE(e);
 
523
    }
 
524
    delete d;
 
525
}
 
526
void
 
527
CLuceneIndexReader::getHits(const Strigi::Query& q,
 
528
        const std::vector<std::string>& fields,
 
529
        const std::vector<Strigi::Variant::Type>& types,
 
530
        std::vector<std::vector<Strigi::Variant> >& result, int off, int max) {
 
531
    result.clear();
 
532
    if (!checkReader() || types.size() < fields.size()) {
 
533
        return;
 
534
    }
 
535
 
 
536
    vector<string> fullFields;
 
537
    fullFields.resize(fields.size());
 
538
    for (size_t i = 0; i < fields.size(); i++) {
 
539
        if (fields[i].compare(0, 6, "xesam:") == 0) {
 
540
            fullFields[i].assign(
 
541
                "http://freedesktop.org/standards/xesam/1.0/core#"
 
542
                + fields[i].substr(6));
 
543
        } else if (fields[i].compare(0, 4, "nie:") == 0) {
 
544
            fullFields[i].assign(
 
545
                "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#"
 
546
                + fields[i].substr(4));
 
547
        } else {
 
548
            fullFields[i].assign(fields[i]);
 
549
        }
 
550
    }
 
551
 
 
552
    // if the query is empty, we return the number of files in the index
 
553
    if (q.term().string().size() == 0 && q.subQueries().size() == 0) {
 
554
        getDocuments(fullFields, types, result, off, max);
 
555
        return;
 
556
    }
 
557
 
 
558
    Query* bq = p->createQuery(q);
 
559
    IndexSearcher searcher(reader);
 
560
    Hits* hits = 0;
 
561
    int s = 0;
 
562
    try {
 
563
        hits = searcher.search(bq);
 
564
        s = hits->length();
 
565
    } catch (CLuceneError& err) {
 
566
        fprintf(stderr, "could not query: %s\n", err.what());
 
567
    }
 
568
    if (off < 0) off = 0;
 
569
    max += off;
 
570
    if (max < 0) max = s;
 
571
    if (max > s) max = s;
 
572
    if (max > off) {
 
573
        result.reserve(max-off);
 
574
    }
 
575
    result.resize(max-off);
 
576
    for (int i = off; i < max; ++i) {
 
577
        Document *d = &hits->doc(i);
 
578
        vector<Variant>& doc = result[i-off];
 
579
        doc.clear();
 
580
        doc.resize(fields.size());
 
581
 
 
582
        DocumentFieldEnumeration* e = d->fields();
 
583
        while (e->hasMoreElements()) {
 
584
            Field* field = e->nextElement();
 
585
            string name(wchartoutf8(field->name()));
 
586
            for (uint j = 0; j < fullFields.size(); ++j) {
 
587
                if (fullFields[j] == name) {
 
588
                    doc[j] = p->getFieldValue(field, types[j]);
 
589
                }
 
590
            }
 
591
        }
 
592
        _CLDELETE(e);
 
593
    }
 
594
    if (hits) {
 
595
        _CLDELETE(hits);
 
596
    }
 
597
    searcher.close();
 
598
    _CLDELETE(bq);
 
599
}
 
600
int32_t
 
601
CLuceneIndexReader::countDocuments() {
 
602
    if (!checkReader(true)) return -1;
 
603
    if (doccount == -1) {
 
604
        doccount = reader->numDocs();
 
605
    }
 
606
    return doccount;
 
607
}
 
608
int32_t
 
609
CLuceneIndexReader::countWords() {
 
610
    if (!checkReader()) return -1;
 
611
    if (wordcount == -1) {
 
612
        if (reader) {
 
613
            wordcount = 0;
 
614
            lucene::index::TermEnum *terms = reader->terms();
 
615
            while (terms->next()) wordcount++;
 
616
            _CLDELETE(terms);
 
617
        }
 
618
    }
 
619
    return wordcount;
 
620
}
 
621
int64_t
 
622
CLuceneIndexReader::indexSize() {
 
623
    return manager->indexSize();
 
624
}
 
625
int64_t
 
626
CLuceneIndexReader::documentId(const string& uri) {
 
627
    if (!checkReader()) return -1;
 
628
    int64_t id = -1;
 
629
 
 
630
    Term term(mapId(Private::systemlocation()), utf8toucs2( uri ).c_str());
 
631
    TermDocs* docs = reader->termDocs(&term);
 
632
    if (docs->next()) {
 
633
        id = docs->doc();
 
634
    }
 
635
    _CLDELETE(docs);
 
636
 
 
637
    if (id != -1 && reader->isDeleted((int32_t)id)) {
 
638
        id = -1;
 
639
    }
 
640
 
 
641
    return id;
 
642
}
 
643
/**
 
644
 * Retrieve the mtime of the document with id @docid. If this document
 
645
 * is not in the index, the time 0 is returned.
 
646
 **/
 
647
time_t
 
648
CLuceneIndexReader::mTime(int64_t docid) {
 
649
    if (docid < 0) return 0;
 
650
    if (!checkReader(true)) return 0;
 
651
    time_t mtime = 0;
 
652
    Document *d = reader->document((int32_t)docid);
 
653
    if (d) {
 
654
        const TCHAR* v = d->get(Private::mtime());
 
655
        mtime = atoi(wchartoutf8( v ).c_str());
 
656
        delete d;
 
657
    }
 
658
    return mtime;
 
659
}
 
660
/**
 
661
 * Retrieve the mtime of the document with id @docid. If this document
 
662
 * is not in the index, the time 0 is returned.
 
663
 **/
 
664
time_t
 
665
CLuceneIndexReader::mTime(const std::string& uri) {
 
666
    return mTime(documentId(uri));
 
667
}
 
668
class Histogram {
 
669
public:
 
670
    vector<pair<string,uint32_t> > h;
 
671
    vector<int> values;
 
672
};
 
673
#include <time.h>
 
674
vector<pair<string,uint32_t> >
 
675
makeTimeHistogram(const vector<int>& v) {
 
676
    map<int32_t, int32_t> m;
 
677
    vector<int32_t>::const_iterator i;
 
678
    struct tm t;
 
679
    for (i = v.begin(); i < v.end(); ++i) {
 
680
        time_t ti = *i;
 
681
#ifdef _WIN32
 
682
        t = *localtime( &ti );   // is thread-safe on win32
 
683
#else
 
684
        localtime_r(&ti, &t);
 
685
#endif
 
686
        int32_t c = 10000*t.tm_year + 100*t.tm_mon + t.tm_mday;
 
687
        m[c]++;
 
688
    }
 
689
    vector<pair<string,uint32_t> > h;
 
690
    h.reserve(m.size());
 
691
    ostringstream str;
 
692
    map<int32_t,int32_t>::const_iterator j;
 
693
    for (j = m.begin(); j != m.end(); ++j) {
 
694
        str << j->first + 19000100;
 
695
        h.push_back(make_pair<string,uint32_t>(str.str(), j->second));
 
696
        str.str("");
 
697
    }
 
698
    return h;
 
699
}
 
700
vector<pair<string,uint32_t> >
 
701
makeHistogram(const vector<int>& v, int min, int max) {
 
702
    map<int32_t, int32_t> m;
 
703
    vector<int32_t>::const_iterator i;
 
704
    for (i = v.begin(); i < v.end(); ++i) {
 
705
        m[*i]++;
 
706
    }
 
707
    vector<pair<string,uint32_t> > h;
 
708
    h.reserve(m.size());
 
709
    ostringstream str;
 
710
    map<int32_t,int32_t>::const_iterator j;
 
711
    for (j = m.begin(); j != m.end(); ++j) {
 
712
        str << j->first;
 
713
        h.push_back(make_pair<string,uint32_t>(str.str(), j->second));
 
714
        str.str("");
 
715
    }
 
716
    return h;
 
717
}
 
718
vector<pair<string,uint32_t> >
 
719
CLuceneIndexReader::histogram(const string& query,
 
720
        const string& fieldname, const string& labeltype) {
 
721
    vector<pair<string,uint32_t> > h;
 
722
    if (!checkReader()) {
 
723
        return h;
 
724
    }
 
725
    Strigi::QueryParser parser;
 
726
    Strigi::Query q = parser.buildQuery(query);
 
727
    Query* bq = p->createQuery(q);
 
728
    IndexSearcher searcher(reader);
 
729
    Hits* hits = 0;
 
730
    int s = 0;
 
731
    try {
 
732
        hits = searcher.search(bq);
 
733
        s = hits->length();
 
734
    } catch (CLuceneError& err) {
 
735
        fprintf(stderr, "could not query: %s\n", err.what());
 
736
    }
 
737
    wstring field = utf8toucs2(fieldname);
 
738
    int32_t max = INT_MIN;
 
739
    int32_t min = INT_MAX;
 
740
    vector<int32_t> values;
 
741
    values.reserve(s);
 
742
    char* end;
 
743
    for (int i = 0; i < s; ++i) {
 
744
        Document *d = &hits->doc(i);
 
745
        const TCHAR* v = d->get(field.c_str());
 
746
        if (v) {
 
747
            int val = (int)strtol(wchartoutf8( v ).c_str(), &end, 10);
 
748
            if ( *end != 0) {
 
749
                _CLDELETE(hits);
 
750
                return h;
 
751
            }
 
752
            values.push_back(val);
 
753
            max = (max>val) ?max :val;
 
754
            min = (min<val) ?min :val;
 
755
        }
 
756
    }
 
757
    if (hits) {
 
758
        _CLDELETE(hits);
 
759
    }
 
760
    searcher.close();
 
761
    _CLDELETE(bq);
 
762
    if (fieldname == FieldRegister::mtimeFieldName || labeltype == "time") {
 
763
        return makeTimeHistogram(values);
 
764
    } else {
 
765
        return makeHistogram(values, min, max);
 
766
    }
 
767
}
 
768
vector<string>
 
769
CLuceneIndexReader::fieldNames() {
 
770
    vector<string> s;
 
771
    if (!checkReader()) {
 
772
        return s;
 
773
    }
 
774
    TCHAR** names = reader->getFieldNames();
 
775
    if (names == 0) return s;
 
776
    TCHAR** n = names;
 
777
    while (*n) {
 
778
        string str(wchartoutf8(*n));
 
779
        s.push_back(str);
 
780
        _CLDELETE_ARRAY(*n);
 
781
        n++;
 
782
    }
 
783
    _CLDELETE_ARRAY(names);
 
784
    return s;
 
785
}
 
786
int32_t
 
787
CLuceneIndexReader::countKeywords(const string& keywordprefix,
 
788
        const vector<string>& fieldnames) {
 
789
    return 2;
 
790
}
 
791
vector<string>
 
792
CLuceneIndexReader::keywords(const string& keywordmatch,
 
793
        const vector<string>& fieldnames, uint32_t max, uint32_t offset) {
 
794
    vector<string> fn;
 
795
    if (fieldnames.size()) {
 
796
        fn = fieldnames;
 
797
    } else {
 
798
        fn = fieldNames();
 
799
    }
 
800
    set<wstring> s;
 
801
    wstring prefix = utf8toucs2(keywordmatch);
 
802
    const wchar_t* prefixtext = prefix.c_str();
 
803
    string::size_type prefixLen = prefix.length();
 
804
    vector<string>::const_iterator i;
 
805
    Term* lastTerm = 0;
 
806
    for (i = fn.begin(); i != fn.end() && s.size() << max; ++i) {
 
807
         wstring fieldname(utf8toucs2(*i));
 
808
         Term term(fieldname.c_str(), prefix.c_str());
 
809
         TermEnum* enumerator = reader->terms(&term);
 
810
         do {
 
811
             lastTerm = enumerator->term(false);
 
812
             if (lastTerm) {
 
813
                 if (prefixLen > lastTerm->textLength()
 
814
                         || wcsncmp(lastTerm->text(), prefixtext, prefixLen)
 
815
                             != 0) {
 
816
                     break;
 
817
                 }
 
818
                 s.insert(lastTerm->text());
 
819
             }
 
820
         } while (enumerator->next() && s.size() < max);
 
821
    }
 
822
 
 
823
    vector<string> k;
 
824
    k.reserve(s.size());
 
825
    set<wstring>::const_iterator j;
 
826
    for (j = s.begin(); j != s.end(); ++j) {
 
827
        k.push_back(wchartoutf8(*j));
 
828
    }
 
829
    return k;
 
830
}
 
831
void
 
832
CLuceneIndexReader::getChildren(const std::string& parent,
 
833
            std::map<std::string, time_t>& children) {
 
834
    children.clear();
 
835
    // force a fresh reader. This is important because the function
 
836
    // getChildren is essential for updating the index
 
837
    if ( !checkReader(true) ) {
 
838
        return;
 
839
    }
 
840
    // build a query
 
841
    Term* t = Private::createKeywordTerm(Private::parentlocation(),
 
842
        parent);
 
843
    Query* q = _CLNEW TermQuery(t);
 
844
    _CLDECDELETE(t);
 
845
    IndexSearcher searcher(reader);
 
846
    Hits* hits = 0;
 
847
    int nhits = 0;
 
848
    try {
 
849
        hits = searcher.search(q);
 
850
        nhits = hits->length();
 
851
    } catch (CLuceneError& err) {
 
852
        fprintf(stderr, "could not query: %s\n", err.what());
 
853
    }
 
854
    const TCHAR* mtime = mapId(Private::mtime());
 
855
    for (int i = 0; i < nhits; ++i) {
 
856
        Document* d = &hits->doc(i);
 
857
 
 
858
        const TCHAR* v = d->get(mtime);
 
859
        // check that mtime is defined for this document
 
860
        if (v) {
 
861
            time_t mtime = atoi(wchartoutf8( v ).c_str());
 
862
            v = d->get(Private::systemlocation());
 
863
            if (v) {
 
864
                children[wchartoutf8( v )] = mtime;
 
865
            }
 
866
        }
 
867
 
 
868
    }
 
869
    if (hits) {
 
870
        _CLDELETE(hits);
 
871
    }
 
872
    searcher.close();
 
873
    _CLDELETE(q);
 
874
}
 
875
vector<IndexedDocument>
 
876
CLuceneIndexReader::Private::strigiSpecial(const string& command) {
 
877
    vector<IndexedDocument> r;
 
878
cerr << "strigispecial " << command << endl;
 
879
    // we are going to count the size of each of the fields in this index
 
880
    // this requires that we loop through all fields
 
881
    lucene::index::TermEnum* terms = reader.reader->terms();
 
882
 
 
883
    map<const TCHAR *, int64_t> lengths;
 
884
    while (terms->next()) {
 
885
        lengths[terms->term()->field()] += terms->term()->textLength();
 
886
       // cerr << wchartoutf8(terms->term()->field()) << '\t'
 
887
        //    << wchartoutf8(terms->term()->text()) << endl;
 
888
    }
 
889
    int64_t total = 0;
 
890
    for (map<const TCHAR *, int64_t>::const_iterator i = lengths.begin();
 
891
            i != lengths.end(); ++i) {
 
892
        cerr << wchartoutf8(i->first) << '\t' << i->second << endl;
 
893
        total += i->second;
 
894
    }
 
895
    terms->close();
 
896
    cerr << "total" << '\t' << total << endl;
 
897
 
 
898
    int32_t max = reader.reader->numDocs();
 
899
    for (int32_t i=0; i < max; ++i) {
 
900
        lucene::document::Document* d = reader.reader->document(i);
 
901
        lucene::document::DocumentFieldEnumeration* e = d->fields();
 
902
        while (e->hasMoreElements()) {
 
903
            Field* f = e->nextElement();
 
904
            if (f->isStored()) {
 
905
                total += wcslen(f->stringValue());
 
906
            }
 
907
        }
 
908
        delete d;
 
909
    }
 
910
    cerr << "total" << '\t' << total << endl;
 
911
    return r;
 
912
}