Skip to content

Files

Latest commit

b0c864b · Oct 24, 2015

History

History
67 lines (59 loc) · 5.19 KB

03-CollectionsQL.md

File metadata and controls

67 lines (59 loc) · 5.19 KB

CollectionsQL

Нужно написать библиотеку, которая бы позволяла делать SQL-подобные запросы по коллекциям.

Синтаксис:

select_statement := 
from(<collection>)
.select[Distinct](<result_class>, <functions as constructor args>...)
[.where(<predicate>)]
[.groupBy(<function>, ...)]
[.having(<predicate>)]
[.orderBy(<comparator>, ...)]
[.limit(<lines>)]
[.union().<select_statement>]

Пример запроса:

Iterable<Statistics> statistics =
                from(list(
                        student("ivanov", LocalDate.parse("1986-08-06"), "494"),
                        student("sidorov", LocalDate.parse("1986-08-06"), "495"),
                        student("smith", LocalDate.parse("1986-08-06"), "495"),
                        student("petrov", LocalDate.parse("2006-08-06"), "494")))
                        .select(Statistics.class, Student::getGroup, count(Student::getGroup), avg(Student::age))
                        .where(rlike(Student::getName, ".*ov").and(s -> s.age() > 20))
                        .groupBy(Student::getName)
                        .having(s -> s.getCount() > 0)
                        .orderBy(asc(Student::getGroup), desc(count(Student::getGroup)))
                        .limit(100)
                        .union()
                        .from(list(student("ivanov", LocalDate.parse("1985-08-06"), "494")))
                        .selectDistinct(Statistics.class, "all", count(s -> 1), avg(Student::age))
                        .execute();
                        
statistics: [Statistics{group=all,count=3,27},Statistics{group=494,count=2,avg=24},Statistics{group=495,count=1,avg=29}]                        

Нужно реализовать всю функциональность из классов Aggregates, Conditions, OrderByConditions, Sources.

Семантика операций как в SQL:

  • select(, , ...) - для каждой строчки результирующего списка создаёт объект класса, передавая в конструктов результаты вычисления выражений в качестве аргументов
  • selectDistinct(, , ...) - см. select. Оставляет только уникальные строки. Для результирующего класса должен быть реализован адекватный equals() и hashCode().
  • from() - указывает, из какого источника брать данные
  • groupBy(, ...) - группирует результат по заданным выражениям. Как и в SQL, при наличии group by, в select могут быть только выражения, по которым группировали или агрегатные функции.
  • having(, ...) - фильтрует результат группировки, то есть предикат применяется к объекту из select
  • orderBy(, ...) - упорядочивает результат согласно компаратору
  • limit() - оставить только результатов запроса
  • union() - объединяет результаты двух запросов в один. Запросы должны иметь одинаковый тип результата.

Aгрегатные функции (доп.задание со звёздочкой):

  • min() - минимальное значение выражения для элементов списка

  • max() - максимальное значение выражения для элементов списка

  • count() - количество элементов из списка, для которых выражение принимает значение отличное от null

  • avg() - среднее значение выражения для элементов списка

  • Запрос запускается методом execute().

  • Все запросы должны работать максимально эффективно по времени и памяти (асимптотически).

  • Код нужно покрыть модульными тестами.

  • Покрытие тестами должно быть не менее 70% строк кода, лучше более.

  • Тесты должны покрывать все требования + возможные исключения. То есть, если у вас какой-нибудь метод при каком-то понятном условии может выкинуть Exception - то этот случай должен быть покрыт тестом. Тесты можно запускать командой mvn test или из IDE, так же как и main().

Рекомендации:

  1. Смотрите примеры:
  2. Обращайтесь к исходникам Stream API и Hamcrest Matchers.