ブール演算--javaビットマップ検索実装
14071 ワード
前言
ブール演算は偉大なブール発明の代数演算であり、簡単な論理と非しかなく、最初は役に立たなかったが、コンピュータへの影響が大きすぎて、インフラから検索エンジンまでどこにもいなかった.
シーン
私たちとして、日常の仕事の中で、私もその需要に直面しました.シーンは、変化が頻繁ではないため、ユーザーの複数の次元のマッチングに関連する複雑な構成があります.
毎回データベースを検索するのは明らかにお得ではありません.データ量も多すぎず、万未満のレベルで、人が配置していますか.これはとても自然で、サーバーのメモリにキャッシュしましょう、しかしいつも力の一つ一つのマッチングができないでしょう、うるさいです、効率もとても低くて、その上ロジックの判断は少し複雑で、配置は主に4つの次元です:機種、ルート、国家、バージョン、各次元はすべて1つの配列で、ある値は1つの["ALL"]で、意味はすべてマッチングします;
引用する
そこでブール演算を考えてみると、比較的簡単で、各値は長い数字のビットに対応して、何本のデータがあって、各次元の各値は最大何ビットですか.
合計10000個の構成であれば、「中国」に対応するビットベクトルは最大10000ビットであり、「中国」という値に対して、100ビット目が1であることは、100個目の構成がこの「中国」次元値を含むことを示す.照会国が「中国」であるのはmapから「中国」に対応するビットベクトルと「ALL」に対応するビットベクトルを取って演算することである.Javaは大きな整数の実現を持っています:BigInteger;構造方法にバイナリビットを表すbyte配列を渡すことができ、byte配列の長さは明らかに:(構成本数/8)+1であり、BigInteger内部では主に左の連続する0を除去する処理が行われる.
すぐに始まる
我々はMongoDBデータベースを使用しているため、操作はspring-dataによるmongodbのパッケージです.CriteriaとQueryのAPIがあり、これもspringの一貫したスタイルです.
そこで、共通のビットマップクエリーを実現し、CriteriaのAPIと似ています.これにより、コードの変更が小さくなります(import springのパッケージを自分のものに変えると、ほとんど終わりません).
まずテストcase(例は少しlowですが、見てみましょう):
ブール演算は偉大なブール発明の代数演算であり、簡単な論理と非しかなく、最初は役に立たなかったが、コンピュータへの影響が大きすぎて、インフラから検索エンジンまでどこにもいなかった.
シーン
私たちとして、日常の仕事の中で、私もその需要に直面しました.シーンは、変化が頻繁ではないため、ユーザーの複数の次元のマッチングに関連する複雑な構成があります.
毎回データベースを検索するのは明らかにお得ではありません.データ量も多すぎず、万未満のレベルで、人が配置していますか.これはとても自然で、サーバーのメモリにキャッシュしましょう、しかしいつも力の一つ一つのマッチングができないでしょう、うるさいです、効率もとても低くて、その上ロジックの判断は少し複雑で、配置は主に4つの次元です:機種、ルート、国家、バージョン、各次元はすべて1つの配列で、ある値は1つの["ALL"]で、意味はすべてマッチングします;
引用する
そこでブール演算を考えてみると、比較的簡単で、各値は長い数字のビットに対応して、何本のデータがあって、各次元の各値は最大何ビットですか.
合計10000個の構成であれば、「中国」に対応するビットベクトルは最大10000ビットであり、「中国」という値に対して、100ビット目が1であることは、100個目の構成がこの「中国」次元値を含むことを示す.照会国が「中国」であるのはmapから「中国」に対応するビットベクトルと「ALL」に対応するビットベクトルを取って演算することである.Javaは大きな整数の実現を持っています:BigInteger;構造方法にバイナリビットを表すbyte配列を渡すことができ、byte配列の長さは明らかに:(構成本数/8)+1であり、BigInteger内部では主に左の連続する0を除去する処理が行われる.
すぐに始まる
我々はMongoDBデータベースを使用しているため、操作はspring-dataによるmongodbのパッケージです.CriteriaとQueryのAPIがあり、これもspringの一貫したスタイルです.
そこで、共通のビットマップクエリーを実現し、CriteriaのAPIと似ています.これにより、コードの変更が小さくなります(import springのパッケージを自分のものに変えると、ほとんど終わりません).
まずテストcase(例は少しlowですが、見てみましょう):
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Test;
import bitmapSearch.BitmapSearcher;
import bitmapSearch.Criteria;
public class BitmapSearchTest {
@Test
public void testQuery() {
List cars = Arrays.asList(new Car(), //
new Car(" ").setToAreas("ALL"), //
new Car(" ", 3).setToAreas(" "), //
new Car(" ", 6).setToAreas(" "));
BitmapSearcher searcher = new BitmapSearcher(cars, new BitmapSearcher.IndexCreator() {
@Override
public String[] indexKeys() {
return new String[] { "id", "brand", "legs", "toAreas", "desc" };
}
@Override
public Object[] fieldValue(Car bean, String indexKey) {
if ("id".equals(indexKey)) {
return new Object[] { bean.id };
} else if ("brand".equals(indexKey)) {
return new Object[] { bean.getBrand() };
} else if ("legs".equals(indexKey)) {
return new Object[] { bean.getLegs() };
} else if ("toAreas".equals(indexKey)) {
return bean.getToAreas();
}else if ("desc".equals(indexKey)) {
return new Object[] { bean.getDesc() };
}
return null;
}
});
Car rs1 = searcher.findOne(
Criteria.where("legs").is(6)//
.andOperator(new Criteria().orOperator(//
Criteria.where("id").is(4), //
Criteria.where("desc").is("MadeInChina")))//
, null);//
Assert.assertTrue(rs1.brand.equals(" "));
List rs2 = searcher.find(Criteria.where("toAreas").in(" ", "ALL"));
Assert.assertTrue(rs2 != null && rs2.size() == 2);
}
private static class Car {
final int id;
static AtomicInteger ID_GEN = new AtomicInteger();
String brand = "QQ";
int legs = 4;
String[] toAreas;
String desc = "";
public Car() {
super();
id = ID_GEN.incrementAndGet();
}
public Car(String brand) {
this();
this.brand = brand;
}
public Car(String brand, int legs) {
this();
this.brand = brand;
this.legs = legs;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getLegs() {
return legs;
}
public void setLegs(int legs) {
this.legs = legs;
}
public Car setToAreas(String... toAreas) {
this.toAreas = toAreas;
return this;
}
public String[] getToAreas() {
return toAreas;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
}
其中,抽象一个内部类IndexCreator,把构造索引以及如何获取索引字段值的工作抛给用户
/** * */ package bitmapSearch; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * * * @author HouKangxi * */ public final class BitmapSearcher { /** * list, 。 */ @SuppressWarnings("rawtypes") private final List beansList; /** * : K: index, value:
*/ private Map
Criteria :
/** * */ package bitmapSearch; import java.math.BigInteger; import java.util.LinkedList; import java.util.List; /** * * * @author houkangxi * */ public class Criteria { protected Object key; protected List
chain; private Criteria prev = this; public Criteria() { chain = new LinkedList (); } public Criteria(Object key) { this(); this.key = key; } Criteria(int noInitChain) { } public static Criteria where(Object key) { return new Criteria(key); } private Criteria addToChain(Criteria c) { prev = c; chain.add(c); return this; } public Criteria is(Object val) { prev.addToChain(new CriteriaOpIs(prev.key, val)); return this; } public Criteria ne(Object val) { prev.addToChain(new CriteriaOpNot(prev.key, val)); return this; } public Criteria in(Object... val) { prev.addToChain(new CriteriaOpIn(prev.key, val)); return this; } public Criteria and(String key) { return addToChain(new CriteriaOpAnd(key)); } public Criteria or(String key) { return addToChain(new CriteriaOpOr(key)); } public Criteria andOperator(Criteria... o) { return addToChain(new CriteriaOpAnd(o)); } public Criteria orOperator(Criteria... o) { return addToChain(new CriteriaOpOr(o)); } BigInteger proc(BitmapSearcher sea, BigInteger prev) { if (chain == null) { return null; } BigInteger rs = prev; for (Criteria c : chain) { rs = c.proc(sea, rs); } return rs; } @Override public String toString() { return getClass().getSimpleName() + "@key=" + key; } }
/** * */ package bitmapSearch; import java.math.BigInteger; import java.util.Arrays; /** * @author houkangxi * */ abstract class CriteriaChain extends Criteria { CriteriaChain(String key) { super(key); } CriteriaChain(Criteria[] list) { super(0); this.chain = Arrays.asList(list); } protected abstract BigInteger op(BigInteger o1, BigInteger o2); @Override protected final BigInteger proc(BitmapSearcher sea, BigInteger prev) { if (chain == null || chain.isEmpty()) { return null; } BigInteger h = chain.get(0).proc(sea, null); for (int i = 1; i < chain.size() && h != null; i++) { h = op(h, chain.get(i).proc(sea, h)); } return op(prev, h); } }
/** * */ package bitmapSearch; import java.math.BigInteger; /** * And ( ) * @author houkangxi * */ class CriteriaOpAnd extends CriteriaChain { CriteriaOpAnd(String key) { super(key); } CriteriaOpAnd(Criteria[] list) { super(list); } @Override protected BigInteger op(BigInteger o1, BigInteger o2) { if (o2 == null || o1 == null) { return null; } return o1.and(o2); } } /** * */ package bitmapSearch; import java.math.BigInteger; import java.util.Map; /** * IN * @author houkangxi * */ class CriteriaOpIn extends Criteria { Object[] colls; CriteriaOpIn(Object key, Object[] colls) { super(key); this.colls = colls; } @Override protected BigInteger proc(BitmapSearcher sea, BigInteger prev) { Map
bitmap = sea.getBitmap(key); if (bitmap == null || colls == null) { return null; } BigInteger bit = null; for (int i = 0; i < colls.length; i++) { Object val = colls[i]; BigInteger I = bitmap.get(val); if (I != null) { bit = bit == null ? I : bit.or(I); } } return bit; } } /** * */ package bitmapSearch; import java.math.BigInteger; import java.util.Map; /** * Is * * @author houkangxi * */ class CriteriaOpIs extends Criteria { private Object ov; CriteriaOpIs(Object k, Object ov) { super(k); this.ov = ov; } @Override protected BigInteger proc(BitmapSearcher sea, BigInteger prev) { Map bimap = sea.getBitmap(key); if (bimap == null) { return null; } return bimap.get(ov); } } /** * */ package bitmapSearch; import java.math.BigInteger; import java.util.Map; /** * NOT ( ) * @author houkangxi * */ class CriteriaOpNot extends Criteria { private Object ov; CriteriaOpNot(Object k, Object ov) { super(k); this.ov = ov; } @Override protected BigInteger proc(BitmapSearcher sea, BigInteger prev) { Map bimap = sea.getBitmap(key); if (bimap == null) { return null; } BigInteger b = bimap.get(ov); if (b == null) { return null; } return b.not(); } } /** * */ package bitmapSearch; import java.math.BigInteger; /** * Or ( ) * @author houkangxi * */ class CriteriaOpOr extends CriteriaChain { CriteriaOpOr(String k) { super(k); } CriteriaOpOr(Criteria[] list) { super(list); } @Override protected BigInteger op(BigInteger o1, BigInteger o2) { if (o2 == null) { return o1; } if (o1 == null) { return o2; } return o1.or(o2); } }