package cn.reghao.tnb.search.app.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Set;

/**
 * @author reghao
 * @date 2025-08-20 17:32:59
 */
public class LuceneQueryBuilder {
    public void search() throws Exception {
        Analyzer luceneAnalyzer = new IKAnalyzer();
        String keyword = "";

        String field = "title";
        QueryParser queryParser = new QueryParser(field, luceneAnalyzer);
        Query query = queryParser.parse(keyword);

        Query termQuery = new TermQuery(new Term(field, keyword));

        Query fuzzyQuery = new FuzzyQuery(new Term(field, keyword), 1);

        PhraseQuery.Builder builder = new PhraseQuery.Builder();
        builder.add(new Term(field, keyword), 1);
        Query phraseQuery = builder.build();

        Set<Integer> scopes = Set.of();
        Query titleQuery = new QueryParser("title", luceneAnalyzer).parse(keyword);
        // 数值集合
        Query setQuery = IntPoint.newSetQuery("scope", scopes);
        // 布尔运算, MUST + MUST 表示查找 2 个都有的部分
        Query booleanQuery = new BooleanQuery.Builder()
                .add(titleQuery, BooleanClause.Occur.MUST)
                .add(setQuery, BooleanClause.Occur.MUST)
                .build();

        // 字符串精确查询(等值查询)
        Query termQuery1 = new TermQuery(new Term("scope", "1"));
        Term term1 = new Term("scope", "1");
        Term term2 = new Term("scope", "2");

        // 数值精确查询
        Query exactQuery1 = IntPoint.newExactQuery("scope", 1);
        // 数值集合
        // Query setQuery = IntPoint.newSetQuery("scope", 1, 2, 3);

        // 数值范围查询
        // PointRangeQuery
        IntPoint.newRangeQuery("", 1, 4);

        // 排序
        SortField sortField = new SortField("publishTime", SortField.Type.LONG,false);
        Sort sort = new Sort(sortField);
        int pageSize = 10;

        String indexDir = "";
        Directory directory = FSDirectory.open(Paths.get(indexDir));
        IndexReader indexReader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        indexSearcher.search(booleanQuery, pageSize, sort);
    }

    static void termQuery() {
        Query query = new TermQuery(new Term("name", "李白"));
        query(query);
    }

    static PrefixQuery prefixQuery(String field, String startWith) {
        return new PrefixQuery(new Term(field, startWith));
    }

    static void prefixQuery1(String field, String startWith) {
        Query query1 = new TermQuery(new Term("tableName", "RujiaHotel"));
        Query query2 = new PrefixQuery(new Term(field, startWith));

        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(query1, BooleanClause.Occur.MUST);
        builder.add(query2, BooleanClause.Occur.MUST);
        BooleanQuery booleanQuery = builder.build();
        query(booleanQuery);
    }

    static void wildcardQuery() {
        Query query = new WildcardQuery(new Term("name", "李?"));
        query(query);
    }

    static void fuzzyQuery() {
        //"李百"->"李白"，只需修改一次，故可以搜索到数据；"里百"则搜索不到数据
        Query query = new FuzzyQuery(new Term("name", "里百"), 1);
        query(query);
    }

    static void numberQuery() {
        //精确查询
        Query query = LongPoint.newExactQuery("id", 123456);
        query(query);

        //范围查询
        query = LongPoint.newRangeQuery("id", 123L, 12345678L);
        query(query);
    }

    static void booleanQuery() {
        Query query1 = new TermQuery(new Term("name", "李白"));
        Query query2 = new TermQuery(new Term("name", "杜甫"));
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(query1, BooleanClause.Occur.SHOULD);
        builder.add(query2, BooleanClause.Occur.SHOULD);
        BooleanQuery booleanQuery = builder.build();
        query(booleanQuery);
    }

    static void boostQuery() {
        Query query1 = new BoostQuery(new TermQuery(new Term("name", "李白")), 1.5f);
        Query query2 = new BoostQuery(new TermQuery(new Term("name", "杜甫")), 1.6f);
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(query1, BooleanClause.Occur.SHOULD);
        builder.add(query2, BooleanClause.Occur.SHOULD);
        BooleanQuery booleanQuery = builder.build();

        query(booleanQuery);
    }

    static void queryParserQuery() throws ParseException {
        IKAnalyzer ikAnalyzer = new IKAnalyzer();
        QueryParser queryParser = new QueryParser("name", ikAnalyzer);
        Query query = queryParser.parse("李白和杜甫");
        query(query);

        //多字段查询
        MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(new String[]{"name", "about"}, ikAnalyzer);
        query = multiFieldQueryParser.parse("李白和子美");
        query(query);
    }

    public static void query(Query query) {
        String nativeLuceneDir = "/opt/data/search_data/native_lucene";
        String indexName = "wenshu_lucene";
        try {
            String indexDir = String.format("%s/%s", nativeLuceneDir, indexName);
            Directory directory = FSDirectory.open(Paths.get(indexDir));
            IndexReader indexReader = DirectoryReader.open(directory);
            IndexSearcher indexSearcher = new IndexSearcher(indexReader);

            // 查询前 100 条数据
            TopDocs topDocs = indexSearcher.search(query, 100);
            System.out.printf("本次搜索共找到 %s 条数据\n", topDocs.totalHits.value);
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc scoreDoc : scoreDocs) {
                Document document = indexReader.document(scoreDoc.doc);
                System.out.println(document.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
