001package net.gdface.sdk.fse;
002
003import gu.sql2java.BaseRow;
004import gu.sql2java.Managers;
005import gu.sql2java.TableListener;
006import gu.sql2java.TableManager.Action;
007import gu.sql2java.exception.RuntimeDaoException;
008import net.gdface.sdk.fse.CodeBean;
009import net.gdface.sdk.fse.FeatureSe;
010import net.gdface.sdk.fse.FeatureSeDecorator;
011import static net.gdface.utils.SimpleLog.log;
012
013import java.lang.reflect.ParameterizedType;
014import java.util.concurrent.atomic.AtomicInteger;
015
016import static com.google.common.base.Preconditions.*;
017/**
018 * 基于数据库的特征搜索引擎
019 * 依赖库: 
020 * <ul>
021 * <li>groupId: com.gitee.l0km</li>
022 * <li>artifactId: sql2java-manager</li>
023 * </ul>
024 * @author guyadong
025 *
026 * @param <F> 保存特征数据的记录
027 */
028public abstract class BaseFseDbEngine<F extends BaseRow>{
029        protected final FeatureSeDecorator fse;
030        private final Class<F> rawType;
031        /**
032         * 
033         * @param fse {@link FeatureSe}引擎实例
034         */
035        @SuppressWarnings("unchecked")
036        public BaseFseDbEngine(FeatureSe fse) {
037                this.fse= FeatureSeDecorator.makeDecorator(checkNotNull(fse,"fse is null"));
038                rawType = (Class<F>) ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
039        }
040
041        /**
042         * 加载数据库中所有特征到FSE内存表
043         */
044        protected void loaddb(){
045                log("Loading feature data from Database into memory...");
046                final AtomicInteger rowCount = new AtomicInteger(0);
047                Managers.managerOf(rawType).loadAll(new Action<F>(){
048                        @Override
049                        public void call(F bean) throws RuntimeDaoException {
050                                addFeatureBean(bean);   
051                                showProgress(rowCount.incrementAndGet());
052                        }
053                });
054        }
055        /**
056         * 搜索引擎初始化
057         * @param dao Dao对象
058         */
059        public void init(){
060                loaddb();
061                Managers.managerOf(rawType).registerListener(featureTableListener);
062        }
063        /** 显示完成进度 */
064        private void showProgress(int c){
065                // 显示完成进度
066                if(0==c%100000){
067                        System.out.printf("*");
068                        if(0==(c%1000000)){                                                     
069                                if(0==(c%=10000000)){
070                                        System.out.println();
071                                }else{
072                                        System.out.print(" ");
073                                }
074                        }
075                }
076        }
077        
078        private final TableListener<F> featureTableListener = 
079                new TableListener.Adapter<F>(){ 
080                        @Override
081                        public void afterInsert(F bean) throws RuntimeDaoException {                                                            
082                                addFeatureBean(bean);
083                        }
084                        @Override
085                        public void afterDelete(F bean) throws RuntimeDaoException {
086                                fse.removeFeature(featureIdOf(bean));
087                        }
088                };
089        
090        protected void addFeatureBean(F bean){
091                if(beanFilter(bean)){
092                        byte[] feature = featureOf(bean);
093                        if(null != feature){
094                                fse.addFeature(
095                                                featureIdOf(bean),
096                                                feature, ownerOf(bean));
097                        }
098                }
099        }
100        
101        /**
102         * 从特征记录中返回特征数据的MD5
103         * @param bean
104         * @return MD5校验码(16字节数组)
105         */
106        protected byte[] featureIdOf(F bean){
107                return null;
108        }
109        /**
110         * 从特征记录中返回特征所属者ID
111         * @param bean
112         * @return 32字节HEX字符串
113         */
114        protected String ownerOf(F bean){
115                return null;
116        }
117
118        /**
119         * 从特征记录中返回特征数组
120         * @param bean
121         * @return 特征数据(二进制数组)
122         */
123        protected abstract byte[] featureOf(F bean);
124        
125        /**
126         * 数据库记录过滤器函数
127         * @param bean
128         * @return 返回{@code true}则加入FSE,否则跳过
129         */
130        protected boolean beanFilter(F bean) {
131                return bean != null;
132        }
133        /**
134         * @param feature  要比对的特征码
135         * @param similarty 相似度阀值
136         * @param rows 最多返回的记录数目
137         * @return 返回包含相似度(降序)的结果数组,如果没有查到匹配的记录则返回空数组
138         * @see FeatureSe#searchCode(byte[], double, int)
139         */
140        public CodeBean[] searchFeatures(byte[] feature, double similarty, int rows) {
141                return fse.searchCode(checkNotNull(feature,"feature is null"),similarty,rows);
142        }
143
144        @Override
145        public String toString() {
146                StringBuilder builder = new StringBuilder();
147                builder.append("BaseFseDbEngine [fseClassName=");
148                builder.append(fse);
149                builder.append("]");
150                return builder.toString();
151        }
152}