Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.index; 16 : 17 : import com.google.common.collect.ImmutableList; 18 : import com.google.gerrit.exceptions.StorageException; 19 : import com.google.gerrit.index.query.DataSource; 20 : import com.google.gerrit.index.query.FieldBundle; 21 : import com.google.gerrit.index.query.IndexPredicate; 22 : import com.google.gerrit.index.query.Predicate; 23 : import com.google.gerrit.index.query.QueryParseException; 24 : import java.util.Optional; 25 : 26 : /** 27 : * Secondary index implementation for arbitrary documents. 28 : * 29 : * <p>Documents are inserted into the index and are queried by converting special {@link 30 : * com.google.gerrit.index.query.Predicate} instances into index-aware predicates that use the index 31 : * search results as a source. 32 : * 33 : * <p>Implementations must be thread-safe and should batch inserts/updates where appropriate. 34 : */ 35 : public interface Index<K, V> { 36 : /** Returns the schema version used by this index. */ 37 : Schema<V> getSchema(); 38 : 39 : /** Close this index. */ 40 : void close(); 41 : 42 : /** 43 : * Insert a document into the index. 44 : * 45 : * <p>Results may not be immediately visible to searchers, but should be visible within a 46 : * reasonable amount of time. 47 : * 48 : * @param obj document object 49 : */ 50 : void insert(V obj); 51 : 52 : /** 53 : * Update a document in the index. 54 : * 55 : * <p>Semantically equivalent to deleting the document and reinserting it with new field values. A 56 : * document that does not already exist is created. Results may not be immediately visible to 57 : * searchers, but should be visible within a reasonable amount of time. 58 : * 59 : * @param obj document object 60 : */ 61 : void replace(V obj); 62 : 63 : /** 64 : * Delete a document from the index by key. 65 : * 66 : * @param key document key 67 : */ 68 : void delete(K key); 69 : 70 : /** Delete all documents from the index. */ 71 : void deleteAll(); 72 : 73 : /** 74 : * Convert the given operator predicate into a source searching the index and returning only the 75 : * documents matching that predicate. 76 : * 77 : * <p>This method may be called multiple times for variations on the same predicate or multiple 78 : * predicate subtrees in the course of processing a single query, so it should not have any side 79 : * effects (e.g. starting a search in the background). 80 : * 81 : * @param p the predicate to match. Must be a tree containing only AND, OR, or NOT predicates as 82 : * internal nodes, and {@link IndexPredicate}s as leaves. 83 : * @param opts query options not implied by the predicate, such as start and limit. 84 : * @return a source of documents matching the predicate, returned in a defined order depending on 85 : * the type of documents. 86 : * @throws QueryParseException if the predicate could not be converted to an indexed data source. 87 : */ 88 : DataSource<V> getSource(Predicate<V> p, QueryOptions opts) throws QueryParseException; 89 : 90 : /** 91 : * Get a single document from the index. 92 : * 93 : * @param key document key. 94 : * @param opts query options. Options that do not make sense in the context of a single document, 95 : * such as start, will be ignored. 96 : * @return a single document if present. 97 : */ 98 : default Optional<V> get(K key, QueryOptions opts) { 99 6 : opts = opts.withStart(0).withLimit(2); 100 : ImmutableList<V> results; 101 : try { 102 6 : results = getSource(keyPredicate(key), opts).read().toList(); 103 0 : } catch (QueryParseException e) { 104 0 : throw new StorageException("Unexpected QueryParseException during get()", e); 105 6 : } 106 6 : if (results.size() > 1) { 107 0 : throw new StorageException("Multiple results found in index for key " + key + ": " + results); 108 : } 109 6 : return results.stream().findFirst(); 110 : } 111 : 112 : /** 113 : * Get a single raw document from the index. 114 : * 115 : * @param key document key. 116 : * @param opts query options. Options that do not make sense in the context of a single document, 117 : * such as start, will be ignored. 118 : * @return an abstraction of a raw index document to retrieve fields from. 119 : */ 120 : default Optional<FieldBundle> getRaw(K key, QueryOptions opts) { 121 10 : opts = opts.withStart(0).withLimit(2); 122 : ImmutableList<FieldBundle> results; 123 : try { 124 10 : results = getSource(keyPredicate(key), opts).readRaw().toList(); 125 0 : } catch (QueryParseException e) { 126 0 : throw new StorageException("Unexpected QueryParseException during get()", e); 127 10 : } 128 10 : if (results.size() > 1) { 129 0 : throw new StorageException("Multiple results found in index for key " + key + ": " + results); 130 : } 131 10 : return results.stream().findFirst(); 132 : } 133 : 134 : /** 135 : * Get a predicate that looks up a single document by key. 136 : * 137 : * @param key document key. 138 : * @return a single predicate. 139 : */ 140 : Predicate<V> keyPredicate(K key); 141 : 142 : /** 143 : * Mark whether this index is up-to-date and ready to serve reads. 144 : * 145 : * @param ready whether the index is ready 146 : */ 147 : void markReady(boolean ready); 148 : 149 : /** 150 : * Returns whether the index is enabled. {@code true} by default, but could be overridden by 151 : * implementations. 152 : */ 153 : default boolean isEnabled() { 154 53 : return true; 155 : } 156 : }