Search by EGF PIN prefix

This commit is contained in:
Claude Brisson
2024-04-14 10:31:05 +02:00
parent 02c6730336
commit 52d73ac9c2
2 changed files with 60 additions and 42 deletions

View File

@@ -1,7 +1,6 @@
package org.jeudego.pairgoth.ratings package org.jeudego.pairgoth.ratings
import com.republicate.kson.Json import com.republicate.kson.Json
import org.apache.lucene.analysis.LowerCaseFilter
import org.apache.lucene.analysis.standard.StandardAnalyzer import org.apache.lucene.analysis.standard.StandardAnalyzer
import org.apache.lucene.document.Document import org.apache.lucene.document.Document
import org.apache.lucene.document.Field import org.apache.lucene.document.Field
@@ -15,8 +14,8 @@ import org.apache.lucene.index.Term
import org.apache.lucene.queryparser.complexPhrase.ComplexPhraseQueryParser import org.apache.lucene.queryparser.complexPhrase.ComplexPhraseQueryParser
import org.apache.lucene.search.BooleanClause import org.apache.lucene.search.BooleanClause
import org.apache.lucene.search.BooleanQuery import org.apache.lucene.search.BooleanQuery
import org.apache.lucene.search.FuzzyQuery
import org.apache.lucene.search.IndexSearcher import org.apache.lucene.search.IndexSearcher
import org.apache.lucene.search.PrefixQuery
import org.apache.lucene.search.TermQuery import org.apache.lucene.search.TermQuery
import org.apache.lucene.store.ByteBuffersDirectory import org.apache.lucene.store.ByteBuffersDirectory
import org.apache.lucene.store.Directory import org.apache.lucene.store.Directory
@@ -32,6 +31,7 @@ class PlayerIndex {
val FIRSTNAME = "firstname" val FIRSTNAME = "firstname"
val TEXT = "text" val TEXT = "text"
val COUNTRY = "country" val COUNTRY = "country"
val PIN = "egf"
val MAX_HITS = 20 val MAX_HITS = 20
val logger = LoggerFactory.getLogger("index") val logger = LoggerFactory.getLogger("index")
@@ -60,6 +60,9 @@ class PlayerIndex {
doc.add(StringField(ORIGIN, player.field(ORIGIN).lowercase(Locale.ROOT), Field.Store.NO)) doc.add(StringField(ORIGIN, player.field(ORIGIN).lowercase(Locale.ROOT), Field.Store.NO))
doc.add(StringField(COUNTRY, player.field(COUNTRY).lowercase(Locale.ROOT), Field.Store.NO)) doc.add(StringField(COUNTRY, player.field(COUNTRY).lowercase(Locale.ROOT), Field.Store.NO))
doc.add(TextField(TEXT, "${player.field(NAME)} ${player.nullableField(FIRSTNAME)}", Field.Store.NO)) doc.add(TextField(TEXT, "${player.field(NAME)} ${player.nullableField(FIRSTNAME)}", Field.Store.NO))
player.getString(PIN)?.let {
doc.add(StringField(PIN, it, Field.Store.NO))
}
writer.addDocument(doc); writer.addDocument(doc);
++count ++count
} }
@@ -68,49 +71,64 @@ class PlayerIndex {
} }
fun match(needle: String, origins: Int, country: String?): List<Int> { fun match(needle: String, origins: Int, country: String?): List<Int> {
val terms = needle.lowercase(Locale.ROOT) if (needle.trim().matches(Regex("\\d+"))) {
.replace(Regex("([+&|!(){}\\[\\]^\\\\\"~*?:/]|(?<!\\b)-)"), "") // PIN search
.split(Regex("[ -_']+")) val pin = needle.trim().toInt()
.filter { !it.isEmpty() } val query = PrefixQuery(Term(PIN, needle.trim()))
.map { "$it~" } val docs = searcher.search(query, MAX_HITS)
.joinToString(" ") return docs.scoreDocs.map { searcher.doc(it.doc).getField(ID).numericValue().toInt() }.toList()
.let { if (it.isEmpty()) it else "$it ${it.substring(0, it.length - 1) + "*^4"}" } } else {
if (terms.isEmpty()) return emptyList() val terms = needle.lowercase(Locale.ROOT)
logger.info("Search query: $terms") .replace(Regex("([+&|!(){}\\[\\]^\\\\\"~*?:/]|(?<!\\b)-)"), "")
val fuzzy = queryParser.parse(terms) .split(Regex("[ -_']+"))
val activeMask = RatingsManager.activeMask() .filter { !it.isEmpty() }
val query = when (origins.countOneBits()) { .map { "$it~" }
0 -> return emptyList() .joinToString(" ")
1 -> { .let { if (it.isEmpty()) it else "$it ${it.substring(0, it.length - 1) + "*^4"}" }
val filter = TermQuery(Term(ORIGIN, RatingsManager.Ratings.codeOf(origins))) if (terms.isEmpty()) return emptyList()
BooleanQuery.Builder() logger.info("Search query: $terms")
.add(fuzzy, BooleanClause.Occur.SHOULD) val fuzzy = queryParser.parse(terms)
.add(filter, BooleanClause.Occur.FILTER) val activeMask = RatingsManager.activeMask()
.build() val query = when (origins.countOneBits()) {
} 0 -> return emptyList()
2 -> { 1 -> {
if (activeMask.countOneBits() > 2) { val filter = TermQuery(Term(ORIGIN, RatingsManager.Ratings.codeOf(origins)))
val filter =
TermQuery(Term(ORIGIN, RatingsManager.Ratings.codeOf((origins xor activeMask) and activeMask)))
BooleanQuery.Builder() BooleanQuery.Builder()
.add(fuzzy, BooleanClause.Occur.SHOULD) .add(fuzzy, BooleanClause.Occur.SHOULD)
.add(filter, BooleanClause.Occur.MUST_NOT) .add(filter, BooleanClause.Occur.FILTER)
.build() .build()
} else fuzzy }
}
3 -> fuzzy 2 -> {
else -> throw Error("wrong origins mask") if (activeMask.countOneBits() > 2) {
}.let { val filter =
if (country == null) it TermQuery(
else { Term(
val countryFilter = TermQuery(Term(COUNTRY, country.lowercase(Locale.ROOT))) ORIGIN,
BooleanQuery.Builder() RatingsManager.Ratings.codeOf((origins xor activeMask) and activeMask)
.add(it, BooleanClause.Occur.SHOULD) )
.add(countryFilter, BooleanClause.Occur.FILTER) )
.build() BooleanQuery.Builder()
.add(fuzzy, BooleanClause.Occur.SHOULD)
.add(filter, BooleanClause.Occur.MUST_NOT)
.build()
} else fuzzy
}
3 -> fuzzy
else -> throw Error("wrong origins mask")
}.let {
if (country == null) it
else {
val countryFilter = TermQuery(Term(COUNTRY, country.lowercase(Locale.ROOT)))
BooleanQuery.Builder()
.add(it, BooleanClause.Occur.SHOULD)
.add(countryFilter, BooleanClause.Occur.FILTER)
.build()
}
} }
val docs = searcher.search(query, MAX_HITS)
return docs.scoreDocs.map { searcher.doc(it.doc).getField(ID).numericValue().toInt() }.toList()
} }
val docs = searcher.search(query, MAX_HITS)
return docs.scoreDocs.map { searcher.doc(it.doc).getField(ID).numericValue().toInt() }.toList()
} }
} }

View File

@@ -302,7 +302,7 @@
#[[ #[[
<script id="result" type="text/template"> <script id="result" type="text/template">
{{for #data}} {{for #data}}
<div class="result-line" data-index="{{:#getIndex()}}" data-name="{{:name}}">[{{:origin}}] {{:country}} - {{:name}} {{:firstname}} {{:rank}} ({{:club}}) {{if country === 'FR' && license !== 'L'}}<span class="darkred">non licencié</span>{{/if}}</div> <div class="result-line" data-index="{{:#getIndex()}}" data-name="{{:name}}">[{{:origin}}{{if origin === 'EGF'}} {{:egf}}{{/if}}] {{:country}} - {{:name}} {{:firstname}} {{:rank}} ({{:club}}) {{if country === 'FR' && license !== 'L'}}<span class="darkred">non licencié</span>{{/if}}</div>
{{/for}} {{/for}}
</script> </script>
<script type="text/javascript" src="/lib/jsrender-1.0.13/jsrender.min.js"></script> <script type="text/javascript" src="/lib/jsrender-1.0.13/jsrender.min.js"></script>