最近、私はjavaapiドキュメントの簡単な検索プロジェクトを行いました。アイデアを要約するためにここに記事を書いてください。
このプロジェクトは、APIドキュメントをローカルに保存し、ローカルAPIドキュメントにアクセスして使用することです。大きなアイデアは、ローカルAPIドキュメント(htmlおよびフォルダーの形式)をトラバースしてから、ローカルhtml Webページのコンテンツを解析し、解析したコンテンツをファイルに入れることです。ファイルの内容の順索引と転置索引を作成します。検索時には、入力されたコンテンツがセグメント化され、保存された転置インデックスの各セグメントの重みに従って検索されます。検索したコンテンツを降順に並べ替えます。
次に、各ステップを調整し
ます。1。ローカルHTMLファイルをトラバースすると、各HTMLファイルが解析されてオブジェクトになります。オブジェクトのコンテンツを特定の形式のファイルに保存します。
コードアイデアの実現:
public static void main(String[] args) throws IOException {
//找到api本地路径下所有的HTML文件
List<File> htmls=listHtml(new File(API_PATH));
FileWriter fw= new FileWriter(RAW_DATA);
//BufferedWriter bw=new BufferedWriter(fw);
PrintWriter pw=new PrintWriter(fw,true);//自动刷新缓冲区
for(File html:htmls){
//一个HTML解析DocInfo所有属性,
DocInfo doc=parseHtml(html);
//System.out.println(doc);
// docs.add(doc);
//保存本地正排索引文件
//格式:一个行为一个doc,title+\3+url+\3+content
String url= html.getAbsolutePath().substring(API_PATH.length());
System.out.println("Parse"+url);
pw.println(doc.getTitle()+"\3"+doc.getUrl()+"\3"+doc.getContent());
}
2.ポジティブインデックスの
確立ポジティブインデックスの確立は、以前に対応する形式でドキュメントに保存されたコンテンツに基づいており、行ごとにオブジェクトに変換されてリストに保存されます。フォローアップのレビューと操作に便利。
大きな質問のコード:
try {
FileReader fr=new FileReader(Parser.RAW_DATA);
BufferedReader br=new BufferedReader(fr);
int id=0;//行号设置为DocInfo的id
String line;
while ((line=br.readLine())!=null){
if(line.trim().equals("")) continue;
//一行对应一个DocInfo对象
DocInfo doc=new DocInfo();
doc.setId(++id);
String[] parts=line.split("\3");//每行按照\3间隔符拆分
doc.setTitle(parts[0]);
doc.setUrl(parts[1]);
doc.setContent(parts[2]);
//添加到正排索引
FORWARDINDEX.add(doc);
}
3.転置
インデックスに従って、転置インデックスを作成します。転置インデックスの重要なアイデアは、キーワードを使用して関連ドキュメントを検索することです。したがって、最初にキーワードを取り出す必要があります。次に、キーワードが配置されているHTMLの情報を保存します。
具体的な方法は次のとおりです。フロントインデックスをトラバースし、タイトルと本文のコンテンツを別々に分離し、単語のセグメンテーションによってタイトルと本文のキーワードを取り出し、マップ<キーワード、html情報>のマップに配置します。キーワードタイトルの重みは+5、各キーワードコンテンツの重みは+1です。最後にマップをトラバースし、情報をMap <keywords、list <HTML information >>に追加します。
部分的なコード表示:
for(DocInfo doc:FORWARDINDEX){
//一个doc:分别对标题和正文分词,每一个分词生成一个weight对象,需要计算权重
Map<String,Weight> cache=new HashMap<>();
List<Term> titleFencis= ToAnalysis.parse(doc.getTitle()).getTerms();
for(Term titleFenci:titleFencis){
//标题分词遍历处理
/*if(titleFenci.getName().contains("�")){
System.out.println("标题分词============"+doc.getUrl());
}*/
Weight w=cache.get(titleFenci.getName());//获取了标题分词到键对应的weight
if(w==null){
w=new Weight();
w.setDoc(doc);
w.setKeyword(titleFenci.getName());
cache.put(titleFenci.getName(),w);
}
//标题分词,权重+10;
w.setWeight(w.getWeight()+10);
}
//正文分词的逻辑和标题分词逻辑相同
List<Term> contentFencis=ToAnalysis.parse(doc.getContent()).getTerms();
for(Term contentFenci:contentFencis){
/*if(contentFenci.getName().contains("�")){
System.out.println("正文分词============"+doc.getUrl());
}*/
Weight w=cache.get(contentFenci.getName());
if(w==null){
w= new Weight();
w.setDoc(doc);
w.setKeyword(contentFenci.getName());
cache.put(contentFenci.getName(),w);
}
w.setWeight(w.getWeight()+1);
}
//把临时保存的map数据(keyWord-weight) 全部保存到倒排索引里
//遍历
for(Map.Entry<String,Weight> e :cache.entrySet()){
String keyword=e.getKey();
Weight w=e.getValue();
//更新到倒排索引Map<String,List<Weight>>---->多个文档,同一个关键次,保存在list
//先在倒排索引中,通过keyword获取已有的值;
List<Weight> weights=INVERTED_INDEX.get(keyword);
if(weights==null){
//如果拿不到就存放进去
weights=new ArrayList<>();
INVERTED_INDEX.put(keyword,weights);
}
weights.add(w);//倒排中,添加当前文档每个分词对应的weight对象
}
}
上記の手順を実行すると、初期化作業が完了します。
次に、servletを使用して処理します。servlet
の作業プロセス:browser http request ------ "tomcat server -------" arrival servlet ----- "execute doget、dopost method ----》データを返します。
@WebServletアノテーションがサーブレットに設定されている限り、コンテナはその中の情報を自動的に読み取ります。パラメータ値は、訪問を見つけるためのURLです。
具体的な方法は次のとおりです。フロントエンドのWebページがバックエンドのURLを取得し、リクエストを受信する形式がフォームに追加されて、バックエンドと顧客のリクエストを受信します。ユーザーが検索コンテンツを入力すると、バックエンドは入力コンテンツ-----単語のセグメンテーションを処理し、キーワードに従って転置インデックスを検索します。見つかった場合は、このリストを取得します。そして、リストをトラバースし、リスト内の重みに従って出力を並べ替えます。バックエンドは結果をオブジェクトに変換してから、Json文字列にシリアル化します。最後に、フロントエンドディスプレイに戻ります。
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");//ajax请求
//构建返回给前端的内容。使用对象,之后再序列化为json字符串
Map<String,Object> map=new HashMap<>();
//解析请求数据
String query=req.getParameter("query");
List<Result> results=new ArrayList<>();
try{
//根据搜索内容处理搜索业务
if(query==null||query.trim().length()==0){
map.put("ok",false);
map.put("msg","搜索内容为空");
}else{
//1.根据搜索内容,进行分词,遍历每个分词
for (Term t: ToAnalysis.parse(query).getTerms()){
String fenci=t.getName();//搜索的分词
//如果分词是没有意义的分词,就不执行,就跳过
//TODO 定义一个数组,包含没有意义的而关键词,if(isVAlid(fenci))continue;
//2.每个分词,在倒排中查找对应的文档(一个分词对应多个文档)
List<Weight> weights=Index.get(fenci);
//3.一个文档转为换result(不同分词可能存在相同文档,需要合并)
for(Weight w:weights){
//先转换Weight为result
Result r=new Result();
r.setId(w.getDoc().getId());
r.setTitle(w.getDoc().getTitle());
r.setWeight(w.getWeight());
r.setUrl(w.getDoc().getUrl());
//自定义:假设文档内容超过60的部分隐藏为...
String content=w.getDoc().getContent();
r.setDesc((content.length()<=60?content:content.substring(0,60)+"..."));
//TODO 合并操作:需要
// (1)在List<Result>找已有的,判断DocID相同,
// 直接在已有的result权重上加上现有的
//不存在啊,直接放进去
results.add(r);
}
}
//4.合并完成后对,list<result>排序,根据权重降序排序
results.sort((o1,o2 )-> {
//权重降序
return Integer.compare(o2.getWeight(),o1.getWeight());
});
map.put("ok",true);
map.put("data",results);
}
}catch(Exception e){
e.printStackTrace();
map.put("ok",false);//操作失败
map.put("msg",false);
}
PrintWriter pw=resp.getWriter();//获取输出流
//设置响应体内容:map对象序列化为json字符串
pw.println(new ObjectMapper().writeValueAsString(map));
}
これは、検索エンジン全体の一般的な考え方です。
レンダリングを見てみましょう: