Prevent Lucene search engine updates from being interrupted (#4681)

#### What type of PR is this?

/kind bug
/area core

#### What this PR does / why we need it:

Lucene search engine will stop updating while the content of any post is `null`.

This PR resets the `null` content into empty string and ignore the error while updating Lucene document.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4623

#### Does this PR introduce a user-facing change?

```release-note
修复因某篇文章的内容为 null 导致无法搜索部分文章的问题
```
pull/4688/head^2
John Niang 2023-10-07 05:40:45 -05:00 committed by GitHub
parent 3edf8632d3
commit 40565f1f32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 7 additions and 6 deletions

View File

@ -1,5 +1,6 @@
package run.halo.app.search.post; package run.halo.app.search.post;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.lucene.document.Field.Store.YES; import static org.apache.lucene.document.Field.Store.YES;
import static org.apache.lucene.index.IndexWriterConfig.OpenMode.CREATE_OR_APPEND; import static org.apache.lucene.index.IndexWriterConfig.OpenMode.CREATE_OR_APPEND;
@ -40,7 +41,6 @@ import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch; import org.springframework.util.StopWatch;
import reactor.core.Exceptions;
import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.properties.HaloProperties;
import run.halo.app.search.SearchParam; import run.halo.app.search.SearchParam;
import run.halo.app.search.SearchResult; import run.halo.app.search.SearchResult;
@ -121,16 +121,17 @@ public class LucenePostSearchService implements PostSearchService, DisposableBea
writeConfig.setOpenMode(CREATE_OR_APPEND); writeConfig.setOpenMode(CREATE_OR_APPEND);
try (var writer = new IndexWriter(postIndexDir, writeConfig)) { try (var writer = new IndexWriter(postIndexDir, writeConfig)) {
posts.forEach(post -> { posts.forEach(post -> {
var doc = this.convert(post);
try { try {
var doc = this.convert(post);
var seqNum = var seqNum =
writer.updateDocument(new Term(PostDoc.ID_FIELD, post.name()), doc); writer.updateDocument(new Term(PostDoc.ID_FIELD, post.name()), doc);
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Updated document({}) with sequence number {} returned", log.debug("Updated document({}) with sequence number {} returned",
post.name(), seqNum); post.name(), seqNum);
} }
} catch (IOException e) { } catch (Throwable e) {
throw Exceptions.propagate(e); // Continue next update
log.error("Failed to update document for post {}", post.name(), e);
} }
}); });
} }
@ -180,8 +181,8 @@ public class LucenePostSearchService implements PostSearchService, DisposableBea
var doc = new Document(); var doc = new Document();
doc.add(new StringField("name", post.name(), YES)); doc.add(new StringField("name", post.name(), YES));
doc.add(new TextField("title", post.title(), YES)); doc.add(new TextField("title", post.title(), YES));
doc.add(new TextField("excerpt", post.excerpt(), YES)); doc.add(new TextField("excerpt", defaultString(post.excerpt()), YES));
doc.add(new TextField("content", post.content(), YES)); doc.add(new TextField("content", defaultString(post.content()), YES));
var publishTimestamp = post.publishTimestamp().getEpochSecond(); var publishTimestamp = post.publishTimestamp().getEpochSecond();
doc.add(new LongPoint("publishTimestamp", publishTimestamp)); doc.add(new LongPoint("publishTimestamp", publishTimestamp));