Schema for secondary index over accounts
Support searching over the basic fields in Account (ID, registered on, etc.), as well as more complicated fields like AccountExternalId and a fuzzy search over name parts. Change-Id: I3eea825b0e6aa19d87311194ce386a0f07db41e5
This commit is contained in:
@@ -30,8 +30,8 @@ import org.eclipse.jgit.lib.PersonIdent;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -81,13 +81,28 @@ public class SchemaUtil {
|
||||
if (person == null) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
HashSet<String> parts = Sets.newHashSet();
|
||||
String email = person.getEmailAddress().toLowerCase();
|
||||
parts.add(email);
|
||||
parts.addAll(Arrays.asList(email.split("@")));
|
||||
return getPersonParts(
|
||||
person.getName(),
|
||||
Collections.singleton(person.getEmailAddress()));
|
||||
}
|
||||
|
||||
public static Set<String> getPersonParts(String name,
|
||||
Iterable<String> emails) {
|
||||
Splitter at = Splitter.on('@');
|
||||
Splitter s = Splitter.on(CharMatcher.anyOf("@.- ")).omitEmptyStrings();
|
||||
Iterables.addAll(parts, s.split(email));
|
||||
Iterables.addAll(parts, s.split(person.getName().toLowerCase()));
|
||||
HashSet<String> parts = Sets.newHashSet();
|
||||
for (String email : emails) {
|
||||
if (email == null) {
|
||||
continue;
|
||||
}
|
||||
String lowerEmail = email.toLowerCase();
|
||||
parts.add(lowerEmail);
|
||||
Iterables.addAll(parts, at.split(lowerEmail));
|
||||
Iterables.addAll(parts, s.split(lowerEmail));
|
||||
}
|
||||
if (name != null) {
|
||||
Iterables.addAll(parts, s.split(name.toLowerCase()));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,140 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.index.account;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.FieldType;
|
||||
import com.google.gerrit.server.index.SchemaUtil;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/** Secondary index schemas for accounts. */
|
||||
public class AccountField {
|
||||
public static final FieldDef<AccountState, Integer> ID =
|
||||
new FieldDef.Single<AccountState, Integer>(
|
||||
"id", FieldType.INTEGER, true) {
|
||||
@Override
|
||||
public Integer get(AccountState input, FillArgs args) {
|
||||
return input.getAccount().getId().get();
|
||||
}
|
||||
};
|
||||
|
||||
public static final FieldDef<AccountState, Iterable<String>> EXTERNAL_ID =
|
||||
new FieldDef.Repeatable<AccountState, String>(
|
||||
"external_id", FieldType.EXACT, false) {
|
||||
@Override
|
||||
public Iterable<String> get(AccountState input, FillArgs args) {
|
||||
return Iterables.transform(
|
||||
input.getExternalIds(),
|
||||
new Function<AccountExternalId, String>() {
|
||||
@Override
|
||||
public String apply(AccountExternalId in) {
|
||||
return in.getKey().get();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** Fuzzy prefix match on name and email parts. */
|
||||
public static final FieldDef<AccountState, Iterable<String>> NAME_PART =
|
||||
new FieldDef.Repeatable<AccountState, String>(
|
||||
"name", FieldType.PREFIX, false) {
|
||||
@Override
|
||||
public Iterable<String> get(AccountState input, FillArgs args) {
|
||||
String fullName = input.getAccount().getFullName();
|
||||
Set<String> parts = SchemaUtil.getPersonParts(
|
||||
fullName,
|
||||
Iterables.transform(
|
||||
input.getExternalIds(),
|
||||
new Function<AccountExternalId, String>() {
|
||||
@Override
|
||||
public String apply(AccountExternalId in) {
|
||||
return in.getEmailAddress();
|
||||
}
|
||||
}));
|
||||
|
||||
// Additional values not currently added by getPersonParts.
|
||||
// TODO(dborowitz): Move to getPersonParts and remove this hack.
|
||||
if (fullName != null) {
|
||||
parts.add(fullName);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
};
|
||||
|
||||
public static final FieldDef<AccountState, String> ACTIVE =
|
||||
new FieldDef.Single<AccountState, String>(
|
||||
"inactive", FieldType.EXACT, false) {
|
||||
@Override
|
||||
public String get(AccountState input, FillArgs args) {
|
||||
return input.getAccount().isActive() ? "1" : "0";
|
||||
}
|
||||
};
|
||||
|
||||
public static final FieldDef<AccountState, Iterable<String>> EMAIL =
|
||||
new FieldDef.Repeatable<AccountState, String>(
|
||||
"email", FieldType.PREFIX, false) {
|
||||
@Override
|
||||
public Iterable<String> get(AccountState input, FillArgs args) {
|
||||
return FluentIterable.from(input.getExternalIds())
|
||||
.transform(
|
||||
new Function<AccountExternalId, String>() {
|
||||
@Override
|
||||
public String apply(AccountExternalId in) {
|
||||
return in.getEmailAddress();
|
||||
}
|
||||
})
|
||||
.append(
|
||||
Collections.singleton(input.getAccount().getPreferredEmail()))
|
||||
.filter(Predicates.notNull())
|
||||
.transform(
|
||||
new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String in) {
|
||||
return in.toLowerCase();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static final FieldDef<AccountState, Timestamp> REGISTERED =
|
||||
new FieldDef.Single<AccountState, Timestamp>(
|
||||
"registered", FieldType.TIMESTAMP, false) {
|
||||
@Override
|
||||
public Timestamp get(AccountState input, FillArgs args) {
|
||||
return input.getAccount().getRegisteredOn();
|
||||
}
|
||||
};
|
||||
|
||||
public static final FieldDef<AccountState, String> USERNAME =
|
||||
new FieldDef.Single<AccountState, String>(
|
||||
"username", null, false) {
|
||||
@Override
|
||||
public String get(AccountState input, FillArgs args) {
|
||||
return input.getUserName().toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
private AccountField() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.index.account;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.Schema;
|
||||
import com.google.gerrit.server.index.SchemaUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class AccountSchemas {
|
||||
static final Schema<AccountState> V1 = schema(
|
||||
AccountField.ID,
|
||||
AccountField.ACTIVE,
|
||||
AccountField.EMAIL,
|
||||
AccountField.EXTERNAL_ID,
|
||||
AccountField.NAME_PART,
|
||||
AccountField.REGISTERED,
|
||||
AccountField.USERNAME);
|
||||
|
||||
private static Schema<AccountState> schema(
|
||||
Collection<FieldDef<AccountState, ?>> fields) {
|
||||
return new Schema<>(ImmutableList.copyOf(fields));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static Schema<AccountState> schema(
|
||||
FieldDef<AccountState, ?>... fields) {
|
||||
return schema(ImmutableList.copyOf(fields));
|
||||
}
|
||||
|
||||
public static final ImmutableMap<Integer, Schema<AccountState>> ALL =
|
||||
SchemaUtil.schemasFromClass(AccountSchemas.class, AccountState.class);
|
||||
|
||||
public static Schema<AccountState> get(int version) {
|
||||
Schema<AccountState> schema = ALL.get(version);
|
||||
checkArgument(schema != null, "Unrecognized schema version: %s", version);
|
||||
return schema;
|
||||
}
|
||||
|
||||
public static Schema<AccountState> getLatest() {
|
||||
return Iterables.getLast(ALL.values());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user