Skip to content

Commit 6a03c3f

Browse files
committed
Make indexing behaviour if document exists configurable.
That is, if a document with the same persistent identifier (pidField) is already in the index, do we fail, replace it or skip it? A pidField must be configured for this, otherwise the document is always added (so you may get duplicate documents). Configure the behaviour in blacklab[-server].yaml in the indexing section with the ifDocumentExists setting (valid values: fail, replace, skip) or by passing --ifexists <value> to IndexTool.
1 parent bacd803 commit 6a03c3f

5 files changed

Lines changed: 71 additions & 14 deletions

File tree

engine/src/main/java/nl/inl/blacklab/config/BLConfigIndexing.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package nl.inl.blacklab.config;
22

3+
import nl.inl.blacklab.search.BlackLabIndexWriter;
34
import nl.inl.util.DownloadCache;
45

56
public class BLConfigIndexing {
@@ -36,6 +37,11 @@ public class BLConfigIndexing {
3637
@Deprecated
3738
int maxMetadataValuesToStore = 0;
3839

40+
/** What to do if a document already exists in the index (i.e. has the same persistent identifier):
41+
* fail (default), replace (i.e. upsert) or skip?
42+
*/
43+
BlackLabIndexWriter.IfDocumentExists ifDocumentExists = BlackLabIndexWriter.IfDocumentExists.FAIL;
44+
3945
public DownloadCache.Config downloadCacheConfig() {
4046
return new DownloadCache.Config() {
4147
@Override
@@ -155,7 +161,11 @@ public void setMaxValueLength(int maxValueLength) {
155161
this.maxValueLength = maxValueLength;
156162
}
157163

158-
public void setMaxMetadataValuesToStore(int maxMetadataValuesToStore) {
159-
this.maxMetadataValuesToStore = maxMetadataValuesToStore;
164+
public BlackLabIndexWriter.IfDocumentExists getIfDocumentExists() {
165+
return this.ifDocumentExists;
166+
}
167+
168+
public void setIfDocumentExists(BlackLabIndexWriter.IfDocumentExists ifDocumentExists) {
169+
this.ifDocumentExists = ifDocumentExists;
160170
}
161171
}

engine/src/main/java/nl/inl/blacklab/index/BLIndexWriterProxyLucene.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ public void addDocument(BLInputDocument document) throws IOException {
9292
break;
9393
case FAIL:
9494
throw new ErrorIndexingFile("Document with pid '" + pid +
95-
"' already exists in corpus; cannot add document: " + document);
95+
"' already exists in corpus; cannot add it again " +
96+
"(ifDocumentExists setting set to 'fail'; set to 'replace' to upsert instead)");
9697
default:
9798
throw new IllegalArgumentException();
9899
}

engine/src/main/java/nl/inl/blacklab/search/BlackLabIndexWriter.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import org.apache.lucene.search.Query;
88
import org.apache.lucene.search.TermQuery;
99

10+
import com.fasterxml.jackson.annotation.JsonCreator;
11+
import com.fasterxml.jackson.annotation.JsonValue;
12+
1013
import nl.inl.blacklab.index.BLIndexObjectFactory;
1114
import nl.inl.blacklab.index.BLIndexWriterProxy;
1215
import nl.inl.blacklab.index.BLInputDocument;
@@ -27,12 +30,35 @@ enum IfDocumentExists {
2730
SKIP,
2831

2932
/** Fail with an error message. */
30-
FAIL,
33+
FAIL;
34+
35+
@JsonCreator
36+
public static IfDocumentExists forValue(String ifDocumentExists) {
37+
switch (ifDocumentExists.toLowerCase()) {
38+
case "upsert", "replace", "overwrite" -> {
39+
return UPSERT;
40+
}
41+
case "skip" -> {
42+
return SKIP;
43+
}
44+
case "fail" -> {
45+
return FAIL;
46+
}
47+
default -> throw new IllegalArgumentException("Unknown IfDocumentExists value: " + ifDocumentExists + "(valid values: fail, replace or skip)");
48+
}
49+
}
50+
51+
@JsonValue
52+
@Override
53+
public String toString() {
54+
return super.toString().toLowerCase();
55+
}
3156
}
3257

3358
/** What to do if a document with the same persistent identifier (pidField) already exists? */
3459
default IfDocumentExists getIfDocumentExists() {
35-
return IfDocumentExists.UPSERT; // TODO: make configurable (IndexTool cmdline, BLS config)
60+
return BlackLab.config().getIndexing().getIfDocumentExists();
61+
//return IfDocumentExists.UPSERT; // TODO: make configurable (IndexTool cmdline, BLS config)
3662
}
3763

3864
/**

site/docs/server/050_configuration.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,24 @@ You probably won't need to change these settings, but they are available for adv
331331
```yaml
332332
indexing:
333333
# (...above settings...)
334+
335+
# What to do if a document with the same persistent identifier
336+
# already exists in the index.
337+
# Options: fail (default), replace, skip
338+
# (note that this will never apply if no pidField has been configured!)
339+
ifDocumentExists: fail
334340
335341
# Should inline tags and relations be indexed case- and accent-sensitively?
336342
# This used to be the default, but we've switched over to case-insensitive
337343
# indexing by default. Set to true to revert to the old behavior.
338344
# (default: false)
339345
relationsSensitive: false
340346
347+
# Max. length of a value of an annotation value (i.e. word, lemma)
348+
# or 0 for no limit (Lucene has a limit of 32766 characters for a term though;
349+
# longer values will cause an error)
350+
maxValueLength: 0
351+
341352
# Are http downloads of e.g. metadata allowed?
342353
# (default: false)
343354
downloadAllowed: false

tools/src/main/java/nl/inl/blacklab/tools/IndexTool.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,23 @@ public static void main(String[] args) throws ErrorOpeningIndex, ParseException,
147147
return;
148148
}
149149
}
150+
case "ifexists" -> {
151+
if (i + 1 == args.length) {
152+
System.err.println("--ifexists option needs argument");
153+
usage();
154+
return;
155+
}
156+
try {
157+
BlackLabIndexWriter.IfDocumentExists ifExists = BlackLabIndexWriter.IfDocumentExists.forValue(
158+
args[i + 1]);
159+
BlackLab.config().getIndexing().setIfDocumentExists(ifExists);
160+
i++;
161+
} catch (IllegalArgumentException e) {
162+
System.err.println("--ifexists option needs valid argument (fail, replace or skip)");
163+
usage();
164+
return;
165+
}
166+
}
150167
case "help" -> {
151168
usage();
152169
return;
@@ -243,19 +260,11 @@ public static void main(String[] args) throws ErrorOpeningIndex, ParseException,
243260
// Init log4j
244261
LogUtil.setupBasicLoggingConfig();
245262

246-
List<File> dirs = new ArrayList<>(List.of(new File(".")));
247263
Optional<File> inputDir = indexSource.getAssociatedDirectory();
248264
File inputDirParent = null;
249-
if (inputDir.isPresent()) {
250-
dirs.add(inputDir.get());
265+
if (inputDir.isPresent())
251266
inputDirParent = inputDir.get().getAbsoluteFile().getParentFile();
252-
}
253-
if (inputDirParent != null)
254-
dirs.add(inputDirParent);
255-
dirs.add(indexDir);
256267
File indexDirParent = indexDir.getAbsoluteFile().getParentFile();
257-
if (indexDirParent != null)
258-
dirs.add(indexDirParent);
259268

260269
String op = forceCreateNew ? "Creating new" : "Appending to";
261270
System.out.println(op + " index in " + indexDir + File.separator + " from " + indexSource +

0 commit comments

Comments
 (0)