1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2018, 2019, Chris Fraire <cfraire@me.com>. 22 */ 23 package org.opengrok.indexer.index; 24 25 import java.io.IOException; 26 import org.apache.lucene.document.Document; 27 import org.apache.lucene.document.Field; 28 import org.apache.lucene.document.StoredField; 29 import org.apache.lucene.document.StringField; 30 import org.apache.lucene.index.IndexReader; 31 import org.apache.lucene.index.IndexWriter; 32 import org.apache.lucene.index.IndexableField; 33 import org.apache.lucene.index.Term; 34 import org.apache.lucene.queryparser.classic.ParseException; 35 import org.apache.lucene.queryparser.classic.QueryParser; 36 import org.apache.lucene.search.IndexSearcher; 37 import org.apache.lucene.search.Query; 38 import org.apache.lucene.search.TopDocs; 39 import org.opengrok.indexer.analysis.CompatibleAnalyser; 40 import org.opengrok.indexer.search.QueryBuilder; 41 42 /** 43 * Represents a data-access object for {@link IndexAnalysisSettings}. 44 */ 45 public class IndexAnalysisSettingsAccessor { 46 47 /** 48 * The {@link QueryBuilder}-normalized value for UUID 58859C75-F941-42E5-8D1A-FAF71DDEBBA7. 49 */ 50 static final String INDEX_ANALYSIS_SETTINGS_OBJUID = "uthuslvotkgltggqqjmurqojpjpjjkutkujktnkk"; 51 52 private static final int INDEX_ANALYSIS_SETTINGS_OBJVER = 3; 53 54 /** 55 * Searches for a document with a {@link QueryBuilder#OBJUID} value matching 56 * {@link #INDEX_ANALYSIS_SETTINGS_OBJUID}. The first document found is 57 * returned, so this should not be called with a MultiReader. 58 * @param reader a defined instance 59 * @return a defined instance or {@code null} if none could be found 60 * @throws IOException if I/O error occurs while searching Lucene 61 */ read(IndexReader reader)62 public IndexAnalysisSettings3 read(IndexReader reader) throws IOException { 63 IndexAnalysisSettings3[] res = read(reader, 1); 64 return res.length > 0 ? res[0] : null; 65 } 66 67 /** 68 * Searches for documents with a {@link QueryBuilder#OBJUID} value matching 69 * {@link #INDEX_ANALYSIS_SETTINGS_OBJUID}. 70 * @param reader a defined instance 71 * @param n a limit to the number of documents returned. The method may 72 * return less. 73 * @return a defined instance, which is empty if none could be found 74 * @throws IOException if I/O error occurs while searching Lucene 75 */ read(IndexReader reader, int n)76 public IndexAnalysisSettings3[] read(IndexReader reader, int n) 77 throws IOException { 78 IndexSearcher searcher = new IndexSearcher(reader); 79 Query q; 80 try { 81 q = new QueryParser(QueryBuilder.OBJUID, new CompatibleAnalyser()). 82 parse(INDEX_ANALYSIS_SETTINGS_OBJUID); 83 } catch (ParseException ex) { 84 // This is not expected, so translate to RuntimeException. 85 throw new RuntimeException(ex); 86 } 87 TopDocs top = searcher.search(q, n); 88 89 int nres = top.totalHits.value > n ? n : (int) top.totalHits.value; 90 IndexAnalysisSettings3[] res = new IndexAnalysisSettings3[nres]; 91 92 IndexAnalysisSettingsUpgrader upgrader = new IndexAnalysisSettingsUpgrader(); 93 for (int i = 0; i < nres; ++i) { 94 Document doc = searcher.doc(top.scoreDocs[i].doc); 95 IndexableField objser = doc.getField(QueryBuilder.OBJSER); 96 int objver = readObjectVersion(doc); 97 try { 98 res[i] = objser == null ? null : upgrader.upgrade( 99 objser.binaryValue().bytes, objver); 100 } catch (ClassNotFoundException ex) { 101 // This is not expected, so translate to RuntimeException. 102 throw new RuntimeException(ex); 103 } 104 } 105 return res; 106 } 107 108 /** 109 * Writes a document to contain the serialized version of {@code settings}, 110 * with a {@link QueryBuilder#OBJUID} value set to 111 * {@link #INDEX_ANALYSIS_SETTINGS_OBJUID}. An existing version of the 112 * document is first deleted. 113 * @param writer a defined, target instance 114 * @param settings a defined instance 115 * @throws IOException if I/O error occurs while writing Lucene 116 */ write(IndexWriter writer, IndexAnalysisSettings3 settings)117 public void write(IndexWriter writer, IndexAnalysisSettings3 settings) 118 throws IOException { 119 byte[] objser = settings.serialize(); 120 121 writer.deleteDocuments(new Term(QueryBuilder.OBJUID, 122 INDEX_ANALYSIS_SETTINGS_OBJUID)); 123 124 Document doc = new Document(); 125 StringField uidfield = new StringField(QueryBuilder.OBJUID, 126 INDEX_ANALYSIS_SETTINGS_OBJUID, Field.Store.NO); 127 doc.add(uidfield); 128 doc.add(new StoredField(QueryBuilder.OBJSER, objser)); 129 doc.add(new StoredField(QueryBuilder.OBJVER, 130 INDEX_ANALYSIS_SETTINGS_OBJVER)); 131 writer.addDocument(doc); 132 } 133 readObjectVersion(Document doc)134 private int readObjectVersion(Document doc) { 135 IndexableField objver = doc.getField(QueryBuilder.OBJVER); 136 return objver == null ? 1 : objver.numericValue().intValue(); 137 } 138 } 139