Статьи

Начало работы с CQEngine: LINQ для Java, только быстрее

CQEngine или механизм запросов коллекций  — это библиотека, которая позволяет создавать индексы для коллекций Java и запрашивать их для объектов с использованием открытых свойств. Он предлагает аналогичную возможность LINQ  в .net, но считается, что он быстрее, потому что он строит индексы по коллекциям, прежде чем запрашивать их, и использует теорию множеств вместо итераций.

В этом посте мы увидим, как запросить простую коллекцию объектов, в нашем примере — коллекцию пользователей гипотетической системы, используя CQEngine. В следующем посте мы также увидим, как итеративный поиск в коллекции сравнивается с запросом через CQEngine.

Первый шаг — получить файл JAR CQEengine. Загрузите jar с веб-сайта CQEngine или, если вы используете Maven, добавьте следующую зависимость.

	<dependency>
	      <groupId>com.googlecode.cqengine</groupId>
	      <artifactId>cqengine</artifactId>
	      <version>1.0.3</version>
	</dependency>

Далее, давайте создадим класс, чей объект мы будем искать:

package co.syntx.examples.cqengine;

import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.attribute.SimpleAttribute;

public class User {

	private String username;
	private String password;
	private String fullname;
        private Role role;

	public User(String username, String password, String fullname, Role role) {
		super();
		this.username = username;
		this.password = password;
		this.fullname = fullname;
		this.role = role;
	}

	 public static final Attribute<User, String> FULL_NAME = new SimpleAttribute<User, String>("fullname") {
	        public String getValue(User user) { return user.fullname; }
	 };

	 public static final Attribute<User, String> USERNAME = new SimpleAttribute<User, String>("username") {
	        public String getValue(User user) { return user.username; }
	 };

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getFullname() {
		return fullname;
	}

	public void setFullname(String fullname) {
		this.fullname = fullname;
	}

	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}
}

Далее мы пишем класс, чтобы выполнить наши поиски. Я пойду функция за функцией.

1. Функция для создания тестовой индексированной коллекции.
В следующей функции мы создаем индексированную коллекцию, определяем индексы для атрибутов и заполняем эту коллекцию определенным количеством объектов. При фактическом использовании ваша коллекция, вероятно, будет заполнена объектами, возвращаемыми из БД, считанными из файла или другими подобными сценариями.

public void buildIndexedCollection(int size) throws Exception {
	indexedUsers = CQEngine.newInstance();
	indexedUsers.addIndex(HashIndex.onAttribute(User.FULL_NAME));
	indexedUsers.addIndex(SuffixTreeIndex.onAttribute(User.FULL_NAME));

	for (int i = 0; i < size; i++) {
	    String username = RandomStringGenerator.generateRandomString(8,RandomStringGenerator.Mode.ALPHANUMERIC);
	    String password = RandomStringGenerator.generateRandomString(8,RandomStringGenerator.Mode.ALPHANUMERIC);
	    String fullname = RandomStringGenerator.generateRandomString(5,RandomStringGenerator.Mode.ALPHA)
			+ " " + RandomStringGenerator.generateRandomString(5,RandomStringGenerator.Mode.ALPHA);
	    Role role = new Role();
	    role.setName("admin");
	    indexedUsers.add(new User(username, password, fullname, role));
	}
}

  • В строке 3 мы инициализируем новую индексированную коллекцию, ссылка на которую хранится в переменной класса indexedUsers . Ссылка имеет тип  IndexedCollection <Пользователь>
  • В строках  и мы определяем два индекса: i) хэш-индекс, подходящий для  запросов одинакового  стиля. ii) индекс суффикса, подходящий для  концов с  запросом стиля. В этом примере мы строим индексы только в поле «Полное имя».
  • В строке мы используем генератор случайных строк  для заполнения фиктивных объектов.
  • В строке  14  мы добавляем наш объект в нашу проиндексированную коллекцию.

2. Функция для выполнения индексированного поиска точных совпадений:
в этой функции мы запрашиваем имена, которые точно соответствуют данному имени. Функция равенства  принимает атрибут для выполнения запроса и значение для поиска. Метод  равно  статически импортируется через

импортировать статический com.googlecode.cqengine.query.QueryFactory. *;

В приведенном ниже примере мы зацикливаем результаты, возвращаемые методом retrieve, и ничего не делаем с ним. В вашем случае вы можете вернуть итератор, возвращаемый методом retrieve .

public void indexedSearchForEquals(String fullname) throws Exception {
       Query query = equal(User.FULL_NAME, fullname);
	   for (User user : indexedUsers.retrieve(query)) {
		// System.out.println(user.getFullname());
	   }
}

3. Функция для выполнения индексированного поиска концов с совпадениями:
в этой функции мы запрашиваем имена, заканчивающиеся определенным суффиксом.

public void indexedSearchForEndsWith(String endswith) throws Exception {
       Query query1 = endsWith(User.FULL_NAME, endswith);
	   for (User user : indexedUsers.retrieve(query1)) {
		// System.out.println(user.getFullname());
	   } 
}

4. Функция для выполнения индексированного поиска для совпадений или концов с совпадениями:
эта функция является комбинацией обоих запросов, упомянутых ниже, и имеет отношение или между ними.

public void indexedSearchForEqualOrEndsWith(String equals, String ends) throws Exception {
       Query query = or(equal(User.FULL_NAME, equals),endsWith(User.FULL_NAME, ends));
	   for (User user : indexedUsers.retrieve(query)) {
                 // System.out.println(user.getFullname());
	   }
}

5. Собираем все вместе:
все функции выше принадлежат классу с именем CQEngineTest . Мы создаем новый объект, создаем тестовую коллекцию, затем ищем либо точные совпадения, строки, которые заканчиваются определенным суффиксом, либо оба.

      CQEngineTest test = new CQEngineTest();
      test.buildIndexedCollection(size);
      test.indexedSearchForEqualOrEndsWith("test", "test");

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

В следующем посте   мы также увидим, как индексированный поиск сравнивается с типичным итеративным поиском с точки зрения времени.