~ubuntu-branches/ubuntu/quantal/recoll/quantal

« back to all changes in this revision

Viewing changes to rcldb/rcldb.cpp

  • Committer: Package Import Robot
  • Author(s): Kartik Mistry
  • Date: 2012-03-27 12:15:51 UTC
  • mfrom: (1.3.8)
  • Revision ID: package-import@ubuntu.com-20120327121551-nmntidzpehudushy
Tags: 1.17.1-1
* New upstream release.
* Enable Python module resulting into new binary: python-recoll.
* debian/control:
  + Updated Build-Deps: libqtwebkit-dev, python-all-dev.
  + Added python-recoll binary.
  + Updated Standards-Version to 3.9.3
* debian/rules:
  + Build Python module by default.
* debian/recoll.menu, debian/python-recoll.install, debian/recoll.install:
  + Changes for new binary package.
* debian/copyright:
  + Updated to copyright-format 1.0
  + Updated upstream and Debian copyright.
  + Fixed unicode.org/copyright.html URL.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include "xapian.h"
35
35
 
36
36
#include "rclconfig.h"
 
37
#include "debuglog.h"
37
38
#include "rcldb.h"
38
39
#include "rcldb_p.h"
39
40
#include "stemdb.h"
41
42
#include "transcode.h"
42
43
#include "unacpp.h"
43
44
#include "conftree.h"
44
 
#include "debuglog.h"
45
45
#include "pathut.h"
46
46
#include "smallut.h"
47
47
#include "utf8iter.h"
52
52
#include "rclversion.h"
53
53
#include "cancelcheck.h"
54
54
#include "ptmutex.h"
 
55
#include "termproc.h"
55
56
 
56
57
#ifndef MAX
57
58
#define MAX(A,B) (A>B?A:B)
94
95
// Only ONE field name inside the index data record differs from the
95
96
// Rcl::Doc ones: caption<->title, for a remnant of compatibility with
96
97
// omega
97
 
static const string cstr_keycap("caption");
98
98
 
99
99
// Static/Default table for field->prefix/weight translation. 
100
100
// This is logically const after initialization. Can't use a
125
125
    fldToTraits["ext"] = FieldTraits("XE");
126
126
    fldToTraits[Doc::keyfn] = FieldTraits("XSFN");
127
127
 
128
 
    fldToTraits[cstr_keycap] = FieldTraits("S");
 
128
    fldToTraits[cstr_caption] = FieldTraits("S");
129
129
    fldToTraits[Doc::keytt] = FieldTraits("S");
130
130
    fldToTraits["subject"] = FieldTraits("S");
131
131
 
141
141
    fldToTraits["xapyear"] = FieldTraits("Y");
142
142
    fldToTraits["xapyearmon"] = FieldTraits("M");
143
143
    fldToTraits["xapdate"] = FieldTraits("D");
 
144
    fldToTraits[Doc::keytp] = FieldTraits("T");
144
145
}
145
146
 
146
147
// Compute the unique term used to link documents to their origin. 
198
199
    parms.get(Doc::keyfmt, doc.fmtime);
199
200
    parms.get(Doc::keydmt, doc.dmtime);
200
201
    parms.get(Doc::keyoc, doc.origcharset);
201
 
    parms.get(cstr_keycap, doc.meta[Doc::keytt]);
 
202
    parms.get(cstr_caption, doc.meta[Doc::keytt]);
202
203
    parms.get(Doc::keykw, doc.meta[Doc::keykw]);
203
204
    parms.get(Doc::keyabs, doc.meta[Doc::keyabs]);
204
205
    // Possibly remove synthetic abstract indicator (if it's there, we
209
210
        doc.syntabs = true;
210
211
    }
211
212
    parms.get(Doc::keyipt, doc.ipath);
 
213
    parms.get(Doc::keypcs, doc.pcbytes);
212
214
    parms.get(Doc::keyfs, doc.fbytes);
213
215
    parms.get(Doc::keyds, doc.dbytes);
214
216
    parms.get(Doc::keysig, doc.sig);
570
572
        m_config->getConfParam("maxfsoccuppc", &m_maxFsOccupPc);
571
573
        m_config->getConfParam("idxflushmb", &m_flushMb);
572
574
    }
 
575
#ifdef IDX_THREADS
 
576
    if (m_ndb && !m_ndb->m_wqueue.start(DbUpdWorker, this)) {
 
577
        LOGERR(("Db::Db: Worker start failed\n"));
 
578
        return;
 
579
    }
 
580
#endif // IDX_THREADS
573
581
}
574
582
 
575
583
Db::~Db()
859
867
// The splitter breaks text into words and adds postings to the Xapian
860
868
// document. We use a single object to split all of the document
861
869
// fields and position jumps to separate fields
862
 
class TextSplitDb : public TextSplit {
 
870
class TextSplitDb : public TextSplitP {
863
871
 public:
864
 
    Xapian::WritableDatabase db;
865
872
    Xapian::Document &doc;   // Xapian document 
866
873
    // Base for document section. Gets large increment when we change
867
874
    // sections, to avoid cross-section proximity matches.
874
881
    // to compute the first position of the next section.
875
882
    Xapian::termpos curpos;
876
883
 
877
 
    StopList &stops;
878
 
    TextSplitDb(Xapian::WritableDatabase idb, 
879
 
                Xapian::Document &d, StopList &_stops) 
880
 
        : db(idb), doc(d), basepos(1), curpos(0), stops(_stops), wdfinc(1)
 
884
    TextSplitDb(Xapian::Document &d, TermProc *prc)
 
885
        : TextSplitP(prc), 
 
886
          doc(d), basepos(1), curpos(0), wdfinc(1)
881
887
    {}
882
888
    // Reimplement text_to_words to add start and end special terms
883
889
    virtual bool text_to_words(const string &in);
884
 
    bool takeword(const std::string &term, int pos, int, int);
885
890
    void setprefix(const string& pref) {prefix = pref;}
886
891
    void setwdfinc(int i) {wdfinc = i;}
887
892
 
 
893
    friend class TermProcIdx;
 
894
 
888
895
private:
889
896
    // If prefix is set, we also add a posting for the prefixed terms
890
897
    // (ie: for titles, add postings for both "term" and "Sterm")
893
900
    int wdfinc;
894
901
};
895
902
 
896
 
 
 
903
// Reimplement text_to_words to insert the begin and end anchor terms.
897
904
bool TextSplitDb::text_to_words(const string &in) 
898
905
{
899
 
    LOGDEB2(("TextSplitDb::text_to_words\n"));
 
906
    bool ret = false;
900
907
    string ermsg;
 
908
 
901
909
    try {
902
910
        // Index the possibly prefixed start term.
903
911
        doc.add_posting(prefix + start_of_field_term, basepos, wdfinc);
905
913
    } XCATCHERROR(ermsg);
906
914
    if (!ermsg.empty()) {
907
915
        LOGERR(("Db: xapian add_posting error %s\n", ermsg.c_str()));
908
 
        basepos += curpos + 100;
909
 
        return false;
 
916
        goto out;
910
917
    }
911
918
 
912
 
    if (!TextSplit::text_to_words(in)) {
 
919
    if (!TextSplitP::text_to_words(in)) {
913
920
        LOGDEB(("TextSplitDb: TextSplit::text_to_words failed\n"));
914
 
        basepos += curpos + 100;
915
 
        return false;
 
921
        goto out;
916
922
    }
917
923
 
918
924
    try {
922
928
    } XCATCHERROR(ermsg);
923
929
    if (!ermsg.empty()) {
924
930
        LOGERR(("Db: xapian add_posting error %s\n", ermsg.c_str()));
925
 
        basepos += curpos + 100;
926
 
        return false;
 
931
        goto out;
927
932
    }
 
933
 
 
934
    ret = true;
 
935
 
 
936
out:
928
937
    basepos += curpos + 100;
929
938
    return true;
930
939
}
931
940
 
932
 
// Get one term from the doc, remove accents and lowercase, then add posting
933
 
bool TextSplitDb::takeword(const std::string &_term, int pos, int, int)
934
 
{
935
 
    LOGDEB2(("TextSplitDb::takeword: [%s]\n", _term.c_str()));
936
 
 
937
 
    string term;
938
 
    if (!unacmaybefold(_term, term, "UTF-8", true)) {
939
 
        LOGINFO(("Db::splitter::takeword: unac failed for [%s]\n", 
940
 
                 _term.c_str()));
941
 
        term.clear();
942
 
        // We don't generate a fatal error because of a bad term
943
 
        return true;
944
 
    }
945
 
 
946
 
    if (stops.isStop(term)) {
947
 
        LOGDEB1(("Db: takeword [%s] in stop list\n", term.c_str()));
948
 
        return true;
949
 
    }
950
 
 
951
 
    // Compute absolute position (pos is relative to current segment),
952
 
    // and remember relative.
953
 
    curpos = pos;
954
 
    pos += basepos;
955
 
    string ermsg;
956
 
    try {
957
 
        // Index without prefix, using the field-specific weighting
958
 
        doc.add_posting(term, pos, wdfinc);
 
941
class TermProcIdx : public TermProc {
 
942
public:
 
943
    TermProcIdx() : TermProc(0), m_ts(0) {}
 
944
    void setTSD(TextSplitDb *ts) {m_ts = ts;}
 
945
 
 
946
    bool takeword(const std::string &term, int pos, int, int)
 
947
    {
 
948
        // Compute absolute position (pos is relative to current segment),
 
949
        // and remember relative.
 
950
        m_ts->curpos = pos;
 
951
        pos += m_ts->basepos;
 
952
        string ermsg;
 
953
        try {
 
954
            // Index without prefix, using the field-specific weighting
 
955
            LOGDEB1(("Emitting term at %d : [%s]\n", pos, term.c_str()));
 
956
            m_ts->doc.add_posting(term, pos, m_ts->wdfinc);
959
957
#ifdef TESTING_XAPIAN_SPELL
960
 
        if (Db::isSpellingCandidate(term)) {
961
 
            db.add_spelling(term);
962
 
        }
 
958
            if (Db::isSpellingCandidate(term)) {
 
959
                m_ts->db.add_spelling(term);
 
960
            }
963
961
#endif
964
 
        // Index the prefixed term.
965
 
        if (!prefix.empty()) {
966
 
            doc.add_posting(prefix + term, pos, wdfinc);
967
 
        }
968
 
        return true;
969
 
    } XCATCHERROR(ermsg);
970
 
    LOGERR(("Db: xapian add_posting error %s\n", ermsg.c_str()));
971
 
    return false;
972
 
}
 
962
            // Index the prefixed term.
 
963
            if (!m_ts->prefix.empty()) {
 
964
                m_ts->doc.add_posting(m_ts->prefix + term, pos, m_ts->wdfinc);
 
965
            }
 
966
            return true;
 
967
        } XCATCHERROR(ermsg);
 
968
        LOGERR(("Db: xapian add_posting error %s\n", ermsg.c_str()));
 
969
        return false;
 
970
    }
 
971
 
 
972
private:
 
973
    TextSplitDb *m_ts;
 
974
};
 
975
 
973
976
 
974
977
#ifdef TESTING_XAPIAN_SPELL
975
978
string Db::getSpellingSuggestion(const string& word)
1005
1008
 
1006
1009
#define RECORD_APPEND(R, NM, VAL) {R += NM + "=" + VAL + "\n";}
1007
1010
 
 
1011
#ifdef IDX_THREADS
 
1012
void *DbUpdWorker(void* vdbp)
 
1013
{
 
1014
    Db *dbp = (Db *)vdbp;
 
1015
    WorkQueue<DbUpdTask*> *tqp = &(dbp->m_ndb->m_wqueue);
 
1016
    DbUpdTask *tsk;
 
1017
 
 
1018
    for (;;) {
 
1019
        if (!tqp->take(&tsk)) {
 
1020
            tqp->workerExit();
 
1021
            return (void*)1;
 
1022
        }
 
1023
        LOGDEB(("DbUpdWorker: got task, ql %d\n", int(tqp->size())));
 
1024
 
 
1025
        const char *fnc = tsk->udi.c_str();
 
1026
        string ermsg;
 
1027
 
 
1028
        // Add db entry or update existing entry:
 
1029
        try {
 
1030
            Xapian::docid did = 
 
1031
                dbp->m_ndb->xwdb.replace_document(tsk->uniterm, 
 
1032
                                                  tsk->doc);
 
1033
            if (did < dbp->updated.size()) {
 
1034
                dbp->updated[did] = true;
 
1035
                LOGINFO(("Db::add: docid %d updated [%s]\n", did, fnc));
 
1036
            } else {
 
1037
                LOGINFO(("Db::add: docid %d added [%s]\n", did, fnc));
 
1038
            }
 
1039
        } XCATCHERROR(ermsg);
 
1040
 
 
1041
        if (!ermsg.empty()) {
 
1042
            LOGERR(("Db::add: replace_document failed: %s\n", ermsg.c_str()));
 
1043
            ermsg.erase();
 
1044
            // FIXME: is this ever actually needed?
 
1045
            try {
 
1046
                dbp->m_ndb->xwdb.add_document(tsk->doc);
 
1047
                LOGDEB(("Db::add: %s added (failed re-seek for duplicate)\n", 
 
1048
                        fnc));
 
1049
            } XCATCHERROR(ermsg);
 
1050
            if (!ermsg.empty()) {
 
1051
                LOGERR(("Db::add: add_document failed: %s\n", ermsg.c_str()));
 
1052
                tqp->workerExit();
 
1053
                return (void*)0;
 
1054
            }
 
1055
        }
 
1056
        dbp->maybeflush(tsk->txtlen);
 
1057
 
 
1058
        delete tsk;
 
1059
    }
 
1060
}
 
1061
#endif // IDX_THREADS
 
1062
 
1008
1063
// Add document in internal form to the database: index the terms in
1009
1064
// the title abstract and body and add special terms for file name,
1010
1065
// date, mime type etc. , create the document data record (more
1011
1066
// metadata), and update database
1012
1067
bool Db::addOrUpdate(const string &udi, const string &parent_udi,
1013
 
                     const Doc &idoc)
 
1068
                     Doc &doc)
1014
1069
{
1015
1070
    LOGDEB(("Db::add: udi [%s] parent [%s]\n", 
1016
1071
             udi.c_str(), parent_udi.c_str()));
1030
1085
        m_occtxtsz = m_curtxtsz;
1031
1086
    }
1032
1087
 
1033
 
    Doc doc = idoc;
1034
 
 
1035
1088
    Xapian::Document newdocument;
1036
 
    TextSplitDb splitter(m_ndb->xwdb, newdocument, m_stops);
 
1089
 
 
1090
    // The term processing pipeline:
 
1091
    TermProcIdx tpidx;
 
1092
    TermProc *nxt = &tpidx;
 
1093
    TermProcStop tpstop(nxt, m_stops);nxt = &tpstop;
 
1094
//    TermProcCommongrams tpcommon(nxt, m_stops); nxt = &tpcommon;
 
1095
    TermProcPrep tpprep(nxt); nxt = &tpprep;
 
1096
 
 
1097
    TextSplitDb splitter(newdocument, nxt);
 
1098
    tpidx.setTSD(&splitter);
1037
1099
 
1038
1100
    // Split and index file name as document term(s)
1039
1101
    LOGDEB2(("Db::add: split file name [%s]\n", fn.c_str()));
1193
1255
    }
1194
1256
    RECORD_APPEND(record, Doc::keyoc, doc.origcharset);
1195
1257
 
1196
 
    if (!doc.fbytes.empty())
 
1258
    if (doc.fbytes.empty())
 
1259
        doc.fbytes = doc.pcbytes;
 
1260
 
 
1261
    if (!doc.fbytes.empty()) {
1197
1262
        RECORD_APPEND(record, Doc::keyfs, doc.fbytes);
 
1263
        leftzeropad(doc.fbytes, 12);
 
1264
        newdocument.add_value(VALUE_SIZE, doc.fbytes);
 
1265
    }
 
1266
 
 
1267
    if (!doc.pcbytes.empty())
 
1268
        RECORD_APPEND(record, Doc::keypcs, doc.pcbytes);
 
1269
    char sizebuf[30]; 
 
1270
    sprintf(sizebuf, "%u", (unsigned int)doc.text.length());
 
1271
    RECORD_APPEND(record, Doc::keyds, sizebuf);
 
1272
 
1198
1273
    // Note that we add the signature both as a value and in the data record
1199
1274
    if (!doc.sig.empty())
1200
1275
        RECORD_APPEND(record, Doc::keysig, doc.sig);
1201
1276
    newdocument.add_value(VALUE_SIG, doc.sig);
1202
1277
 
1203
 
    char sizebuf[30]; 
1204
 
    sprintf(sizebuf, "%u", (unsigned int)doc.text.length());
1205
 
    RECORD_APPEND(record, Doc::keyds, sizebuf);
1206
 
 
1207
1278
    if (!doc.ipath.empty())
1208
1279
        RECORD_APPEND(record, Doc::keyipt, doc.ipath);
1209
1280
 
1210
 
    if (doc.meta[Doc::keytt].empty())
1211
 
        doc.meta[Doc::keytt] = doc.utf8fn;
1212
1281
    doc.meta[Doc::keytt] = 
1213
1282
        neutchars(truncate_to_word(doc.meta[Doc::keytt], 150), cstr_nc);
1214
1283
    if (!doc.meta[Doc::keytt].empty())
1215
 
        RECORD_APPEND(record, cstr_keycap, doc.meta[Doc::keytt]);
 
1284
        RECORD_APPEND(record, cstr_caption, doc.meta[Doc::keytt]);
1216
1285
 
1217
1286
    trimstring(doc.meta[Doc::keykw], " \t\r\n");
1218
1287
    doc.meta[Doc::keykw] = 
1264
1333
    LOGDEB0(("Rcl::Db::add: new doc record:\n%s\n", record.c_str()));
1265
1334
    newdocument.set_data(record);
1266
1335
 
 
1336
#ifdef IDX_THREADS
 
1337
    DbUpdTask *tp = new DbUpdTask(udi, uniterm, newdocument, doc.text.length());
 
1338
    if (!m_ndb->m_wqueue.put(tp)) {
 
1339
        LOGERR(("Db::addOrUpdate:Cant queue task\n"));
 
1340
        return false;
 
1341
    }
 
1342
#else
1267
1343
    const char *fnc = udi.c_str();
1268
1344
    string ermsg;
1269
1345
 
1296
1372
 
1297
1373
    // Test if we're over the flush threshold (limit memory usage):
1298
1374
    maybeflush(doc.text.length());
 
1375
#endif // IDX_THREADS
1299
1376
    return true;
1300
1377
}
1301
1378
 
1363
1440
            // Up to date. 
1364
1441
 
1365
1442
            // Set the uptodate flag for doc / pseudo doc
1366
 
            updated[*docid] = true;
 
1443
            if (m_mode  != DbRO) {
 
1444
                updated[*docid] = true;
1367
1445
 
1368
 
            // Set the existence flag for all the subdocs (if any)
1369
 
            vector<Xapian::docid> docids;
1370
 
            if (!m_ndb->subDocs(udi, docids)) {
1371
 
                LOGERR(("Rcl::Db::needUpdate: can't get subdocs list\n"));
1372
 
                return true;
1373
 
            }
1374
 
            for (vector<Xapian::docid>::iterator it = docids.begin();
1375
 
                 it != docids.end(); it++) {
1376
 
                if (*it < updated.size()) {
1377
 
                    LOGDEB2(("Db::needUpdate: set flag for docid %d\n", *it));
1378
 
                    updated[*it] = true;
 
1446
                // Set the existence flag for all the subdocs (if any)
 
1447
                vector<Xapian::docid> docids;
 
1448
                if (!m_ndb->subDocs(udi, docids)) {
 
1449
                    LOGERR(("Rcl::Db::needUpdate: can't get subdocs list\n"));
 
1450
                    return true;
 
1451
                }
 
1452
                for (vector<Xapian::docid>::iterator it = docids.begin();
 
1453
                     it != docids.end(); it++) {
 
1454
                    if (*it < updated.size()) {
 
1455
                        LOGDEB2(("Db::needUpdate: set flag for docid %d\n", *it));
 
1456
                        updated[*it] = true;
 
1457
                    }
1379
1458
                }
1380
1459
            }
1381
1460
            return false;
1446
1525
    if (m_ndb->m_isopen == false || m_ndb->m_iswritable == false) 
1447
1526
        return false;
1448
1527
 
 
1528
#ifdef IDX_THREADS
 
1529
    m_ndb->m_wqueue.waitIdle();
 
1530
#endif // IDX_THREADS
 
1531
 
1449
1532
    // For xapian versions up to 1.0.1, deleting a non-existant
1450
1533
    // document would trigger an exception that would discard any
1451
1534
    // pending update. This could lose both previous added documents
1511
1594
    LOGDEB(("Db:purgeFile: [%s]\n", udi.c_str()));
1512
1595
    if (m_ndb == 0 || !m_ndb->m_iswritable)
1513
1596
        return false;
 
1597
 
 
1598
#ifdef IDX_THREADS
 
1599
    m_ndb->m_wqueue.waitIdle();
 
1600
#endif // IDX_THREADS
 
1601
 
1514
1602
    Xapian::WritableDatabase db = m_ndb->xwdb;
1515
1603
    string uniterm = make_uniterm(udi);
1516
1604
    string ermsg;
1559
1647
    // each end: match any substring
1560
1648
    if (pattern[0] == '"' && pattern[pattern.size()-1] == '"') {
1561
1649
        pattern = pattern.substr(1, pattern.size() -2);
1562
 
    } else if (pattern.find_first_of("*?[") == string::npos && 
 
1650
    } else if (pattern.find_first_of(cstr_minwilds) == string::npos && 
1563
1651
               !unaciscapital(pattern)) {
1564
1652
        pattern = "*" + pattern + "*";
1565
1653
    } // else let it be