【问题标题】:Lucene - Search by a field sorting by another, falling back to a secondary fieldLucene - 按另一个字段排序的字段搜索,回退到辅助字段
【发布时间】:2015-02-19 10:11:45
【问题描述】:

我希望使用以下字段开发一个简单的搜索

  • 标题
  • 总结
  • 人气

如果有人搜索“ga”,我会搜索标题中的部分匹配项(例如“The Game”),然后按受欢迎程度对这些结果进行排序。

如果有

例如搜索“ga*”

"The Game"      | "About stuff"   | popularity = 3  | (title match)
"Gant charts"   | "great stats"   | popularity = 7  | (title match)
"Some Title"    | "mind the gap"  | popularity = 1  | (summary match)
"Another Title" | "blah games"    | popularity = 5  | (summary match)

我编写了一个简单的实现,它执行 1 次 Lucene 搜索,如果有

这可能吗?如果可以,怎么做?

(我目前正在使用 Java Lucene jar 进行开发)

这是我目前的尝试(用 Scala 编写)

// Creating the indexes
private def addDoc(w:IndexWriter , clientContent: ClientContent, contentType:String):Unit ={
  val doc:Document = new Document()
  doc.add(new TextField("title", clientContent.title, Field.Store.YES))
  doc.add(new TextField("synopsis", clientContent.synopsis, Field.Store.YES))
  doc.add(new StringField("id", clientContent.id, Field.Store.YES))
  doc.add(new IntField("popularity", 100000 - clientContent.popularity.day, Field.Store.YES))
  doc.add(new StringField("contentType", contentType, Field.Store.YES))
  w.addDocument(doc);
}

def createIndex: Unit = {
  index = new RAMDirectory()

  val analyzer = new StandardAnalyzer(Version.LUCENE_43)
  val config = new IndexWriterConfig(Version.LUCENE_43, analyzer)
  val w = new IndexWriter(index, config)

  clientApplication.shows.list.map {addDoc(w, _, "Show")}

  w.close()
  reader = IndexReader.open(index)
  searcher = new IndexSearcher(reader)
}


// Searching by one field 
def dataSearch(queryString: String, section:String, limit:Int):Array[ScoreDoc] = {

  val collector = TopFieldCollector.create(
  new Sort(new SortField("popularity", SortField.Type.INT, true)),
    limit,false,true, true, false);       

  val analyzer = new StandardAnalyzer(Version.LUCENE_43)
  val q = new QueryParser(Version.LUCENE_43, section, analyzer).parse(queryString+"*")
  searcher.search(q, collector)
  collector.topDocs().scoreDocs
}

// Searching for a query 
def search(queryString:String) = {
  println(s"search $queryString")

  val titleResults = dataSearch(queryString, "title", limit)

  if (titleResults.length < limit) {
    val synopsisResults = dataSearch(queryString, "synopsis", limit - titleResults.length)  
    createModel(titleResults  ++ synopsisResults)
  }
  else
    createModel(titleResults)
}

【问题讨论】:

  • 那么,让我们看看到目前为止的尝试
  • 我的第一次尝试添加了 - 它只是原型代码,所以它可能不是最好的方法

标签: lucene


【解决方案1】:

您可以先按分数排序,然后按人气排序,并大大提高标题查询。只要与标题匹配的所有字段的分数相等,并且仅与摘要匹配的文档的分数相等,就可以这样做:

Sort mySort = new Sort(SortField.FIELD_SCORE, new SortField("popularity", SortField.Type.INT, true));

当然,他们可能不会平等。只要提升足够大, idf 应该不是问题,但是...如果不同文档的字段长度不同,则 lengthNorm 将使分数不相等,除非您禁用了规范。坐标因素会导致问题,因为匹配 both 字段的文档将比仅匹配标题的文档得分更高。如果一个匹配的词在一个字段中出现了不止一次,那么 tf 就会明显不同。

因此,您需要一种方法来简化评分,并防止所有花哨的 lucene 相关性评分逻辑失灵。您可以使用ConstantScoreQuery 和DisjunctionMaxQuery 获取分数以做您想做的事情。

Query titleQuery = new ConstantScoreQuery(new PrefixQuery(new Term("title", queryString)));
titleQuery.setBoost(2);
Query summaryQuery = new ConstantScoreQuery(new PrefixQuery(new Term("title", queryString)));
//Combine with a dismax, so matching both fields won't get a higher score than just the title
Query finalQuery = new DisjnctionMaxQuery(0);
finalQuery.add(titleQuery);
finalQuery.add(summaryQuery);

Sort mySort = new Sort(
    SortField.FIELD_SCORE, 
    new SortField("popularity", SortField.Type.INT, true)
);

val collector = TopFieldCollector.create(mySort,limit,false,true,true,false);

searcher.search(finalQuery, collector);

对于您提供的代码,这将起作用,因为除了构造前缀查询之外,您实际上并不需要查询解析器。不过,您也可以保留解析器。 ConstantScoreQuery 是一个包装查询。您可以同样轻松地包装从 QueryParser.parse 返回的查询:

QueryParser parser = new QueryParser(Version.LUCENE_43, "title", analyzer);

Query titleQuery = new ConstantScoreQuery(parser.parse(queryString + "*"));
titleQuery.SetBoost(2);
Query summaryQuery = new ConstantScoreQuery(parser.parse("summary:" + queryString + "*"));

【讨论】:

  • 感谢您的全面回答!这听起来正是我所需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
相关资源
最近更新 更多