Google我想大家应该都用过,输入我们的搜索关键字,然后回车,Google就会返回搜索结果,在返回的界面里,会对命中的关键字进行红色字体标注出来,这就是高亮功能。
Lucene5中高亮功能相关API都在org.apache.lucene.search.highlight包下,我们先从简单的高亮器开始即Highlighter
透过Hightlighter类的源码,我们首先需要去了解里面的每个成员变量的含义:
public static final int DEFAULT_MAX_CHARS_TO_ANALYZE = 50*1024; private int maxDocCharsToAnalyze = DEFAULT_MAX_CHARS_TO_ANALYZE; private Formatter formatter; private Encoder encoder; private Fragmenter textFragmenter=new SimpleFragmenter(); private Scorer fragmentScorer=null;
formatter:高亮的格式化器,即使用什么标签来高亮。默认是<B></B>
Encoder:编码器,比如返回的高亮片段里面包含了特殊字符,比如< > & "等等,如果你需要进行转义,则 需要指定一个编码器
Scorer:是用来为每个命中的Frag进行打分的
Fragmenter:即拆分器,把原始文本拆分成一个个高亮片段。
DEFAULT_MAX_CHARS_TO_ANALYZE:设置了当前高亮器可以处理的最大字符个数
下面是一些高亮器的简单使用示例:
package com.yida.framework.lucene5.hightlight; import java.io.IOException; import java.util.Arrays; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.document.IntField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.queries.CommonTermsQuery; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermRangeQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.search.highlight.Formatter; import org.apache.lucene.search.highlight.Fragmenter; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; import org.apache.lucene.search.highlight.QueryScorer; import org.apache.lucene.search.highlight.QueryTermScorer; import org.apache.lucene.search.highlight.Scorer; import org.apache.lucene.search.highlight.SimpleFragmenter; import org.apache.lucene.search.highlight.SimpleHTMLEncoder; import org.apache.lucene.search.highlight.SimpleHTMLFormatter; import org.apache.lucene.search.highlight.SimpleSpanFragmenter; import org.apache.lucene.search.highlight.TokenSources; import org.apache.lucene.search.join.BitDocIdSetCachingWrapperFilter; import org.apache.lucene.search.join.BitDocIdSetFilter; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ToParentBlockJoinQuery; import org.apache.lucene.search.spans.SpanNearQuery; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.search.spans.SpanTermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.util.BytesRef; /** * 高亮简单测试 * * @author Lanxiaowei * */ public class SimpleHightlightTest { final int QUERY = 0; final int QUERY_TERM = 1; final String FIELD_NAME = "contents"; private static final String NUMERIC_FIELD_NAME = "nfield"; private Directory ramDir = new RAMDirectory(); private Analyzer analyzer = new StandardAnalyzer(); int numHighlights = 0; TopDocs hits; int mode = QUERY; Fragmenter frag = new SimpleFragmenter(20); final FieldType FIELD_TYPE_TV; { FieldType fieldType = new FieldType(TextField.TYPE_STORED); fieldType.setStoreTermVectors(true); fieldType.setStoreTermVectorPositions(true); fieldType.setStoreTermVectorPayloads(true); fieldType.setStoreTermVectorOffsets(true); fieldType.freeze(); FIELD_TYPE_TV = fieldType; } String[] texts = { "Hello this is a piece of text that is very long and contains too much preamble and the meat is really here which says kennedy has been shot", "This piece of text refers to Kennedy at the beginning then has a longer piece of text that is very long in the middle and finally ends with another reference to Kennedy", "JFK has been shot", "John Kennedy Kennedy has been shot", "This text has a typo in referring to Keneddy", "wordx wordy wordz wordx wordy wordx worda wordb wordy wordc", "y z x y z a b", "lets is a the lets is a the lets is a the lets" }; /** * 创建测试索引 * * @throws IOException */ public void createIndex() throws IOException { // Analyzer analyzer = new StandardAnalyzer(); IndexWriter writer = new IndexWriter(ramDir, new IndexWriterConfig( analyzer)); // 添加几个文本域 for (String text : texts) { writer.addDocument(doc(FIELD_NAME, text)); } // 添加几个数字域 Document doc = new Document(); doc.add(new IntField(NUMERIC_FIELD_NAME, 1, Field.Store.NO)); doc.add(new StoredField(NUMERIC_FIELD_NAME, 1)); writer.addDocument(doc); doc = new Document(); doc.add(new IntField(NUMERIC_FIELD_NAME, 3, Field.Store.NO)); doc.add(new StoredField(NUMERIC_FIELD_NAME, 3)); writer.addDocument(doc); doc = new Document(); doc.add(new IntField(NUMERIC_FIELD_NAME, 5, Field.Store.NO)); doc.add(new StoredField(NUMERIC_FIELD_NAME, 5)); writer.addDocument(doc); doc = new Document(); doc.add(new IntField(NUMERIC_FIELD_NAME, 7, Field.Store.NO)); doc.add(new StoredField(NUMERIC_FIELD_NAME, 7)); writer.addDocument(doc); Document childDoc = doc(FIELD_NAME, "child document"); Document parentDoc = doc(FIELD_NAME, "parent document"); writer.addDocuments(Arrays.asList(childDoc, parentDoc)); // 强制合并段文件,限制合并后段文件个数最大数量 writer.forceMerge(1); writer.close(); } /** * 为Document添加域 * * @param name * @param value * @return */ private Document doc(String name, String value) { Document d = new Document(); d.add(new Field(name, value, FIELD_TYPE_TV)); return d; } /** * 创建Token对象 * * @param term * @param start * @param offset * @return */ private static Token createToken(String term, int start, int offset) { return new Token(term, start, offset); } public Highlighter getHighlighter(Query query, String fieldName, Formatter formatter) { return getHighlighter(query, fieldName, formatter, true); } /** * 创建高亮器 * * @param query * @param fieldName * @param formatter * @param expanMultiTerm * @return */ public Highlighter getHighlighter(Query query, String fieldName, Formatter formatter, boolean expanMultiTerm) { Scorer scorer; if (mode == QUERY) { scorer = new QueryScorer(query, fieldName); // 是否展开多Term查询 if (!expanMultiTerm) { ((QueryScorer) scorer).setExpandMultiTermQuery(false); } } else if (mode == QUERY_TERM) { scorer = new QueryTermScorer(query); } else { throw new RuntimeException("Unknown highlight mode"); } return new Highlighter(formatter, scorer); } /** * 获取高亮后的文本(如果高亮失败,则返回原样文本) * * @param query * @param fieldName * @param text * @return * @throws IOException * @throws InvalidTokenOffsetsException */ private String highlightField(Query query, String fieldName, String text) throws IOException, InvalidTokenOffsetsException { // 将用户输入的搜索关键字通过分词器转化为TokenStream TokenStream tokenStream = analyzer.tokenStream(fieldName, text); // SimpleHTMLFormatter默认是使用<B></B> SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(); // 第3个参数表示默认域 QueryScorer scorer = new QueryScorer(query, fieldName, FIELD_NAME); Highlighter highlighter = new Highlighter(formatter, scorer); highlighter.setTextFragmenter(new SimpleFragmenter(Integer.MAX_VALUE)); // maxNumFragments:最大的高亮个数,separator:多个高亮段之间的分隔符,默认是... String rv = highlighter.getBestFragments(tokenStream, text, 1, "..."); return rv.length() == 0 ? text : rv; } public Query doSearching(Query unReWrittenQuery) throws Exception { IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); // 对于MultiTermQuery, TermRangeQuery, PrefixQuery,你如果使用QueryTermScorer而非QueryScorer, //那么你必须对MultiTermQuery, TermRangeQuery, PrefixQuery进行rewrite Query query = unReWrittenQuery.rewrite(reader); hits = searcher.search(query, null, 1000); return query; } public void testHighlightingWithDefaultField() throws Exception { String s1 = "I call our world world Flatland, not because we call it so"; PhraseQuery q = new PhraseQuery(); // 表示两个Term之间最大3个间距 q.setSlop(3); q.add(new Term(FIELD_NAME, "world")); q.add(new Term(FIELD_NAME, "flatland")); String observed = highlightField(q, FIELD_NAME, s1); System.out.println(observed); q = new PhraseQuery(); q.setSlop(3); q.add(new Term("text", "world")); q.add(new Term("text", "flatland")); // 高亮域域查询时Query域不一致,所以无法高亮,这个务必注意 observed = highlightField(q, FIELD_NAME, s1); System.out.println(observed); } /** * CommonTermsQuery中使用高亮 * * @throws Exception */ public void testHighlightingCommonTermsQuery() throws Exception { createIndex(); // 第一个参数:频率高的Term必须出现,第二个参数:频率低的Term可有可无,第三个参数表示Term出现的最大频率 CommonTermsQuery query = new CommonTermsQuery(Occur.MUST, Occur.SHOULD, 3); query.add(new Term(FIELD_NAME, "this")); query.add(new Term(FIELD_NAME, "long")); query.add(new Term(FIELD_NAME, "very")); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); TopDocs hits = searcher.search(query, 10); System.out.println("hits.totalHits:" + hits.totalHits); QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); Document doc = searcher.doc(hits.scoreDocs[0].doc); String storedField = doc.get(FIELD_NAME); TokenStream stream = TokenSources.getAnyTokenStream( searcher.getIndexReader(), hits.scoreDocs[0].doc, FIELD_NAME, doc, analyzer); Fragmenter fragmenter = new SimpleSpanFragmenter(scorer); highlighter.setTextFragmenter(fragmenter); String fragment = highlighter.getBestFragment(stream, storedField); System.out.println("fragment:" + fragment); doc = searcher.doc(hits.scoreDocs[1].doc); storedField = doc.get(FIELD_NAME); stream = TokenSources.getAnyTokenStream(searcher.getIndexReader(), hits.scoreDocs[1].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleSpanFragmenter(scorer)); fragment = highlighter.getBestFragment(stream, storedField); // 打印第二个匹配结果高亮后的结果,默认是加<B></B> System.out.println("fragment:" + fragment); reader.close(); ramDir.close(); } /** * 测试下高亮最大显示个数和高亮段显示字符长度控制 * * @throws Exception */ public void testSimpleTermQueryHighlighter() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); Query query = doSearching(new TermQuery(new Term(FIELD_NAME, "kennedy"))); // 这里不能简单的使用TermQuery,MultiTermQuery,需要query.rewriter下,需要引起你们的注意 // Query query = new TermQuery(new Term(FIELD_NAME, "kennedy")); // 设置最大显示的高亮段个数,即显示<B></B>的个数 int maxNumFragmentsRequired = 1; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { String text = searcher.doc(hits.scoreDocs[i].doc).get(FIELD_NAME); TokenStream tokenStream = analyzer.tokenStream(FIELD_NAME, text); // SimpleFragmenter构造函数里的这个参数表示显示的高亮段字符的总长度<B></B>标签也是计算在内的 // 自己调整这个数字,数数显示的高亮段字符的长度去感受下,你就懂了 highlighter.setTextFragmenter(new SimpleFragmenter(17)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } public void testSimplePhraseQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); PhraseQuery phraseQuery = new PhraseQuery(); phraseQuery.add(new Term(FIELD_NAME, "very")); phraseQuery.add(new Term(FIELD_NAME, "long")); phraseQuery.add(new Term(FIELD_NAME, "contains"), 3); // 如果不对Query进行rewrite,你将会得到一个NullPointerException Query query = doSearching(phraseQuery); // 这个参数很诡异 SimpleFragmenter的构造参数, int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } // 测试2 phraseQuery = new PhraseQuery(); phraseQuery.add(new Term(FIELD_NAME, "piece"), 1); phraseQuery.add(new Term(FIELD_NAME, "text"), 3); phraseQuery.add(new Term(FIELD_NAME, "refers"), 4); phraseQuery.add(new Term(FIELD_NAME, "kennedy"), 6); query = doSearching(phraseQuery); maxNumFragmentsRequired = 2; scorer = new QueryScorer(query, FIELD_NAME); highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在正则查询中使用高亮器 * * @throws Exception */ public void testRegexQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); Query query = new RegexpQuery(new Term(FIELD_NAME, "ken.*")); searcher = new IndexSearcher(reader); hits = searcher.search(query, 100); int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在通配符查询中使用高亮器 * * @throws Exception */ public void testWildcardQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); Query query = new WildcardQuery(new Term(FIELD_NAME, "k?nnedy")); searcher = new IndexSearcher(reader); hits = searcher.search(query, 100); int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在TermRangeQuery中使用高亮器 * * @throws Exception */ public void testTermRangeQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); TermRangeQuery rangeQuery = new TermRangeQuery( FIELD_NAME, new BytesRef("kannedy"), new BytesRef("kznnedy"), true, true); rangeQuery.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE); searcher = new IndexSearcher(reader); hits = searcher.search(rangeQuery, 100); int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(rangeQuery, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在SpanNear查询中使用高亮器 * * @throws Exception */ public void testSpanNearQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); Query query = new SpanNearQuery(new SpanQuery[] { new SpanTermQuery(new Term(FIELD_NAME, "beginning")), new SpanTermQuery(new Term(FIELD_NAME, "kennedy")) }, 3, false); /*Query query = doSearching(new SpanNearQuery(new SpanQuery[] { new SpanTermQuery(new Term(FIELD_NAME, "beginning")), new SpanTermQuery(new Term(FIELD_NAME, "kennedy")) }, 3, false));*/ searcher = new IndexSearcher(reader); hits = searcher.search(query, 100); int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在FuzzyQuery查询中使用高亮器 * * @throws Exception */ public void testFuzzyQueryHightlighting() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); FuzzyQuery query = new FuzzyQuery(new Term(FIELD_NAME, "kinnedy"), 2); searcher = new IndexSearcher(reader); hits = searcher.search(query, 100); int maxNumFragmentsRequired = 2; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { final Document doc = searcher.doc(hits.scoreDocs[i].doc); String text = doc.get(FIELD_NAME); TokenStream tokenStream = TokenSources.getAnyTokenStream(reader, hits.scoreDocs[i].doc, FIELD_NAME, doc, analyzer); highlighter.setTextFragmenter(new SimpleFragmenter(40)); String result = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + result); } } /** * 在joinQuery中使用高亮器 * @throws Exception */ public void testToParentBlockJoinQuery() throws Exception { // 创建索引 createIndex(); IndexReader reader = DirectoryReader.open(ramDir); IndexSearcher searcher = new IndexSearcher(reader); //你过滤出域值包含parent的索引文档作为parent BitDocIdSetFilter parentFilter = new BitDocIdSetCachingWrapperFilter( new QueryWrapperFilter(new TermQuery(new Term(FIELD_NAME, "parent")))); //然后通过ToParentBlockJoinQuery在parent中找child索引文档且child索引文档必须符合[域值包含child字符] //我们在创建索引时是通过addDocuments添加的parent和child的,即addDocuments,这里接收一个documents数组, //父子关系判定规则是,数组中最后一个索引为parent,前面剩下的索引文档都作为parent的child,记住child必须在parent前面 //这也是addDocuments和addDocument的区别 Query query = new ToParentBlockJoinQuery(new TermQuery(new Term( FIELD_NAME, "child")), parentFilter, ScoreMode.Total); hits = searcher.search(query, 100); int maxNumFragmentsRequired = 3; QueryScorer scorer = new QueryScorer(query, FIELD_NAME); Highlighter highlighter = new Highlighter(scorer); for (int i = 0; i < hits.totalHits; i++) { String text = "child document"; TokenStream tokenStream = analyzer.tokenStream(FIELD_NAME, text); highlighter.setTextFragmenter(new SimpleFragmenter(50)); String fragment = highlighter.getBestFragments(tokenStream, text, maxNumFragmentsRequired, "..."); System.out.println("\t" + fragment); } } /** * 测试高亮时对特殊字符进行编码,如< > & "等等 * 在构造高亮器时传入SimpleHTMLEncoder即可 * 通过SimpleHTMLFormatter可以自定义高亮时的开始和结束标签,如:new SimpleHTMLFormatter("<font color=\"red\">","</font>") * 默认是<B> </B> * @throws Exception */ public void testEncoding() throws Exception { String rawDocContent = "\"Smith & sons' prices < 3 and >4\" claims article"; Query query = new RegexpQuery(new Term(FIELD_NAME,"price.*")); QueryScorer scorer = new QueryScorer(query, FIELD_NAME, FIELD_NAME); Highlighter highlighter = new Highlighter(new SimpleHTMLFormatter("<font color=\"red\">","</font>"),new SimpleHTMLEncoder(),scorer); highlighter.setTextFragmenter(new SimpleFragmenter(2000)); TokenStream tokenStream = analyzer.tokenStream(FIELD_NAME, rawDocContent); String encodedSnippet = highlighter.getBestFragments(tokenStream, rawDocContent, 1, ""); System.out.println(encodedSnippet); } public static void main(String[] args) throws Exception { SimpleHightlightTest simpleHightlightTest = new SimpleHightlightTest(); // simpleHightlightTest.testHighlightingCommonTermsQuery(); // simpleHightlightTest.testHighlightingWithDefaultField(); // simpleHightlightTest.testSimpleTermQueryHighlighter(); // simpleHightlightTest.testSimplePhraseQueryHightlighting(); simpleHightlightTest.testRegexQueryHightlighting(); //simpleHightlightTest.testWildcardQueryHightlighting(); //simpleHightlightTest.testToParentBlockJoinQuery(); //simpleHightlightTest.testSpanNearQueryHightlighting(); //simpleHightlightTest.testFuzzyQueryHightlighting(); //simpleHightlightTest.testTermRangeQueryHightlighting(); //simpleHightlightTest.testEncoding(); } }
请注意看里面的代码注释,关键地方我有加相关说明。
这两个参数很诡异
当你设置最多显示2个高亮段,但如果SimpleFragmenter构造参数设置的最大段字符长度能够显示超过2个高亮段,则会无视maxNumFragmentsRequired设置
相反如果你最大能显示的段字符长度设置的很小不足以显示1个高亮段,而最多能显示的高亮段个数大于1,这是最大能显示的段字符长度设置无效,以最多能显示的高亮段个数为准。
int maxNumFragmentsRequired = 3;
new SimpleFragmenter(2)
上面两个参数的设置需要引起你们的注意。
接着来说说FastVectorHighlighter快速高亮器,为什么叫快速高亮器呢?意思就是说使用它进行高亮速度比较快,那它跟普通的Hightlighter有何区别呢?
两者本质区别就是实现方式不同,普通的Hightlighter是基本分词实现的,即先把用户输入的搜索关键字通过分词器Analyzer分词为一个个的Term,然后与Filed的域值进行算法匹配的。而FastVectorHighlighter是基于项向量实现的,从域中加载出位置起始信息,位置增量,项向量等信息,知道了每个域中每个Term的位置信息,自然就能快速的定位Term,然后在Term两头添加上高亮标签。既然需要读取项向量信息,意味着我们在创建索引的时候,就需要设置域存储位置起始索引、位置增量以及项向量,体现在API上就是:
type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true);
由于需要额外存储TermVector信息,则意味着需要额外占用硬盘空间和更多的磁盘IO操作,索引体积变大了,我们在进行索引查询的时候,占用的内存也会加大,所以不能说FastVectorHighlighter能完全替代Hightlighter,该不该使用FastVectorHighlighter应该考虑为索引域额外存储TermVector信息会带来多少查询性能的损耗,两者之间要做一个权衡。如果影响很大,这时就可以考虑使用前端JavaScript里进行高亮。即把用户输入的搜索关键字传递到后台里,后台对用户输入的搜索关键字进行分词,然后把分词后的Term回传到前端,在前端JS里进行高亮操作。
FastVectorHighlighter除了在高亮速度上比普通Hightlighter快点以外,它还有个特色就是支持多种样式高亮即不同的命中关键字可以使用不同的高亮样式进行显示(比如字体颜色不同,这取决于你高亮标签里的CSS样式),FastVectorHighlighter还支持相邻的几个命中的关键字合并在一起进行高亮等等。
至于FastVectorHighlighter如何使用,两者在API使用上没有太大的区别,大家还是看下面的示例代码吧:
package com.yida.framework.lucene5.facet; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.highlight.Encoder; import org.apache.lucene.search.highlight.SimpleHTMLEncoder; import org.apache.lucene.search.vectorhighlight.FastVectorHighlighter; import org.apache.lucene.search.vectorhighlight.FieldQuery; import org.apache.lucene.search.vectorhighlight.FragListBuilder; import org.apache.lucene.search.vectorhighlight.FragmentsBuilder; import org.apache.lucene.search.vectorhighlight.ScoreOrderFragmentsBuilder; import org.apache.lucene.search.vectorhighlight.SimpleFragListBuilder; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; /** * 快速高亮器测试 * * @author Lanxiaowei * */ public class FastVectorHighlighterTest { public static void main(String[] args) throws Exception { // testSimpleHighlightTest(); // testPhraseHighlightLongTextTest(); // testPhraseHighlightTest(); // testBoostedPhraseHighlightTest(); testFormater(); } /** * 快速高亮器第一个简单测试 * * @throws IOException */ public static void testSimpleHighlightTest() throws IOException { Directory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig( new StandardAnalyzer())); Document doc = new Document(); FieldType type = new FieldType(TextField.TYPE_STORED); type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true); type.freeze(); Field field = new Field( "field", "This is a test where foo is highlighed and should be highlighted", type); doc.add(field); writer.addDocument(doc); FastVectorHighlighter highlighter = new FastVectorHighlighter(); IndexReader reader = DirectoryReader.open(writer, true); int docId = 0; FieldQuery fieldQuery = highlighter.getFieldQuery(new TermQuery( new Term("field", "foo")), reader); /** * 测试高亮段显示字符最大长度的影响 */ String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, "field", 54, 1); System.out.println(bestFragments[0]); bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, "field", 52, 1); System.out.println(bestFragments[0]); bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, "field", 30, 1); System.out.println(bestFragments[0]); reader.close(); writer.close(); dir.close(); } public static void testPhraseHighlightLongTextTest() throws IOException { Directory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig( new StandardAnalyzer())); Document doc = new Document(); FieldType type = new FieldType(TextField.TYPE_STORED); type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true); type.freeze(); Field text = new Field( "text", "Netscape was the general name for a series of web browsers originally produced by Netscape Communications Corporation, now a subsidiary of AOL The original browser was once the dominant browser in terms of usage share, but as a result of the first browser war it lost virtually all of its share to Internet Explorer Netscape was discontinued and support for all Netscape browsers and client products was terminated on March 1, 2008 Netscape Navigator was the name of Netscape\u0027s web browser from versions 1.0 through 4.8 The first beta release versions of the browser were released in 1994 and known as Mosaic and then Mosaic Netscape until a legal challenge from the National Center for Supercomputing Applications (makers of NCSA Mosaic, which many of Netscape\u0027s founders used to develop), led to the name change to Netscape Navigator The company\u0027s name also changed from Mosaic Communications Corporation to Netscape Communications Corporation The browser was easily the most advanced...", type); doc.add(text); writer.addDocument(doc); FastVectorHighlighter highlighter = new FastVectorHighlighter(); IndexReader reader = DirectoryReader.open(writer, true); int docId = 0; String field = "text"; { // BooleanQuery把两个Term分别进行高亮,因为BooleanQuery无法表示两个Term之间的位置关系 BooleanQuery query = new BooleanQuery(); query.add(new TermQuery(new Term(field, "internet")), Occur.MUST); query.add(new TermQuery(new Term(field, "explorer")), Occur.MUST); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 128, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } { // 构造PhraseQuery时添加的两个Term之间是没有间隙,是连在一起的,且两者在原文中也是连在一起的, // 所以高亮时也是当作一个整体进行高亮的,这是普通高亮器实现不了的 PhraseQuery query = new PhraseQuery(); query.add(new Term(field, "internet")); query.add(new Term(field, "explorer")); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 128, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } reader.close(); writer.close(); dir.close(); } public static void testPhraseHighlightTest() throws IOException { Directory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig( new StandardAnalyzer())); Document doc = new Document(); FieldType type = new FieldType(TextField.TYPE_STORED); // //////////////////////////////////////////// // 因为FastVectorHightlighter高亮器就是依赖项向量来完成高亮功能的,所以下面的3项设置是必须的 type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true); // //////////////////////////////////////////// type.freeze(); Field longTermField = new Field( "long_term", "This is a test thisisaverylongwordandmakessurethisfails where foo is highlighed and should be highlighted", type); Field noLongTermField = new Field( "no_long_term", "This is a test where foo is highlighed and should be highlighted", type); doc.add(longTermField); doc.add(noLongTermField); writer.addDocument(doc); FastVectorHighlighter highlighter = new FastVectorHighlighter(); IndexReader reader = DirectoryReader.open(writer, true); int docId = 0; String field = "no_long_term"; { BooleanQuery query = new BooleanQuery(); query.add(new TermQuery(new Term(field, "test")), Occur.MUST); query.add(new TermQuery(new Term(field, "foo")), Occur.MUST); query.add(new TermQuery(new Term(field, "highlighed")), Occur.MUST); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } { BooleanQuery query = new BooleanQuery(); PhraseQuery pq = new PhraseQuery(); pq.add(new Term(field, "test")); pq.add(new Term(field, "foo")); pq.add(new Term(field, "highlighed")); pq.setSlop(5); query.add(new TermQuery(new Term(field, "foo")), Occur.MUST); query.add(pq, Occur.MUST); query.add(new TermQuery(new Term(field, "highlighed")), Occur.MUST); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); if (bestFragments.length > 0) { System.out.println(bestFragments[0]); } bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 30, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } { PhraseQuery query = new PhraseQuery(); query.add(new Term(field, "test")); query.add(new Term(field, "foo")); query.add(new Term(field, "highlighed")); query.setSlop(3); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 30, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } { PhraseQuery query = new PhraseQuery(); query.add(new Term(field, "test")); query.add(new Term(field, "foo")); query.add(new Term(field, "highlighted")); query.setSlop(30); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); } { BooleanQuery query = new BooleanQuery(); PhraseQuery pq = new PhraseQuery(); pq.add(new Term(field, "test")); pq.add(new Term(field, "foo")); pq.add(new Term(field, "highlighed")); pq.setSlop(5); BooleanQuery inner = new BooleanQuery(); inner.add(pq, Occur.MUST); inner.add(new TermQuery(new Term(field, "foo")), Occur.MUST); query.add(inner, Occur.MUST); query.add(pq, Occur.MUST); query.add(new TermQuery(new Term(field, "highlighed")), Occur.MUST); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 30, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } field = "long_term"; { BooleanQuery query = new BooleanQuery(); query.add(new TermQuery(new Term(field, "thisisaverylongwordandmakessurethisfails")), Occur.MUST); query.add(new TermQuery(new Term(field, "foo")), Occur.MUST); query.add(new TermQuery(new Term(field, "highlighed")), Occur.MUST); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); // 如果Term关键字自身长度就已经超过了设置的高亮段字符显示最大长度,则直接无视该设置,会完整显示该Term并加上高亮标签 String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, field, 18, 1); System.out.println(bestFragments.length); System.out.println(bestFragments[0]); } reader.close(); writer.close(); dir.close(); } public static void testBoostedPhraseHighlightTest() throws IOException { Directory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig( new StandardAnalyzer())); Document doc = new Document(); FieldType type = new FieldType(TextField.TYPE_STORED); type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true); type.freeze(); StringBuilder text = new StringBuilder(); text.append("words words junk junk junk junk junk junk junk junk highlight junk junk junk junk together junk "); for (int i = 0; i < 10; i++) { text.append("junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk "); } text.append("highlight words together "); for (int i = 0; i < 10; i++) { text.append("junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk junk "); } doc.add(new Field("text", text.toString().trim(), type)); writer.addDocument(doc); FastVectorHighlighter highlighter = new FastVectorHighlighter(); IndexReader reader = DirectoryReader.open(writer, true); BooleanQuery terms = new BooleanQuery(); terms.add(clause("text", "highlight"), Occur.MUST); terms.add(clause("text", "words"), Occur.MUST); terms.add(clause("text", "together"), Occur.MUST); BooleanQuery phrase = new BooleanQuery(); phrase.add(clause("text", "highlight", "words", "together"), Occur.MUST); phrase.setBoost(100); BooleanQuery query = new BooleanQuery(); query.add(terms, Occur.MUST); // 加上PhraseQuery就能将多个连在一起的Term一起高亮 query.add(phrase, Occur.SHOULD); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); String fragment = highlighter.getBestFragment(fieldQuery, reader, 0, "text", 10000); System.out.println(fragment); reader.close(); writer.close(); dir.close(); } public static void testFormater() throws IOException, ParseException { Directory dir = new RAMDirectory(); IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig( new StandardAnalyzer())); Document doc = new Document(); FieldType type = new FieldType(TextField.TYPE_STORED); type.setStoreTermVectorOffsets(true); type.setStoreTermVectorPositions(true); type.setStoreTermVectors(true); type.freeze(); Field field = new Field( "field", "This is a test where foo is highlighed&<underline> and should be \"highlighted\".", type); doc.add(field); writer.addDocument(doc); //自定义高亮标签,默认为<B></B> String[] preTags = new String[] { "<font color=\"#0000FF\">","<strong>" }; String[] postTags = new String[] { "</font>","</strong>" }; FragListBuilder fragListBuilder = new SimpleFragListBuilder(); FragmentsBuilder fragmentsBuilder = new ScoreOrderFragmentsBuilder(preTags,postTags); //创建快速高亮器 FastVectorHighlighter highlighter = new FastVectorHighlighter(true,true,fragListBuilder,fragmentsBuilder); // 特殊字符编码器 Encoder encoder = new SimpleHTMLEncoder(); IndexReader reader = DirectoryReader.open(writer, true); /*PhraseQuery query = new PhraseQuery(); query.add(new Term("field", "test")); query.add(new Term("field", "foo")); query.setSlop(2);*/ QueryParser queryParser = new QueryParser("field",new StandardAnalyzer()); Query query = queryParser.parse("test foo"); System.out.println(query.toString()); FieldQuery fieldQuery = highlighter.getFieldQuery(query, reader); int docId = 0; // matchedFields对哪些域进行高亮,添加多个域即可以对多个域进行高亮 Set<String> matchedFields = new HashSet<String>(); matchedFields.add("field"); String[] bestFragments = highlighter.getBestFragments(fieldQuery, reader, docId, "field", matchedFields, 100, 1, fragListBuilder, fragmentsBuilder, preTags, postTags, encoder); System.out.println(bestFragments[0]); reader.close(); writer.close(); dir.close(); } private static Query clause(String field, String... terms) { return clause(field, 1, terms); } private static Query clause(String field, float boost, String... terms) { Query q; if (terms.length == 1) { q = new TermQuery(new Term(field, terms[0])); } else { PhraseQuery pq = new PhraseQuery(); for (String term : terms) { pq.add(new Term(field, term)); } q = pq; } q.setBoost(boost); return q; } }
至于使用那种高亮器,请对这两种高亮器分别进行性能测试,用事实测试数据说话,不能简单说FastVectorHighlighter比Highlighter好或Highlighter比FastVectorHighlighter好,在特定的场景下表现良好才是真的好。OK,有关高亮器就说这么多了,如果有哪里说的不对或者有哪里没说到的地方,还望大家积极指正,互相交流互相学习共同进步!DEMO源码请在最底下的附近里下载。
对于最近有个很令人作呕的人(名字我就不公布了,给你留点面子)老是对我的博客文章进行恶意点“踩”,请问这位童鞋,你是妒忌呢还是出门忘吃药了?更恶心的是,居然经常在我博客下方评论里贴他的淘宝链接推广他那恶心的代码,未经过我允许就在我博客里推广你的淘宝就够恶心的了,居然卖的还是代码,还是请自重吧!
如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,
或者加裙一起交流学习!
相关推荐
chrome-headless-shell-win64-135.0.7004.0 (Canary).zip
建筑工地个人防护装备分割系统源码&数据集分享
车牌字符识别系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
实时可调的DDS信号发生器设计:基于FPGA的Verilog编程,灵活控制波形与频率的调制系统,(可实时切波形并控制频率)DDS信号发生器设计。 FPGA设计,verilog设计,DDS信号发生器:基于调制方式灵活可控的信号发生器DDS。 可生成ASK调制波、FSK调制波、正弦波、矩形波、三角波、锯齿波等多种波形。 方案采用三个按键控制,可按键控制不同信号、不同频率输出。 按键1:控制切六种波形(ASK调制波,FSK调制波,正弦波,矩形波,三角波,锯齿波)。 按键2:每按下一次,频率增加1 10。 按键3:每按下一次,频率减小1 10。 频率相位可灵活调整。 ,核心关键词: 1. DDS信号发生器设计 2. FPGA设计 3. Verilog设计 4. 调制方式 5. 波形切换 6. 频率控制 7. 按键控制 8. 频率相位调整 用分号分隔的关键词结果为:DDS信号发生器设计;FPGA设计;Verilog设计;调制方式;波形切换;频率控制;按键控制;频率相位调整;,灵活控制的FPGA设计:基于DDS技术的信号发生器实现方案
一个windows上使用的搜索小工具
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
标题:基于放射虫和硅藻的温度重建揭示南大洋印度洋扇区表面及亚表面温度变化 内容: 一项最新的研究在南大洋印度洋扇区的四个沉积物核心中提出了五项新的温度记录。这项研究使用了名为SORAD的数据集作为训练集,应用Imbrie和Kipp方法对经过对数转换的数据进行分析,重建了基于放射虫的亚表面温度(subST)。此外,通过利用Crosta等人于2020年编制的包含249个样本的数据集,并采用现代类似种技术应用于33个硅藻分类单元的相对丰度,预测了基于硅藻的海表温度(SST)。 特别地,来自DCR-1PC(Crosta等人,2020年)和MD19-3580(本研究)的核心SST记录以及来自MD19-3575CQ和MD19-3580(本研究)的核心subST记录被重采样至每3千年一个间隔,以便从SST中减去subST在这两个核心站点的值(DCR-1PC/MD19-3575CQ和MD19-3580),从而重建表面与亚表面之间温度差(deltaT)的演变过程。本研究包括了这些重采样数据和deltaT记录,为理解该地区过去气候条件提供了重要线索。
人脸表情行为识别系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
Zabbix 适用于各种规模的企业和组织,特别是那些需要监控复杂 IT 基础设施的用户。以下是一些使用 Zabbix 的优势: 开源免费:Zabbix 是开源软件,可以免费使用,且社区活跃,文档丰富。 高度可定制:用户可以根据需求自定义监控项、告警规则和报表。 跨平台支持:支持多种操作系统(Linux、Windows、Unix等)和数据库(MySQL、PostgreSQL、Oracle等)。 强大的社区支持:Zabbix 拥有庞大的用户社区,用户可以轻松找到解决方案和最佳实践。 企业级功能:支持高可用性、分布式监控和自动化运维,适合企业级应用。 该软件包使用rockylinux8、mysql、nginx。 软件使用:解压,createrepo创建本地yum仓库,创建仓库文件,可yum或dnf 下载,详细见:https://blog.csdn.net/m0_74744227/article/details/145389193
豆类苗叶检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
魔众视频管理系统是由国内领先的技术团队开发商进行开发的,是搭建视频系统最佳选择的程序源码,支持多端同步浏览。 魔众视频管理系统,轻量级视频管理系统。 2022年08月19日魔众视频管理系统发布v4.1.0版本,增加了以下27个特性: ·[新功能] 文件上传切片最大2M,分片文件根据配置动态清除 ·[新功能] 图标库中新增cube图标 ·[新功能] UEditorPlus升级到2.3.0 ·[新功能] bodyProperties可为body标签增加属性 ·[新功能] 图标库新增 check-simple ·[新功能] Cookie库新增属性参数 ·[新功能] 后台管理员角色新增备注字段 ·[新功能] Grid编辑操作名称可自定义 ·[新功能] FileUtil新增文件大小精简格式化 ·[新功能] 登录界面全新改版大气美观 ·[新功能] Type类型数据新增导出JS配置文件 ·[新功能] 富文本组件新增 editor-ready 自定义事件 ·[新功能] Response新增停止执行抛出消息的方法 ·[新功能] Grid新增批量弹窗快捷操作方式 ·[新功能] Json组件增加高度可配
永磁同步电机Q15焦点:无传感自适应滑膜算法及SMO_C语言定点代码与仿真模型示例解析,永磁同步电机无传感自适应滑模 滑膜观测器(SMO)_示例C语言定点代码和仿真模型,Q15 foc pmsm 使用“自适应”滑模算法消除一阶滤波器 ,核心关键词:永磁同步电机; 无传感自适应滑模; 滑膜观测器(SMO); C语言定点代码; Q15; foc pmsm; 自适应滑模算法; 一阶滤波器。 关键词用分号分隔为:永磁同步电机; 无传感; 自适应滑模; 滑膜观测器(SMO); C语言定点代码; Q15; foc pmsm; 一阶滤波器。,"自适应滑模算法在永磁同步电机中的应用:Q15定点代码与仿真模型示例"
Python泰勒图实现多模型可视化对比:源码高逼格版展示与使用教程,Python高逼格泰勒图及源码~,多模型对比~可视化工具 ,Python; 泰勒图; 高逼格; 多模型对比; 可视化工具,Python泰勒图高逼格展示:多模型对比可视化工具源码
基于WOA-XGBoost的回归预测:以优化XGBoost树参数的改进算法研究(基于MATLAB代码,包括评价指标),基于鲸鱼算法优化极限梯度提升树的数据回归预测(WOA-XGBoost) 鲸鱼算法WOA优化极限梯度提升树XGBoost树的数量、树的深度和学习率 基于MATLAB环境 替自己的数据即可 代码注释清晰 适合学习 回归预测的评价指标包括平均绝对误差 均方误差 均方根误差 平均绝对百分比误差以及关联系数 ,WOA-XGBoost; 鲸鱼算法优化; 树的数量; 树的深度; 学习率优化; MATLAB环境; 代码注释清晰; 回归预测; 评价指标,基于WOA-XGBoost算法的数据回归预测模型
介绍: 在2025年,随着技术的不断进步和用户需求的多样化,社区平台的设计与功能也在不断演变。StarFree作为一款全新的唯美风格社区源码,凭借其独特的设计理念和强大的功能,迅速成为了开发者与用户关注的焦点。本文将为您详细介绍StarFree源码的特点、功能以及应用场景。 1. 唯美设计,极致视觉体验 StarFree源码以“唯美”为核心设计理念,采用了简约而不失优雅的UI设计。整体界面以柔和的色调为主,搭配流畅的动画效果,为用户带来极致的视觉享受。无论是社区主页、个人中心,还是帖子详情页,每一个细节都经过精心打磨,确保用户在浏览时感受到舒适与愉悦。 2. 模块化架构,灵活扩展 StarFree采用模块化架构设计,开发者可以根据需求自由组合或扩展功能模块。无论是社交互动、内容发布,还是用户管理,每个模块都独立且高效,便于二次开发与定制。这种设计不仅降低了开发难度,还提高了系统的可维护性。 3. 多端适配,无缝体验 StarFree源码支持多端适配,包括Web端、移动端(iOS/Android)以及小程序。无论用户通过哪种设备访问社区,都能获得一致且流畅的体验。源码内置响应式布局
open3d qt6demo示例
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
二维码与条形码检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
内容: 该数据集由Moraitou-Apostolopoulou, M、Zervoudaki, S和Kapiris, K于2013年发布,提供了1994年9月和12月在爱琴海收集的翼足类(一种浮游软体动物)丰度数据。数据集包含了90个数据点,详细记录了这两个时间点翼足类的数量情况。此研究有助于了解气候变化对海洋生态系统中特定物种的影响。访问以下链接获取完整数据集: 请注意,直接点击上述链接将引导您至数据集的官方页面,而非直接下载页面。
1. 用户管理 用户注册与登录:支持志愿者(学生)、管理员(指导老师)等角色的注册与登录。 角色权限管理:根据不同角色分配相应的权限,如志愿者可以查看活动信息、报名,管理员可以管理活动及志愿者信息。 2. 志愿者信息管理 志愿者档案管理:记录志愿者的基本信息,包括姓名、学号、联系方式、专业等。 技能特长登记:志愿者可以添加自己的技能、特长,为活动匹配合适的志愿者。 3. 活动管理 活动信息发布:管理员可以创建、编辑和删除志愿者活动信息,包括活动名称、时间、地点、人数限制等。 活动查询与搜索:志愿者可以查看所有活动,支持按标签、时间等条件搜索活动信息。 4. 报名管理 在线报名:志愿者可在线报名参加活动,系统自动更新参与人员信息。 报名状态查询:志愿者可以查看自己报名的活动及其状态(已报名、待审核、已结束等)。 5. 反馈与评价 活动反馈:参与完活动后,志愿者可以填写反馈表,评价活动内容和组织。 统计分析:管理员可以对反馈进行统计分析,改进后续活动的安排。 6. 通知管理 消息推送:通过系统向志愿者发送活动提醒、重要通知等。 公告板:展示志愿者最新动态、活动报道等信息。 7. 系统管理