Lucene支持创建多个索引目录,同时存储多个索引。我们可能担心的问题是,在索引的过程中,分散地存储到多个索引目录中,是否在搜索时能够得到全局的相关度计算得分,其实Lucene的ParallelMultiSearcher和MultiSearcher支持全局得分的计算,也就是说,虽然索引分布在多个索引目录中,在搜索的时候还会将全部的索引数据聚合在一起进行查询匹配和得分计算。
索引目录处理
下面我们通过将索引随机地分布到以a~z的26个目录中,并实现一个索引和搜索的程序,来验证一下Lucene得分的计算。
首先,实现一个用来构建索引目录以及处理搜索的工具类,代码如下所示:
- package org.shirdrn.lucene;
- import java.io.File;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Random;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- import org.apache.lucene.index.CorruptIndexException;
- import org.apache.lucene.index.IndexWriter;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.apache.lucene.index.IndexWriter.MaxFieldLength;
- import org.apache.lucene.search.DefaultSimilarity;
- import org.apache.lucene.search.IndexSearcher;
- import org.apache.lucene.search.Searchable;
- import org.apache.lucene.search.Similarity;
- import org.apache.lucene.store.FSDirectory;
- import org.apache.lucene.store.LockObtainFailedException;
- import org.shirdrn.lucene.MultipleIndexing.IndexWriterObj;
- /**
- * Indexing accross multiple Lucene indexes.
- *
- * @author shirdrn
- * @date 2011-12-12
- */
- public class IndexHelper {
- private static WriterHelper writerHelper = null;
- private static SearcherHelper searcherHelper = null;
- public static WriterHelper newWriterHelper(String root, IndexWriterConfig indexConfig) {
- return WriterHelper.newInstance(root, indexConfig);
- }
- public static SearcherHelper newSearcherHelper(String root, IndexWriterConfig indexConfig) {
- return SearcherHelper.newInstance(root, indexConfig);
- }
- protected static class WriterHelper {
- private String alphabet = “abcdefghijklmnopqrstuvwxyz”;
- private Lock locker = new ReentrantLock();
- private String indexRootDir = null;
- private IndexWriterConfig indexConfig;
- private Map<Character, IndexWriterObj> indexWriters = new HashMap<Character, IndexWriterObj>();
- private static Random random = new Random();
- private WriterHelper() {
- }
- private synchronized static WriterHelper newInstance(String root, IndexWriterConfig indexConfig) {
- if(writerHelper==null) {
- writerHelper = new WriterHelper();
- writerHelper.indexRootDir = root;
- writerHelper.indexConfig = indexConfig;
- }
- return writerHelper;
- }
- public IndexWriterObj selectIndexWriter() {
- int pos = random.nextInt(alphabet.length());
- char ch = alphabet.charAt(pos);
- String dir = new String(new char[] {ch});
- locker.lock();
- try {
- File path = new File(indexRootDir, dir);
- if(!path.exists()) {
- path.mkdir();
- }
- if(!indexWriters.containsKey(ch)) {
- IndexWriter indexWriter = new IndexWriter(FSDirectory.open(path), indexConfig.getAnalyzer(), MaxFieldLength.UNLIMITED);
- indexWriters.put(ch, new IndexWriterObj(indexWriter, dir));
- }
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (LockObtainFailedException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- locker.unlock();
- }
- return indexWriters.get(ch);
- }
- @SuppressWarnings(“deprecation”)
- public void closeAll(boolean autoOptimize) {
- Iterator<Map.Entry<Character, IndexWriterObj>> iter = indexWriters.entrySet().iterator();
- while(iter.hasNext()) {
- Map.Entry<Character, IndexWriterObj> entry = iter.next();
- try {
- if(autoOptimize) {
- entry.getValue().indexWriter.optimize();
- }
- entry.getValue().indexWriter.close();
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- protected static class SearcherHelper {
- private List<IndexSearcher> searchers = new ArrayList<IndexSearcher>();
- private Similarity similarity = new DefaultSimilarity();
- private SearcherHelper() {
- }
- private synchronized static SearcherHelper newInstance(String root, IndexWriterConfig indexConfig) {
- if(searcherHelper==null) {
- searcherHelper = new SearcherHelper();
- if(indexConfig.getSimilarity()!=null) {
- searcherHelper.similarity = indexConfig.getSimilarity();
- }
- File indexRoot = new File(root);
- File[] files = indexRoot.listFiles();
- for(File f : files) {
- IndexSearcher searcher = null;
- try {
- searcher = new IndexSearcher(FSDirectory.open(f));
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if(searcher!=null) {
- searcher.setSimilarity(searcherHelper.similarity);
- searcherHelper.searchers.add(searcher);
- }
- }
- }
- return searcherHelper;
- }
- public void closeAll() {
- Iterator<IndexSearcher> iter = searchers.iterator();
- while(iter.hasNext()) {
- try {
- iter.next().close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public Searchable[] getSearchers() {
- Searchable[] a = new Searchable[searchers.size()];
- return searchers.toArray(a);
- }
- }
- }
在搜索的时候,通过IndexHelper.SearcherHelper工具来获取多个Searchable实例的数组,调用getSearchers()即可以获取到,提供给MultiSearcher构建搜索。
索引实现我们的数据源,选择根据指定的查询条件直接从MongoDB中读取,所以有关处理与MongoDB进行交互的代码都封装到了处理索引的代码中,通过内部类实现。我们看一下,执行索引数据的实现,代码如下所示:
- package org.shirdrn.lucene;
- import java.io.IOException;
- import java.io.Serializable;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.apache.lucene.document.Document;
- import org.apache.lucene.document.Field;
- import org.apache.lucene.document.Field.Index;
- import org.apache.lucene.document.Field.Store;
- import org.apache.lucene.index.CorruptIndexException;
- import org.apache.lucene.index.IndexWriter;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.shirdrn.lucene.IndexHelper.WriterHelper;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.mongodb.BasicDBObject;
- import com.mongodb.DBCollection;
- import com.mongodb.DBCursor;
- import com.mongodb.Mongo;
- import com.mongodb.MongoException;
- /**
- * Indexing accross multiple Lucene indexes.
- *
- * @author shirdrn
- * @date 2011-12-12
- */
- public class MultipleIndexing {
- private static Logger LOG = LoggerFactory.getLogger(MultipleIndexing.class);
- private DBCollection pageColletion;
- private WriterHelper writerHelper;
- private Map<IndexWriter, IntCounter> docCountPerIndexWriter = new HashMap<IndexWriter, IntCounter>();
- private int maxIndexCommitCount = 100;
- private AtomicInteger docCounter = new AtomicInteger();
- public MultipleIndexing(String indexRoot, int maxIndexCommitCount, MongoConfig mongoConfig, IndexWriterConfig indexConfig) {
- super();
- if (maxIndexCommitCount != 0) {
- this.maxIndexCommitCount = maxIndexCommitCount;
- }
- pageColletion = MongoHelper.newHelper(mongoConfig).getCollection(mongoConfig.collectionName);
- writerHelper = IndexHelper.newWriterHelper(indexRoot, indexConfig);
- }
- /**
- * Indexing
- * @param conditions
- */
- public void index(Map<String, Object> conditions) {
- DBCursor cursor = pageColletion.find(new BasicDBObject(conditions));
- try {
- while (cursor.hasNext()) {
- try {
- IndexWriterObj obj = writerHelper.selectIndexWriter();
- Document document = encapsulate(cursor.next().toMap(), obj.name);
- obj.indexWriter.addDocument(document);
- docCounter.addAndGet(1);
- LOG.info(“Global docCounter: “ + docCounter.get());
- increment(obj.indexWriter);
- checkCommit(obj.indexWriter);
- } catch (MongoException e) {
- e.printStackTrace();
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- finallyCommitAll();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- cursor.close();
- writerHelper.closeAll(true);
- LOG.info(“Close all indexWriters.”);
- }
- }
- private void finallyCommitAll() throws Exception {
- Iterator<IndexWriter> iter = docCountPerIndexWriter.keySet().iterator();
- while(iter.hasNext()) {
- iter.next().commit();
- }
- }
- private void checkCommit(IndexWriter indexWriter) throws Exception {
- if(docCountPerIndexWriter.get(indexWriter).value%maxIndexCommitCount==0) {
- indexWriter.commit();
- LOG.info(“Commit: “ + indexWriter + “, “ + docCountPerIndexWriter.get(indexWriter).value);
- }
- }
- private void increment(IndexWriter indexWriter) {
- IntCounter counter = docCountPerIndexWriter.get(indexWriter);
- if (counter == null) {
- counter = new IntCounter(1);
- docCountPerIndexWriter.put(indexWriter, counter);
- } else {
- ++counter.value;
- }
- }
- @SuppressWarnings(“unchecked”)
- private Document encapsulate(Map map, String path) {
- String title = (String) map.get(“title”);
- String content = (String) map.get(“content”);
- String url = (String) map.get(“url”);
- Document doc = new Document();
- doc.add(new Field(FieldName.TITLE, title, Store.YES, Index.ANALYZED_NO_NORMS));
- doc.add(new Field(FieldName.CONTENT, content, Store.NO, Index.ANALYZED));
- doc.add(new Field(FieldName.URL, url, Store.YES, Index.NOT_ANALYZED_NO_NORMS));
- doc.add(new Field(FieldName.PATH, path, Store.YES, Index.NOT_ANALYZED_NO_NORMS));
- return doc;
- }
- protected interface FieldName {
- public static final String TITLE = “title”;
- public static final String CONTENT = “content”;
- public static final String URL = “url”;
- public static final String PATH = “path”;
- }
- protected class IntCounter {
- public IntCounter(int value) {
- super();
- this.value = value;
- }
- private int value;
- }
- protected static class IndexWriterObj {
- IndexWriter indexWriter;
- String name;
- public IndexWriterObj(IndexWriter indexWriter, String name) {
- super();
- this.indexWriter = indexWriter;
- this.name = name;
- }
- @Override
- public String toString() {
- return “[“ + name + “]”;
- }
- }
- public static class MongoConfig implements Serializable {
- private static final long serialVersionUID = -3028092758346115702L;
- private String host;
- private int port;
- private String dbname;
- private String collectionName;
- public MongoConfig(String host, int port, String dbname, String collectionName) {
- super();
- this.host = host;
- this.port = port;
- this.dbname = dbname;
- this.collectionName = collectionName;
- }
- @Override
- public boolean equals(Object obj) {
- MongoConfig other = (MongoConfig) obj;
- return host.equals(other.host) && port == other.port && dbname.equals(other.dbname) && collectionName.equals(other.collectionName);
- }
- }
- protected static class MongoHelper {
- private static Mongo mongo;
- private static MongoHelper helper;
- private MongoConfig mongoConfig;
- private MongoHelper(MongoConfig mongoConfig) {
- super();
- this.mongoConfig = mongoConfig;
- }
- public synchronized static MongoHelper newHelper(MongoConfig mongoConfig) {
- try {
- if (helper == null) {
- helper = new MongoHelper(mongoConfig);
- mongo = new Mongo(mongoConfig.host, mongoConfig.port);
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- if (mongo != null) {
- mongo.close();
- }
- }
- });
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return helper;
- }
- public DBCollection getCollection(String collectionName) {
- DBCollection c = null;
- try {
- c = mongo.getDB(mongoConfig.dbname).getCollection(collectionName);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return c;
- }
- }
- }
另外需要说明一点,有关索引数据分布和更新的问题。基于上述随机选择索引目录,在一定程度上能够均匀地将数据分布到不同的目录中,但是在更新的时候,如果处理不当会造成数据的重复(因为随机),解决重复的方法就是在外部增加重复检测工作,限制将重复(非常相似)的文档再次进行索引。
下面我们看一下索引的测试用例,代码如下所示:
- package org.shirdrn.lucene;
- import java.util.HashMap;
- import java.util.Map;
- import junit.framework.TestCase;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.apache.lucene.index.IndexWriterConfig.OpenMode;
- import org.apache.lucene.util.Version;
- import org.shirdrn.lucene.MultipleIndexing.MongoConfig;
- public class TestMultipleIndexing extends TestCase {
- MultipleIndexing indexer;
- @Override
- protected void setUp() throws Exception {
- MongoConfig mongoConfig = new MongoConfig(“192.168.0.184”, 27017, “page”, “Article”);
- String indexRoot = “E:\\Store\\indexes”;
- int maxIndexCommitCount = 200;
- Analyzer a = new SmartChineseAnalyzer(Version.LUCENE_35, true);
- IndexWriterConfig indexConfig = new IndexWriterConfig(Version.LUCENE_35, a);
- indexConfig.setOpenMode(OpenMode.CREATE);
- indexer = new MultipleIndexing(indexRoot, maxIndexCommitCount, mongoConfig, indexConfig);
- }
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
- public void testIndexing() {
- Map<String, Object> conditions = new HashMap<String, Object>();
- conditions.put(“spiderName”, “sinaSpider”);
- indexer.index(conditions);
- }
- }
搜索实现
在搜索的时候,你可以选择ParallelMultiSearcher或MultiSearcher的任意一个,MultiSearcher是在搜索时候,通过一个循环来遍历多个索引获取到检索结果,而ParallelMultiSearcher则是启动多个线程并行执行搜索,使用它们的效率在不同配置的机器上效果是不同的,在实际使用的时候根据你的需要来决定。我简单地使用了MultiSearcher来构建搜索,实现代码如下所示:
- package org.shirdrn.lucene;
- import java.io.IOException;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.index.CorruptIndexException;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.apache.lucene.queryParser.ParseException;
- import org.apache.lucene.queryParser.QueryParser;
- import org.apache.lucene.search.MultiSearcher;
- import org.apache.lucene.search.Query;
- import org.apache.lucene.search.ScoreDoc;
- import org.apache.lucene.search.Searcher;
- import org.apache.lucene.search.TopScoreDocCollector;
- import org.apache.lucene.util.Version;
- import org.shirdrn.lucene.IndexHelper.SearcherHelper;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- /**
- * Searching accross multiple Lucene indexes.
- *
- * @author shirdrn
- * @date 2011-12-12
- */
- public class MultipleSearching {
- private static Logger LOG = LoggerFactory.getLogger(MultipleSearching.class);
- private SearcherHelper searcherHelper;
- private Searcher searcher;
- private QueryParser queryParser;
- private IndexWriterConfig indexConfig;
- private Query query;
- private ScoreDoc[] scoreDocs;
- public MultipleSearching(String indexRoot, IndexWriterConfig indexConfig) {
- searcherHelper = IndexHelper.newSearcherHelper(indexRoot, indexConfig);
- this.indexConfig = indexConfig;
- try {
- searcher = new MultiSearcher(searcherHelper.getSearchers());
- searcher.setSimilarity(indexConfig.getSimilarity());
- queryParser = new QueryParser(Version.LUCENE_35, “content”, indexConfig.getAnalyzer());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public void search(String queries) {
- try {
- query = queryParser.parse(queries);
- TopScoreDocCollector collector = TopScoreDocCollector.create(100000, true);
- searcher.search(query, collector);
- scoreDocs = collector.topDocs().scoreDocs;
- } catch (ParseException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public void iterateDocs(int start, int end) {
- for (int i = start; i < Math.min(scoreDocs.length, end); i++) {
- try {
- LOG.info(searcher.doc(scoreDocs[i].doc).toString());
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public void explain(int start, int end) {
- for (int i = start; i < Math.min(scoreDocs.length, end); i++) {
- try {
- System.out.println(searcher.explain(query, scoreDocs[i].doc));
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public void close() {
- searcherHelper.closeAll();
- try {
- searcher.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
下面给出搜索的测试用例,代码如下所示:
- package org.shirdrn.lucene;
- import org.apache.lucene.analysis.Analyzer;
- import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.apache.lucene.util.Version;
- import junit.framework.TestCase;
- public class TestMultipleSearching extends TestCase {
- MultipleSearching searcher;
- @Override
- protected void setUp() throws Exception {
- String indexRoot = “E:\\Store\\indexes”;
- Analyzer a = new SmartChineseAnalyzer(Version.LUCENE_35, true);
- IndexWriterConfig indexConfig = new IndexWriterConfig(Version.LUCENE_35, a);
- searcher = new MultipleSearching(indexRoot, indexConfig);
- }
- @Override
- protected void tearDown() throws Exception {
- searcher.close();
- }
- public void testSearching() {
- searcher.search(“+title:拉斯维加斯^1.25 (+content:美国^1.50 +content:拉斯维加斯)”);
- searcher.iterateDocs(0, 10);
- searcher.explain(0, 5);
- }
- }
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:全新体验 拉斯维加斯的完美24小时(组图)(4)_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2010-08-16/1400141443_4.shtml> stored,indexed,omitNorms<path:x>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:拉斯维加斯 触摸你的奢侈底线(组图)_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2009-05-21/095684952.shtml> stored,indexed,omitNorms<path:v>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:美国拉斯维加斯地图_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2008-08-20/113317460.shtml> stored,indexed,omitNorms<path:a>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:美国拉斯维加斯:潮野水上乐园_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2008-08-20/093217358.shtml> stored,indexed,omitNorms<path:e>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:美国拉斯维加斯:米高梅历险游乐园_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2008-08-20/095617381.shtml> stored,indexed,omitNorms<path:k>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:美国拉斯维加斯主要景点_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2008-08-20/114817479.shtml> stored,indexed,omitNorms<path:m>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:娱乐之都拉斯维加斯宣布在中国推旅游市场新战略_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/news/2008-11-19/094337435.shtml> stored,indexed,omitNorms<path:j>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:美国拉斯维加斯简介_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/world/2008-08-19/160017116.shtml> stored,indexed,omitNorms<path:v>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:拉斯维加斯“猫王模仿秀”亮相国际旅交会_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/news/2009-11-23/1004116788.shtml> stored,indexed,omitNorms<path:j>>
- 2011-12-13 12:12:55 org.shirdrn.lucene.MultipleSearching iterateDocs
- 信息: Document<stored,indexed,tokenized,omitNorms<title:10大美食家的饕餮名城:拉斯维加斯(图)(4)_新浪旅游_新浪网> stored,indexed,omitNorms<url:http://travel.sina.com.cn/food/2009-01-16/090855088.shtml> stored,indexed,omitNorms<path:s>>
通过path可以看到,搜索结果是将多个索引目录下的结果进行了聚合。
下面是搜索结果相关度得分情况,如下所示:
- 7.3240967 = (MATCH) sum of:
- 6.216492 = (MATCH) weight(title:拉斯维加斯^1.25 in 616), product of:
- 0.7747233 = queryWeight(title:拉斯维加斯^1.25), product of:
- 1.25 = boost
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 0.07723921 = queryNorm
- 8.024145 = (MATCH) fieldWeight(title:拉斯维加斯 in 616), product of:
- 1.0 = tf(termFreq(title:拉斯维加斯)=1)
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 1.0 = fieldNorm(field=title, doc=616)
- 1.1076047 = (MATCH) sum of:
- 0.24895692 = (MATCH) weight(content:美国^1.5 in 616), product of:
- 0.39020002 = queryWeight(content:美国^1.5), product of:
- 1.5 = boost
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.07723921 = queryNorm
- 0.63802385 = (MATCH) fieldWeight(content:美国 in 616), product of:
- 1.7320508 = tf(termFreq(content:美国)=3)
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.109375 = fieldNorm(field=content, doc=616)
- 0.8586478 = (MATCH) weight(content:拉斯维加斯 in 616), product of:
- 0.49754182 = queryWeight(content:拉斯维加斯), product of:
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.07723921 = queryNorm
- 1.7257802 = (MATCH) fieldWeight(content:拉斯维加斯 in 616), product of:
- 2.4494898 = tf(termFreq(content:拉斯维加斯)=6)
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.109375 = fieldNorm(field=content, doc=616)
- 7.2405667 = (MATCH) sum of:
- 6.216492 = (MATCH) weight(title:拉斯维加斯^1.25 in 2850), product of:
- 0.7747233 = queryWeight(title:拉斯维加斯^1.25), product of:
- 1.25 = boost
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 0.07723921 = queryNorm
- 8.024145 = (MATCH) fieldWeight(title:拉斯维加斯 in 2850), product of:
- 1.0 = tf(termFreq(title:拉斯维加斯)=1)
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 1.0 = fieldNorm(field=title, doc=2850)
- 1.0240744 = (MATCH) sum of:
- 0.17423354 = (MATCH) weight(content:美国^1.5 in 2850), product of:
- 0.39020002 = queryWeight(content:美国^1.5), product of:
- 1.5 = boost
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.07723921 = queryNorm
- 0.44652367 = (MATCH) fieldWeight(content:美国 in 2850), product of:
- 1.4142135 = tf(termFreq(content:美国)=2)
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.09375 = fieldNorm(field=content, doc=2850)
- 0.8498409 = (MATCH) weight(content:拉斯维加斯 in 2850), product of:
- 0.49754182 = queryWeight(content:拉斯维加斯), product of:
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.07723921 = queryNorm
- 1.7080793 = (MATCH) fieldWeight(content:拉斯维加斯 in 2850), product of:
- 2.828427 = tf(termFreq(content:拉斯维加斯)=8)
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.09375 = fieldNorm(field=content, doc=2850)
- 7.1128473 = (MATCH) sum of:
- 6.216492 = (MATCH) weight(title:拉斯维加斯^1.25 in 63), product of:
- 0.7747233 = queryWeight(title:拉斯维加斯^1.25), product of:
- 1.25 = boost
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 0.07723921 = queryNorm
- 8.024145 = (MATCH) fieldWeight(title:拉斯维加斯 in 63), product of:
- 1.0 = tf(termFreq(title:拉斯维加斯)=1)
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 1.0 = fieldNorm(field=title, doc=63)
- 0.896355 = (MATCH) sum of:
- 0.1451946 = (MATCH) weight(content:美国^1.5 in 63), product of:
- 0.39020002 = queryWeight(content:美国^1.5), product of:
- 1.5 = boost
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.07723921 = queryNorm
- 0.37210304 = (MATCH) fieldWeight(content:美国 in 63), product of:
- 1.4142135 = tf(termFreq(content:美国)=2)
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=63)
- 0.7511604 = (MATCH) weight(content:拉斯维加斯 in 63), product of:
- 0.49754182 = queryWeight(content:拉斯维加斯), product of:
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.07723921 = queryNorm
- 1.5097432 = (MATCH) fieldWeight(content:拉斯维加斯 in 63), product of:
- 3.0 = tf(termFreq(content:拉斯维加斯)=9)
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=63)
- 7.1128473 = (MATCH) sum of:
- 6.216492 = (MATCH) weight(title:拉斯维加斯^1.25 in 2910), product of:
- 0.7747233 = queryWeight(title:拉斯维加斯^1.25), product of:
- 1.25 = boost
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 0.07723921 = queryNorm
- 8.024145 = (MATCH) fieldWeight(title:拉斯维加斯 in 2910), product of:
- 1.0 = tf(termFreq(title:拉斯维加斯)=1)
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 1.0 = fieldNorm(field=title, doc=2910)
- 0.896355 = (MATCH) sum of:
- 0.1451946 = (MATCH) weight(content:美国^1.5 in 2910), product of:
- 0.39020002 = queryWeight(content:美国^1.5), product of:
- 1.5 = boost
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.07723921 = queryNorm
- 0.37210304 = (MATCH) fieldWeight(content:美国 in 2910), product of:
- 1.4142135 = tf(termFreq(content:美国)=2)
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=2910)
- 0.7511604 = (MATCH) weight(content:拉斯维加斯 in 2910), product of:
- 0.49754182 = queryWeight(content:拉斯维加斯), product of:
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.07723921 = queryNorm
- 1.5097432 = (MATCH) fieldWeight(content:拉斯维加斯 in 2910), product of:
- 3.0 = tf(termFreq(content:拉斯维加斯)=9)
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=2910)
- 7.1128473 = (MATCH) sum of:
- 6.216492 = (MATCH) weight(title:拉斯维加斯^1.25 in 2920), product of:
- 0.7747233 = queryWeight(title:拉斯维加斯^1.25), product of:
- 1.25 = boost
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 0.07723921 = queryNorm
- 8.024145 = (MATCH) fieldWeight(title:拉斯维加斯 in 2920), product of:
- 1.0 = tf(termFreq(title:拉斯维加斯)=1)
- 8.024145 = idf(docFreq=82, maxDocs=93245)
- 1.0 = fieldNorm(field=title, doc=2920)
- 0.896355 = (MATCH) sum of:
- 0.1451946 = (MATCH) weight(content:美国^1.5 in 2920), product of:
- 0.39020002 = queryWeight(content:美国^1.5), product of:
- 1.5 = boost
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.07723921 = queryNorm
- 0.37210304 = (MATCH) fieldWeight(content:美国 in 2920), product of:
- 1.4142135 = tf(termFreq(content:美国)=2)
- 3.3678925 = idf(docFreq=8734, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=2920)
- 0.7511604 = (MATCH) weight(content:拉斯维加斯 in 2920), product of:
- 0.49754182 = queryWeight(content:拉斯维加斯), product of:
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.07723921 = queryNorm
- 1.5097432 = (MATCH) fieldWeight(content:拉斯维加斯 in 2920), product of:
- 3.0 = tf(termFreq(content:拉斯维加斯)=9)
- 6.4415708 = idf(docFreq=403, maxDocs=93245)
- 0.078125 = fieldNorm(field=content, doc=2920)
可见,搜索结果相关度得分,是基于全部的多个索引来计算的(maxDocs=93245)。