Lucene - 按另一个字段排序搜索,然后返回辅助字段

时间:2015-02-19 10:11:45

标签: lucene

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

  • 标题
  • 摘要
  • 普及

如果有人按照" ga"进行搜索,我会搜索标题以获得部分匹配(例如"游戏"),然后按人气排序这些结果。

如果有< 10结果,我想回归总结。但是,我希望摘要匹配比任何标题匹配更低,再次按人气排序

E.g。搜索" 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搜索的简单实现,如果有< 10个结果,对概要进行第二次搜索 - 然后我在语法上合并结果。然而,这并不理想,因为我需要解决重复问题,并且分页不会很好 - 如果可能的话,最好全部在1中完成。

这是可能的,如果可以,怎么样?

(我目前正在使用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)
}

1 个答案:

答案 0 :(得分:1)

您可以先按分数排序,然后按人气排序,并对标题查询进行大幅提升。只要匹配标题的所有字段的分数相等,只有匹配摘要的文档的分数相等,这样做才有效:

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

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

所以,你需要一种方法来简化评分,并防止所有花哨的lucene相关性评分逻辑得到解决方案。您可以使用ConstantScoreQueryDisjunctionMaxQuery来获得所需的分数。

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);

对于您提供的代码,这将起作用,因为除了构造prefixquery之外,您不需要查询解析器。你也可以保留解析器。 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 + "*"));