【CQEngine】Java7でもStreamとか.NetのLINQみたいにコレクションを扱いたい

# 背景
* レガシー環境では、ミドルウェアの関係でJava8が使えずStreamが利用できない
* コレクションを整形するときはfor文でぶん回している。

そこでJava7でも、StreamやLinQのようにスマートにコレクション操作できるものを探しました。
(ほかにfor文のぶん回し回避方法をご存じでしたら教えてください)

# CQEngineについて
[Awesome Java : 素晴しい Java フレームワーク・ライブラリ・ソフトウェアの数々](https://qiita.com/hatai/items/7868996e3eb5fa55f11a)
上記記事で紹介されていました。

## CQEngine
公式リポジトリ　[Github : CQengin](https://github.com/npgall/cqengine)

* CQEngineはモデルに対し、Indexを設定することで超高速なCollection操作を提供するライブラリのようです
* 今回はStreamの代わりとしてJava7で使えるかを検証していきます
* 主題とずれるためIndexについては検証しません

## License
「CQEngine」のライセンスは「Apache License 2.0」です。

## CQEngineでのコレクション操作の記述方法
### Queryの利用
* メリット
   * タイプセーフに書ける
   * for文でぶん回すより可読性が高い
   * Indexを設定することによりJava8のStreamより早くなるみたいです ※ [[question] Performance difference in Query vs standard collection lambda](https://github.com/npgall/cqengine/issues/180)
* デメリット
   * POJOそのままには使えない
   * Attributeの設定方法の学習コストがかかりそう
   * CQEngine用の記述方法が必要

``` Java
// already defined Attribute in Car Class
// search brand-new AAACar
uery<Car> query1 =	and(
						endsWith(Car.NAME, "AAACar"),
						equal(Car.DISCREPRION, "brandNew")
					);
ResultSet<car> resultSet = cars.retrieve(query1));


```

### CQN dialectsの利用
* メリット
    * POJOに適用できる
    * for文でぶん回すより可読性が高い・・・？
* デメリット
    * タイプセーフでない
    * CQEngine用の記述方法が必要 
    * 
 
``` Java
import static com.googlecode.cqengine.codegen.AttributeBytecodeGenerator.*;

// generate Attribute
CQNParser<Car> parser = CQNParser.forPojoWithAttributes(Car.class, createAttributes(Car.class));
// search brand-new AAACar
ResultSet<Car> results = parser.retrieve(cars,
								"and("
									+ "equal(\"name\",\"AAACar\"),"
									+ "equal(\"discription\", \"brandNew\")"
								+ " )");
```

### SQL dialectsの利用
* メリット
    * POJOに適用できる
    * SQLがわかる人であれば使える(ちょっと異なるルールもありそう)
* デメリット
    * タイプセーフでない

``` Java
// generate Attribute
SQLParser<Car> parser = SQLParser.forPojoWithAttributes(Car.class, createAttributes(Car.class));

// search brand-new AAACar
ResultSet<Car> results = parser.retrieve(cars,
								"SELECT * FROM cars " +
								"WHERE (" +
									" name = 'AAACar' " +
									" AND discription = 'brandNew')"
								);

for (Car car : results ) {
	System.out.println(car.toString());
}
```

# 検証
##  Maven
~~現時点での最新版を入れています~~
Java7で使える「2.12.4」を入れます

``` xml
<dependency>
  <groupId>com.googlecode.cqengine</groupId>
  <artifactId>cqengine</artifactId>
  <version>2.12.4</version>
</dependency>
```
リリースノートを見ると、Ver3からJava7以下は対象外となったみたいです。
[CQEngine Release Notes](https://github.com/npgall/cqengine/blob/master/documentation/ReleaseNotes.md)



## 比較用のデータの準備
### コレクション用のモデルの用意
Carを作りました。※ @ Data はlombokを使っているため記述しています

``` Java
@Data
public class Car {
	private int carId;
	private String name;
	private String discription;
	private List<String> features;
}
```

## 比較
### Java7のfor文ぶん回し

``` Java
// create car collection
Set<Car> cars = new HashSet<>();
cars.add( Car.builder().carId(0).name("AAACar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );
cars.add( Car.builder().carId(1).name("BBBCar").discription("used").features( Arrays.asList("spare tyre", "radio")).build() );
cars.add( Car.builder().carId(2).name("CCCCar").discription("used").features( Arrays.asList("radio", "sunroof")).build() );
cars.add( Car.builder().carId(3).name("DDDCar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );
cars.add( Car.builder().carId(4).name("AAACar").discription("used").features( Arrays.asList("radio", "sunroof")).build() );
cars.add( Car.builder().carId(5).name("BBBCar").discription("brandNew").features( Arrays.asList("spare tyre", "radio")).build() );
cars.add( Car.builder().carId(6).name("CCCCar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );
cars.add( Car.builder().carId(7).name("DDDCar").discription("used").features( Arrays.asList("radio", "sunroof")).build() );
cars.add( Car.builder().carId(8).name("AAACar").discription("used").features( Arrays.asList("spare tyre", "sunroof")).build() );
cars.add( Car.builder().carId(9).name("BBBCar").discription("used").features( Arrays.asList("spare tyre", "radio")).build() );
cars.add( Car.builder().carId(10).name("CCCCar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );

// search brand-new AAACar
Set<Car> results = new HashSet<>();
for (Car car : cars) {
	if ( Objects.equals(car.getName(), "AAACar")
			&& Objects.equals(car.getDiscription(), "brandNew") ) {
		results.add(car);
	}
}

for (Car car : results ) {
	System.out.println(car.toString()); // => Car(carId=0, name=AAACar, discription=brandNew, features=[spare tyre, sunroof])
}
```

### CQN(CQEngine Native)を用いる

Streamの代替品として利用することを目的としているため、POJOにAttributeを加えない方針で検証します。
Attributeを用いないCQN記法で試してみます。 
[Generate attributes from private class fields](https://github.com/npgall/cqengine/issues/181)
POJOのprivateなフィールドを取得することは難しいそうなので、getterを用いる設定をcreateAttributesに設定しています。

``` Java
// generate Attribute
CQNParser<Car> parser = CQNParser.forPojoWithAttributes(Car.class, createAttributes(Car.class, MemberFilters.GETTER_METHODS_ONLY));

// create car collection
IndexedCollection<Car> cars = new ConcurrentIndexedCollection<>();
cars.add( Car.builder().carId(0).name("AAACar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );
//(省略)
cars.add( Car.builder().carId(10).name("CCCCar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );

// search brand-new AAACar
ResultSet<Car> results = parser.retrieve(cars,
								"and("
									+ "equal(\"getName\",\"AAACar\"),"
									+ "equal(\"getDiscription\", \"brandNew\")"
								+ " )");

for (Car car : results ) {
	System.out.println(car.toString());   // => Car(carId=0, name=AAACar, discription=brandNew, features=[spare tyre, sunroof])
}
```


### Java8のStream

``` Java
// create car collection
Set<Car> cars = new HashSet<>();
cars.add( Car.builder().carId(0).name("AAACar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );
//(省略)
cars.add( Car.builder().carId(10).name("CCCCar").discription("brandNew").features( Arrays.asList("spare tyre", "sunroof")).build() );

// search brand-new AAACar
Set<Car> results =  cars.stream()
						.filter(car -> Objects.equals(car.getName(), "AAACar")
								&& Objects.equals(car.getDiscription(), "brandNew") )
						.collect(Collectors.toSet());

for (Car car : results ) {
	System.out.println(car.toString());
}
```


## CQEngineをJava7以下で使う方法
CQEngineのVer3以上をはJava7対応していないので、下記エラーが出ます。

```
 Unsupported major.minor version 52.0
```


## 結論
* タイプセーフにこだわらないのであれば、可読性は向上できそうです
* タイプセーフに使うためにはひと手間付け加える必要あり(POJOにAttributeを設定する)
* 最新版はJava7では使えない
* Java7でStreamの代用として利用するという点ではそこまで魅力を感じなかったので(もともとそういうライブラリでない)  
  大量データを高速に処理したいときの候補として覚えておきます

## もっと素直にStreamを Java7- で使う
[Lightweight-Stream-API - Java6・7でもStream APIを使いたい](https://blog.y-yuki.net/entry/2016/11/08/000000) 
こちらはそのままStreamのように利用できるライブラリみたいです
Java8に移行するときは、移行前はこのライブラリで移行後のコードはJava8のStreamといった
カオスな状況にならないように気を付けないといけないとおもったので
今回CQEngineがJava8以降についても標準ライブラリを超えられるかという観点で調査しようと思いました。
なので、今後は大量データ処理でのCQEngineの性能を評価していきたいと思います


# 大量データでの利用
(別記事で書きます)
データを用意するのは大変なので、多めのデータが取れるAPIを探します。
[個人でも使える！おすすめAPI一覧](https://qiita.com/mikan3rd/items/ba4737023f08bb2ca161)