Статьи

Что если бы коллекции Java и иерархии Java были легко доступны для поиска? Они с Бун!

Что если бы было легко сделать запрос сложного набора объектов Java во время выполнения? Что если бы существовал API, который поддерживал ваши индексы объектов (на самом деле только TreeMaps и HashMaps) в синхронизации.? Хорошо, тогда у вас будет репозиторий Boon. В этой статье показано, как использовать утилиты репозитория Boon для запросов к объектам Java. Это первая часть. Там может быть много, много частей. 🙂

Репозиторий Boon значительно упрощает выполнение запросов к коллекциям на основе индексов.

Почему Бун данных репо

Репозиторий Boon позволяет вам рассматривать коллекции Java больше как базу данных, по крайней мере, когда дело доходит до запросов к коллекциям. Репозиторий Boon не является базой данных в памяти и не может заменить размещение ваших объектов в структурах данных, оптимизированных для вашего приложения.

Если вы хотите тратить свое время на предоставление клиентской ценности, создание объектов и классов и использование API коллекций для ваших структур данных, то DataRepo предназначена для вас. Это не исключает появления книг Кнута и создания оптимизированной структуры данных. Это просто помогает упростить мирские дела, чтобы вы могли тратить время на то, чтобы делать сложные вещи.

Родился по нужде

Этот проект вышел из необходимости. Я работал над проектом, который планировал хранить большую коллекцию доменных объектов в оперативной памяти, и кто-то задал важный для меня вопрос, который я пропустил. Как мы собираемся запросить эти данные. Я ответил, что мы будем использовать API коллекций и API потоковой передачи. Тогда я попытался сделать это … Хммм …

Я также устал использовать потоковый API JDK 8 для большого набора данных, и это было медленно. (Репозиторий Boon работает с JDK7 и JDK8). Это был линейный поиск / фильтр. Это по замыслу, но для того, что я делал, это не сработало. Мне нужны индексы для поддержки произвольных запросов.

Благо в Dzone!

Репозиторий Boon дополняет потоковый API.

Репозиторий Boon не стремится заменить потоковый API JDK 8, и фактически с ним хорошо работает. Репозиторий Boon позволяет создавать индексированные коллекции. Индексы могут быть любыми (это подключаемые).

В данный момент индексы репо данных Boon основаны на  ConcurrentHashMap  и  ConcurrentSkipListMap .

По своему дизайну репозиторий Boon работает со стандартными библиотеками коллекций. Не планируется создавать набор пользовательских коллекций. Нужно иметь возможность подключить Guava, Concurrent Trees или Trove, если кто-то хочет это сделать.

Это обеспечивает упрощенный API для этого. Он позволяет выполнять линейный поиск для определения завершенности, но я рекомендую использовать его в первую очередь для использования индексов, а затем использовать потоковый API для остальных (для безопасности типов и скорости).

Пик пробега перед пошаговым

Допустим, у вас есть метод, который создает 200 000 объектов сотрудников, например:

 List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);

Так что теперь у нас 200 000 сотрудников. Давайте искать их …

Первая упаковка сотрудников в поисковом запросе:

 employees = query(employees);

Теперь поиск:

 List<Employee> results = query(employees, eq("firstName", firstName));

Так в чем же главное отличие вышеуказанного API от потокового?

 employees.stream().filter(emp -> emp.getFirstName().equals(firstName)

Приблизительно в 20 000% быстрее использовать DataRepo от Boon! Ах, сила HashMaps и TreeMaps. 🙂

Вопрос: ОБНОВЛЕНИЕ ВОПРОСА ОТ ЧИТАТЕЛЯ:

В субботу, 2 ноября 2013 года, Крис Б написал:

«Очень интересно, Рик, у меня было время только на быстрое прочтение статьи, поэтому прости меня, если на этот вопрос ответят в твоей статье. Но мне было любопытно, какие накладные расходы будут возникать при создании индексов, когда ты оборачиваешь свою коллекцию в объект запроса … Скажите для 200_000 сотрудников в приведенном ниже примере. Сколько времени потребуется для построения индексированной структуры? «

Спасибо, Крис. Хороший вопрос! Это было бы довольно дорого, поэтому вы могли бы использовать эту конструкцию только в том случае, если планируете какое-то время удерживать коллекцию. Существует также   объект репо, если вы хотите постепенно обновлять индексы коллекции.

Пример использования: представьте, что вы извлекаете список сотрудников из memcached каждые 20 минут. Вы хотите выполнять запросы к сотрудникам, поэтому вам нужны индексы, и вы хотите что-то быстрее, чем хранить каждую возможную комбинацию запроса в  memcached  (да, я видел, что люди делают это с настройкой в ​​12 ГБ … в 20 раз больше, чем данные в БД. Я видел это не раз). Итак, теперь вы опускаете их, запрашиваете коллекцию с возможностью поиска Это позволяет избежать вызова  memcached  и взрыва каждого возможного кешируемого запроса. В любом случае … хранилище данных было написано, чтобы избежать многих анти-паттернов, которые я видел при кэшировании. Это позволяет вам выполнять запросы к объектам Java. 

Существует API, который выглядит так же, как ваши встроенные коллекции. Существует также API, который больше похож на объект DAO или объект Repo.

Простой запрос с объектом Repo / DAO выглядит следующим образом (репо позволяет постепенно обновлять индексы):

 List<Employee> employees = repo.query(eq("firstName", "Diana"));

Более сложный запрос будет выглядеть так:

 List<Employee> employees = repo.query(
and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));

Или это:

 List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
gte("salary", 190_000)));

Или даже это:

 List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));

Или, если вы хотите использовать потоковый API JDK 8, это работает не против:

 int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
.mapToInt(b -> b.getSalary())
.sum();

Вышеперечисленное было бы намного быстрее, если бы количество сотрудников было довольно большим. Это сузило бы сотрудников, чье имя начиналось с Смита и получало зарплату выше 50 000. Допустим, у вас было 100 000 сотрудников и только 50 по имени Смит, так что теперь вы быстро сужаетесь до 50, используя индекс, который эффективно вытягивает 50 сотрудников из 100 000, а затем мы фильтруем всего 50 вместо целых 100 000.

Вот контрольный прогон из репо линейного поиска по сравнению с индексированным поиском в наносекундах:

Именной указатель Time 218 Boon data repo!

Имя линейное Время 3542320 Не благоприятно. 🙁

Имя индекса Время 218 

Имя линейного времени 3511667 

Имя индекса Время 218 

Имя линейного времени 3709120 

Имя индекса Время 213 

Имя линейного времени 3606171 

Имя индекса Время 219 

Имя линейного времени 3528839

Кто-то недавно сказал мне: «Но с потоковым API вы можете запустить фильтр в parralel).

Давайте посмотрим, как работает математика:

35,28,839 / 16 потоков против 219

201,802 против 219.

Индексы выигрывают, но это было фотофиниш. 🙂 

Это было только на 9,500% быстрее вместо 40 000% быстрее. так близко …..

По умолчанию все поисковые индексы и индексы поиска допускают дублирование (кроме индекса первичного ключа).

 repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);

Вы можете переопределить это, указав истинный флаг в качестве второго аргумента для searchIndex.

Обратите внимание, что empNum — это уникальный для поиска индекс.

 Использование репозитория Boon на примере

Краткое объявление от нашего спонсора: «Благо = простая самоуверенная Java для начинающих программистов на Java. Низкая церемония. Высокая производительность. Настоящая находка для разработчиков Java!»

Бун Главная | Благо Источник | Если вы новичок в благе, вы можете начать здесь .

Java Boon — начало работы с репозиторием Boon

Использование Boon DataRepo на примере

Репозиторий Boon позволяет быстро запрашивать сложные деревья объектов Java, которые хранятся в коллекциях Java.

Давайте начнем с нескольких простых примеров.

Сначала приведем импорт, чтобы показать, откуда берутся примеры классов и служебные классы:

//Sample Test model
import com.examples.model.test.Email;
import com.examples.security.model.User;
import static com.examples.security.model.User.user;




//Data repo classes
import org.boon.datarepo.DataRepoException;
import org.boon.datarepo.Repo;
import org.boon.datarepo.RepoBuilder;
import org.boon.datarepo.Repos;
//Boon utility methods
import org.boon.Lists;
import org.boon.core.Typ;
import static org.boon.Boon.putl;
import static org.boon.Boon.puts;
import static org.boon.Exceptions.die;
import static org.boon.Lists.idx;
import static org.boon.criteria.CriteriaFactory.eq;
import static org.boon.criteria.CriteriaFactory.eqNested;
import static org.boon.criteria.CriteriaFactory.notEq;

Мы создадим хранилище пользовательских объектов, а затем выполним некоторые базовые запросы к ним.

Некоторое домашнее хозяйство. Давайте создадим константу для  электронной почты,  чтобы мы не использовали ее снова и снова.

private static String EMAIL = "email";

Давайте идти.

public static void main ( String... args ) {
boolean test = true;

Затем создайте конструктор репо, который создает  репо пользователяПользователь  — это образец объекта домена, и у него есть одно свойство, называемое  электронной почтой .

Репо  является основной концепцией в Boon репо данных. Думайте о  репо  как о  коллекции,  которая разрешает запросы. Запросы могут быть очень быстрыми, поскольку они используют  поисковые индексы  (TreeMap) и  поисковые индексы  (HashMap).

 
RepoBuilder repoBuilder = Repos.builder ();
repoBuilder.primaryKey ( EMAIL );

Выше создается репо, первичным ключом которого является свойство email.

Затем мы создаем  репо  с ключом типа  String.class  и классом элементов  User.class .

final Repo< String, User > userRepo = repoBuilder.build ( Typ.string, user );

Выше создается  userRepo  с использованием компоновщика. Обратите внимание, что  Typ.string  просто равен  String.class .

Теперь давайте добавим некоторых пользователей в наш репозиторий, чтобы мы могли проверить его.

 final List<User> users = Lists.list (
user ( "rick.hightower@foo.com" ),
user ( "bob.jones@foo.com" ),
user ( "sam.jones@google.com" )
);

Метод  user  является статическим методом из класса  User . Я покажу вам это через секунду. List.list  — это вспомогательный метод, который создает  java.util.List  (более подробную информацию о Boon см. В вики, ссылки внизу блога).

UserRepo  позволяет добавить список пользователей к нему с  addAll  следующим способом:

 userRepo.addAll ( users );

Теперь, когда у нас есть репо с пользователями, давайте запустим несколько запросов к нему.

Вот запрос с использованием   критериев eq .

 List<User> results =
userRepo.query ( eq ( EMAIL, "rick.hightower@foo.com") );

Метод  userRepo.query  возвращает  java.util.List  пользователей.

Давайте распечатаем всех пользователей.

 putl ( "Simple Query using Equals Results", results );

Бун имеет два коммунальных методу  ставит и  putl . Метод  put  похож на метод Ruby. Метод  putl  выводит строки на элемент.

Выход :

 [User{email='rick.hightower@foo.com'}]

Теперь давайте удостоверимся, что мы получили то, что ожидали.



/** Same as results.get(0) */
User rick = idx (results, 0);




/* Make sure we got what we wanted. */
test |= Objects.equals (rick.getEmail (), "rick.hightower@foo.com") ||
die( "Rick's email not equal to 'rick.hightower@foo.com' " );

Метод  idx  является основной концепцией в Boon, но не в репозитории Boon. Метод  idx  индексирует вещи. В вики  Boon довольно много вики-страниц с  обозначениями срезов  и  индексацией .

Теперь давайте попробуем несколько разных запросов к нашей коллекции репо, давайте попробуем   критерии notEq :



results =
userRepo.query ( notEq( EMAIL, "rick.hightower@foo.com" ) );




В репозитории Boon есть много операторов критериев, вот неполный список:

static Group and(Criteria... expressions) 
static Criterion between(java.lang.Class clazz, java.lang.Object name, java.lang.String svalue, java.lang.String svalue2) 
static Criterion between(java.lang.Object name, java.lang.Object value, java.lang.Object value2) 
static Criterion between(java.lang.Object name, java.lang.String svalue, java.lang.String svalue2) 
static Criterion contains(java.lang.Object name, java.lang.Object value) 
static Criterion empty(java.lang.Object name) 
static Criterion endsWith(java.lang.Object name, java.lang.Object value) 
static Criterion eq(java.lang.Object name, java.lang.Object value) 
static Criterion eqNested(java.lang.Object value, java.lang.Object... path) 
static Criterion gt(java.lang.Object name, java.lang.Object value) 
static Criterion gt(java.lang.Object name, java.lang.String svalue) 
static Criterion gte(java.lang.Object name, java.lang.Object value) 
static Criterion implementsInterface(java.lang.Class<?> cls) 
static Criterion in(java.lang.Object name, java.lang.Object... values) 
static Criterion instanceOf(java.lang.Class<?> cls) 
static Criterion isNull(java.lang.Object name) 
static Criterion lt(java.lang.Object name, java.lang.Object value) 
static Criterion lte(java.lang.Object name, java.lang.Object value) 
static Not not(Criteria expression) 
static Criterion notContains(java.lang.Object name, java.lang.Object value) 
static Criterion notEmpty(java.lang.Object name) 
static Criterion notEq(java.lang.Object name, java.lang.Object value) 
static Criterion notIn(java.lang.Object name, java.lang.Object... values) 
static Criterion notNull(java.lang.Object name) 
static Group or(Criteria... expressions) 
static Criterion startsWith(java.lang.Object name, java.lang.Object value) 
static Criterion typeOf(java.lang.String className) 

Мы не будем подробно описывать их все, но вы можете догадаться, что они делают по имени.

Давайте продолжим наш   пример notEq .

Теперь мы должны убедиться, что у нас есть пользователи, которые не являются Риком. Никто не хочет Рика. 🙁

putl ( "Simple Query using Not Equals Results", results );




/** Same as results.get(0) */
User notRick = idx (results, 0);




putl ( notRick );




/* Make sure we got what we wanted, i.e. no Rick! */
test |= !Objects.equals (notRick.getEmail (), "rick.hightower@foo.com") ||
die( "User Not Rick's email should NOT be equal " +
"to 'rick.hightower@foo.com' " );

Выше показано, что Рик не находится в результатах запроса не Рик. 🙂

выход

 [User{email='bob.jones@foo.com'}, User{email='sam.jones@google.com'}]




User{email='bob.jones@foo.com'}

Иногда вы знаете, что вам нужен только один результат запроса, а иногда вам нужен только один период результатов. Для этого вы используете метод результатов, который возвращает ResultSet следующим образом:

rick = userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) ).firstItem ();

Обратите внимание, что мы используем  ** userRepo.results (…). FirstItem () **, чтобы получить один элемент вместо списка элементов.

Что, если мы ожидаем только один элемент и только один элемент, тогда мы будем использовать  результаты (…). Ожидаем, что (). FirstItem ()  следующим образом:

 rick = (User) //expectOne is not generic
userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
.expectOne ().firstItem ();

Обратите внимание, что мы должны использовать приведение. Вы можете использовать  результаты  с  выборками , что означает, что вы можете выбрать любую часть объекта, чтобы  firstItem не всегда возвращал пользователя. Если вы не хотите разыгрывать, вы можете использовать следующую форму.

 rick = userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
.expectOne (user).firstItem ();

Заметьте, что мы передаем пользователя в  ожидаемую сторону  ( пользователь  имеет тип Class и установлен в  User.class ).

Вы могли бы написать выше, как это вместо этого:

 rick = userRepo.results ( eq ( EMAIL, "rick.hightower@foo.com" ) )
.expectOne (User.class).firstItem ();

Я предпочитаю иметь типы, которые проще для глаз, так как они немного привыкли к поддержке репозитория Boon.

Если вы ожидаете один и только один, то вы можете использовать   метод waitOne, и если имеется более одного элемента или ноль элементов, он выдаст исключение, показанное следующим образом:

 /** Example 6: Expect only one item with expectOne(user).firstItem() and we have many. */




try {
putl ( "Example 6: Failure case, we have more than one for",
"query using ResultSet.expectOne(user).firstItem");




rick = userRepo.results ( notEq ( EMAIL, "rick.hightower@foo.com" ) )
.expectOne (user).firstItem ();




die("We should never get here!");




} catch (DataRepoException ex) {
puts ("success for Example 6");
}

Кстати, вы видели использование этого   метода die (…) . Это в основном просто создает исключение во время выполнения. Это вспомогательный метод. Бун получил   концепцию штампа от Perl ( http://perldoc.perl.org/functions/die.html ). Бун старается брать хорошие идеи из других языков и заставлять их создавать продуктивные наборы API.

Бун получает индексацию и нарезку из Python, ставит из Ruby и умирает из Perl. Это еще не все. Старшего брата Буна звали EasyJava (позже названный Facile), и EasyJava / Facile позаимствовали еще много идей из Perl, Python и Ruby. Но EasyJava / Facile были более экспериментальными, в то время как Boon лучше спроектирован и предназначен для курирования и использования в проектах. Благо не нова сама по себе. Boon — это эволюция DataRepo, EasyJava / Facile и Crank. Когда Бун вырастет, это будет очень способная библиотека. Но я отвлекся ….

Давайте посмотрим на наш   объект User, чтобы показать, что это просто старый Java-объект:

Класс пользователя, например

package com.examples.security.model;




public class User {




public static final Class<User> user = User.class;




public static User user (String email) {
return new User( email );
}
private final String email;




...




public String getEmail () {
return email;
}

Пользователь довольно простой POJO.

Репозиторий Boon  может работать со сложными объектными иерархиями и отношениями и может устанавливать поисковые индексы (TreeMap), поисковые индексы (HashMap), неуникальные индексы (TreeMap с несколькими значениями) и неуникальные поисковые индексы, но это для другого время.

Просто чтобы дать вам представление о том, что   может сделать репоРепо  является  ObjectEditor  и  SearchableCollection ,  который может производить ResultSets  поэтому он имеет следующий метод:

void updateByFilter(java.util.List<Update> values, Criteria... expressions) 
void updateByFilter(java.lang.String property, byte value, Criteria... expressions) 
void updateByFilter(java.lang.String property, char value, Criteria... expressions) 
void updateByFilter(java.lang.String property, double value, Criteria... expressions) 
void updateByFilter(java.lang.String property, float value, Criteria... expressions) 
void updateByFilter(java.lang.String property, int value, Criteria... expressions) 
void updateByFilter(java.lang.String property, long value, Criteria... expressions) 
void updateByFilter(java.lang.String property, java.lang.Object value, Criteria... expressions) 
void updateByFilter(java.lang.String property, short value, Criteria... expressions) 
void updateByFilterUsingValue(java.lang.String property, java.lang.String value, Criteria... expressions) 




void addAll(ITEM... items) 
void addAll(java.util.List<ITEM> items) 
void addAllAsync(java.util.Collection<ITEM> items) 
boolean compareAndIncrement(KEY key, java.lang.String property, byte compare) 
boolean compareAndIncrement(KEY key, java.lang.String property, int compare) 
boolean compareAndIncrement(KEY key, java.lang.String property, long compare) 
boolean compareAndIncrement(KEY key, java.lang.String property, short compare) 
boolean compareAndUpdate(KEY key, java.lang.String property, byte compare, byte value) 
boolean compareAndUpdate(KEY key, java.lang.String property, char compare, char value) 
boolean compareAndUpdate(KEY key, java.lang.String property, double compare, double value) 
boolean compareAndUpdate(KEY key, java.lang.String property, float compare, float value)
boolean compareAndUpdate(KEY key, java.lang.String property, int compare, int value) 
boolean compareAndUpdate(KEY key, java.lang.String property, long compare, long value) 
boolean compareAndUpdate(KEY key, java.lang.String property, java.lang.Object compare, java.lang.Object value) 
boolean compareAndUpdate(KEY key, java.lang.String property, short compare, short value)
ITEM get(KEY key) 
byte getByte(ITEM item, java.lang.String property) 
char getChar(ITEM item, java.lang.String property) 
double getDouble(ITEM item, java.lang.String property) 
float getFloat(ITEM item, java.lang.String property) 
int getInt(ITEM item, java.lang.String property) 
KEY getKey(ITEM item) 
long getLong(ITEM item, java.lang.String property) 
java.lang.Object getObject(ITEM item, java.lang.String property) 
short getShort(ITEM item, java.lang.String property) 
<T> T getValue(ITEM item, java.lang.String property, java.lang.Class<T> type) 
void modify(ITEM item) 
void modify(ITEM item, java.lang.String property, byte value) 
void modify(ITEM item, java.lang.String property, char value) 
void modify(ITEM item, java.lang.String property, double value) 
void modify(ITEM item, java.lang.String property, float value) 
void modify(ITEM item, java.lang.String property, int value) 
void modify(ITEM item, java.lang.String property, long value) 
void modify(ITEM item, java.lang.String property, java.lang.Object value) 
void modify(ITEM item, java.lang.String property, short value) 
void modify(ITEM item, Update... values) 
void modifyAll(java.util.Collection<ITEM> items) 
void modifyAll(ITEM... items) 
void modifyByValue(ITEM item, java.lang.String property, java.lang.String value) 
void put(ITEM item) 
byte readByte(KEY key, java.lang.String property) 
char readChar(KEY key, java.lang.String property) 
double readDouble(KEY key, java.lang.String property) 
float readFloat(KEY key, java.lang.String property) 
int readInt(KEY key, java.lang.String property) 
long readLong(KEY key, java.lang.String property) 
byte readNestedByte(KEY key, java.lang.String... properties) 
char readNestedChar(KEY key, java.lang.String... properties) 
double readNestedDouble(KEY key, java.lang.String... properties) 
float readNestedFloat(KEY key, java.lang.String... properties) 
int readNestedInt(KEY key, java.lang.String... properties) 
long readNestedLong(KEY key, java.lang.String... properties) 
short readNestedShort(KEY key, java.lang.String... properties) 
java.lang.Object readNestedValue(KEY key, java.lang.String... properties) 
java.lang.Object readObject(KEY key, java.lang.String property) 
short readShort(KEY key, java.lang.String property) 
<T> T readValue(KEY key, java.lang.String property, java.lang.Class<T> type)
void removeAll(ITEM... items) 
void removeAllAsync(java.util.Collection<ITEM> items) 
void removeByKey(KEY key) 
void update(KEY key, java.lang.String property, byte value) 
void update(KEY key, java.lang.String property, char value) 
void update(KEY key, java.lang.String property, double value) 
void update(KEY key, java.lang.String property, float value) 
void update(KEY key, java.lang.String property, int value) 
void update(KEY key, java.lang.String property, long value) 
void update(KEY key, java.lang.String property, java.lang.Object value) 
void update(KEY key, java.lang.String property, short value) 
void update(KEY key, Update... values) 
void updateByValue(KEY key, java.lang.String property, java.lang.String value)
void addLookupIndex(java.lang.String name, LookupIndex<?,?> si) 
void addSearchIndex(java.lang.String name, SearchIndex<?,?> si) 
java.util.List<ITEM> all() 
int count(KEY key, java.lang.String property, byte value) 
int count(KEY key, java.lang.String property, char value) 
int count(KEY key, java.lang.String property, double value) 
int count(KEY key, java.lang.String property, float value) 
int count(KEY key, java.lang.String property, int value) 
int count(KEY key, java.lang.String property, long value) 
int count(KEY key, java.lang.String property, java.lang.Object value) 
int count(KEY key, java.lang.String property, short value) 
boolean delete(ITEM item) 
ITEM get(KEY key) 
KEY getKey(ITEM item) 
void invalidateIndex(java.lang.String property, ITEM item) 
<T> T max(KEY key, java.lang.String property, java.lang.Class<T> type) 
double maxDouble(KEY key, java.lang.String property) 
int maxInt(KEY key, java.lang.String property) 
long maxLong(KEY key, java.lang.String property) 
java.lang.Number maxNumber(KEY key, java.lang.String property) 
java.lang.String maxString(KEY key, java.lang.String property) 
<T> T min(KEY key, java.lang.String property, java.lang.Class<T> type) 
double minDouble(KEY key, java.lang.String property) 
int minInt(KEY key, java.lang.String property) 
long minLong(KEY key, java.lang.String property) 
java.lang.Number minNumber(KEY key, java.lang.String property) 
java.lang.String minString(KEY key, java.lang.String property) 
java.util.List<ITEM> query(Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> query(java.util.List<Selector> selectors, Criteria... expressions) 
void query(Visitor<KEY,ITEM> visitor, Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> queryAsMaps(Criteria... expressions) 
void removeByKey(KEY key) 
ResultSet<ITEM> results(Criteria... expressions) 
java.util.List<ITEM> sortedQuery(Sort sortBy, Criteria... expressions) 
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> sortedQuery(Sort sortBy, java.util.List<Selector> selectors, Criteria... expressions) 
java.util.List<ITEM> sortedQuery(java.lang.String sortBy, Criteria... expressions)
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> sortedQuery(java.lang.String sortBy, java.util.List<Selector> selectors, Criteria... expressions) 
void sortedQuery(Visitor<KEY,ITEM> visitor, Sort sortBy, Criteria... expressions) 
void sortedQuery(Visitor<KEY,ITEM> visitor, java.lang.String sortBy, Criteria... expressions) 
void validateIndex(java.lang.String property, ITEM item) 

Вот что ResultSet производит из результатов:

package org.boon.datarepo;




public interface ResultSet<T> extends Iterable<T> {




ResultSet expectOne();
<EXPECT> ResultSet <EXPECT> expectOne(Class<EXPECT> clz);
ResultSet expectMany();




ResultSet expectNone();




ResultSet expectOneOrMany();




ResultSet removeDuplication();




ResultSet sort(Sort sort);




Collection<T> filter(Criteria criteria);




ResultSet<List<Map<String, Object>>> select(Selector... selectors);




int[] selectInts(Selector selector);




float[] selectFloats(Selector selector);




short[] selectShorts(Selector selector);




double[] selectDoubles(Selector selector);




byte[] selectBytes(Selector selector);




char[] selectChars(Selector selector);




Object[] selectObjects(Selector selector);




<OBJ> OBJ[] selectObjects(Class<OBJ> cls, Selector selector);




<OBJ> ResultSet<OBJ> selectObjectsAsResultSet(Class<OBJ> cls, Selector selector);
Collection<T> asCollection();




String asJSONString();




List<Map<String, Object>> asListOfMaps();




List<T> asList();




Set<T> asSet();




List<PlanStep> queryPlan();




T firstItem();




Map<String, Object> firstMap();




String firstJSON();




int firstInt(Selector selector);




float firstFloat(Selector selector);




short firstShort(Selector selector);




double firstDouble(Selector selector);




byte firstByte(Selector selector);




char firstChar(Selector selector);




Object firstObject(Selector selector);




<OBJ> OBJ firstObject(Class<OBJ> cls, Selector selector);
List<T> paginate(int start, int size);




List<Map<String, Object>> paginateMaps(int start, int size);




String paginateJSON(int start, int size);




//Size can vary if you allow duplication.
//The size can change after removeDuplication.
int size();
}

Также  ObjectEditor  — это  сумка,  а  SearchableColleciton  — это  java.util.Collection,  поэтому  Repo  — это   тоже сумка  и  коллекция . Смысл в том, чтобы сказать, что вы можете написать книгу о том, что   делает Repo . Он не делает все это (на самом деле это просто интерфейс), но он очень много делает с защитой и делегированием (объекты, которые можно создавать), которые образуют  репоRepo  это просто коллекция , которая позволяет делать запросы через индексы (HashMap, TreeMap).

По умолчанию Boon работает с  полями  в классе, но вы можете указать ему использовать только  свойства  (методы получения и установки).

Давайте посмотрим, как хранилище данных Boon обрабатывает составные объекты и доступ к свойствам следующим образом, но сначала давайте определим еще несколько классов:

Класс электронной почты *

package com.examples.model.test;




public class Email {
private String content;




public String getEmail () {
return content;
}




public void setEmail ( String content ) {
this.content = content;
}




public Email ( String content ) {
this.content = content;
}
public Email ( ) {
}




@Override
public boolean equals ( Object o ) {
if ( this == o ) return true;
if ( !( o instanceof Email ) ) return false;




Email email = ( Email ) o;




if ( content != null ? !content.equals ( email.content ) : email.content != null ) return false;




return true;
}




@Override
public int hashCode () {
return content != null ? content.hashCode () : 0;
}
}

Новый объект User имеет не простое поле электронной почты, а сложное поле электронной почты (Email).

Класс UserEmail, который использует электронную почту вместо простой строки

package com.examples.model.test;
public class UserEmail {




private Email email;




public UserEmail ( String email ) {




this.email = new Email ( email ) ;




}




public Email getEmail () {
return email;
}
}

Давайте снова настроим  репо  , но на этот раз мы будем использовать только  свойства, а  не  поля :

 boolean ok = true;




RepoBuilder repoBuilder = Repos.builder ();




repoBuilder.usePropertyForAccess ( true );
putl ("The primary key is set to email");




repoBuilder.primaryKey ( "email" );
putl ("For ease of use you can setup nested properties ",
"UserEmail.email property is a Email object not a string",
"Email.email is a string.");




//You can index component objects if you want
repoBuilder.nestedIndex ( "email", "email" );

Read the above comments and putl calls, and puts method calls for more instruction.

Next we define our userRepo. Notice that I opted to use the Email.class, and UserEmail.class to show you exactly what we are passing. Email.class is the primary key class, and UserEmail.class is the item type.

/** Create a repo of type String.class and User.class */
final Repo<Email, UserEmail> userRepo = repoBuilder.build (
Email.class, UserEmail.class );

Let’s add some test data using the repo.add method.

puts("Adding three test objects for bob, sam and joe ");
userRepo.add ( new UserEmail ( "bob@bob.com" ) );
userRepo.add ( new UserEmail ( "sam@bob.com" ) );
userRepo.add ( new UserEmail ( "joe@bob.com" ) );

Now we can query userRepo and look for Bob’s email:

 UserEmail bob = (UserEmail) userRepo.results (
eqNested ( "bob@bob.com", "email", "email" ) )
.expectOne ().firstItem ();

Notice that we are using an eqNested operator. So in effect we are looking for the property path of user.email.email. Since the root object of userRepo are users (UserEmail to be precise) then we are looking for root.email.email. This is because UserEmail has a property of type Email called email, and Email has a property called email.

We can test we got the right email address:

 ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();

The above is boon speak for if Bob’s email’s property called email is not equal to «bob@bob.com«, then die (which just means throw a runtime exception).

We can avoid the cast as follows (read putl and comments):

putl("Avoid the cast with using nested query Repo.eqNested(UserEmail.class)");




bob = userRepo.results ( eqNested ( "bob@bob.com", "email", "email" ) )
.expectOne (UserEmail.class).firstItem ();
ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();

You are not stuck with strings, you can query primitives and any complex object that implements an equals method and a hashCodemethod as follows:

Email email = new Email ( "bob@bob.com" );
bob = (UserEmail) userRepo.results ( eq ( EMAIL, email ) )
.expectOne ().firstItem ();




ok |= bob.getEmail ().getEmail ().equals ( "bob@bob.com" ) || die();




puts("success=", ok);

Notice you can query with email (complex object) direct and remember that the email property is not a string but a class called Email that has an equals and a hashCode method.

That is all for part 1 as far as examples go (it goes on and on).

There is a lot more to dataRepo than meets the eye. It can do many things. This was just an introductory article to whet your appetite, and hopefully, you want to learn more.

Here are some of the classes in Boon’s data repo package.

Interfaces

Bag //Like a java.util.List
CollectionDecorator //Decorates built-in collecitons to add indexes and searches
Filter //The filter interface
LookupIndex //LookupIndex implemented with HashMaps or equiv
ObjectEditor //Abilty to edit fields and peform Select Queries against collecitons
Repo //Like a data repo but uses collections api to sync one or more indexes
RepoBuilder //Allows you to build a Repo, setup indexes, etc.
ResultSet //The results from a query.
SearchableCollection //A collection that is searchable.

Classes

Collections //converts regular collections to SearchableCollections with indexes to/for
Repos //Helper class for creating RepBuilders




DataRepoException //Exception class for DataRepo

There is also a Criteria API that works with the DataRepo queries. DataRepo queries start by using the index, and then when an item is not in the index or if the index query did not fully satisfy the criteria, DataRepo can then use the criteria API to perform the rest in a linear search:

Criteria //Criteria
CriteriaFactory //Criteria Factory, we say this earlier, eq, notEq, etc.
Criterion //Concrete classes
Criterion.PrimitiveCriterion //Primitive criteria to avoid wrappers and lots of temp objects
Group //Group of Criteria, Group is also a Criteria so it is nested
Group.And //And criteria together
Group.Or //Or Criteria
Not //Inverse the logic
ProjectedSelector //Perform projections over a Criteria
QueryFactory //Create queries
Selector //Selectors like SQL select
Sort //Define how you want the data Sorted
Update //Ability to select and update objects based on criteria

Enums

Grouping
Operator
SortType

To learn more see the JavaDocs.

http://richardhightower.github.io/site/javadocs/index.html

Let me know what you think, and if you have issues, please file a bug.

If you want to get a sneak peek at what is coming in Boon see https://github.com/RichardHightower/datarepo.

There is a lot more to Boon’s data repo then we can cover in one article.

Further Reading:

If you are new to boon start here:

Why Boon

Easily read in files into lines or a giant string with one method call. Boon has Slice notation for dealing with Strings, Lists, primitive arrays, etc. If you are from Groovy land, Ruby land, Python land, or whatever land, and you have to use Java then Boon might give you some relief. If you are like me, and you like to use Java, then Boon is for you too.

Core Boon Philosophy

Core Boon will never have any dependencies. It will always be able to run as a single jar.