I´m programming a web application with Java EE, Hibernate Search, JPA and JSF.
I have been reading the hibernate Search documantation over and over again, but I just can´t get faceting to work properly.
I have a database which includes several categorys. I made an example with football clubs.
I have the category Germany, which has the subclases Bundesliga, 2. Bundesliga and so on.
I have also a category called ChampionsLeague, EuroLeague and some other leagues representing different countries.
If I search for "Deutschland" Hibernate Search gives me the correct list of all Football clubs playing in Germany. Some of the football clubs participate in the ChampionsLeague and Euroleage. The Faceting in my left navigation bar gives me the categories in which the german clubs take part in. Also it displays the correct facetedCount.
The problem is, that if I click on one of the categories, Hibernate Search displays me all of the clubs in this category not only the german Clubs, which I have searched for on my initial search.
Can anybody tell me how to fix this problem?
Here is my code:
SearchBean:
public void startKeywordSearch(){
fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Company.class).get();
query = qb
.keyword()
.fuzzy().withEditDistanceUpTo(1).withPrefixLength(0)
.onFields("companyName", "companyShortDescription", "companyLongDescription", "categoryList.categoryName", "and so on")
.matching(keyword)
.createQuery();
categoryNameFacetingRequest = qb.facet()
.name("categoryNameFacet")
.onField("categoryList.categoryName_forFaceting")
.discrete()
.orderedBy(FacetSortOrder.COUNT_DESC)
.includeZeroCounts(false)
.maxFacetCount(100)
.createFacetingRequest();
persistenceQuery = fullTextEntityManager.createFullTextQuery(query, Company.class);
facetManager = fullTextEntityManager.createFullTextQuery(query, Company.class).getFacetManager();
facetManager.enableFaceting(categoryNameFacetingRequest);
result = persistenceQuery.getResultList();
facetResults = facetManager.getFacets("categoryNameFacet");
searchCount = result.size();
Here is my Code for the addFacet Method:
public void addFacet(Facet facet) {
fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
Query luceneQuery = facet.getFacetQuery();
persistenceQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, Company.class);
facetManager.enableFaceting(categoryNameFacetingRequest);
result = persistenceQuery.getResultList();
facetResults = facetManager.getFacets("categoryNameFacet");
FacetSelection facetSelection = facetManager.getFacetGroup("categoryNameFacet");
facetSelection.selectFacets(facet);
result = persistenceQuery.getResultList();
And thats the Code for generating my Link:
<div>
<h:form id="facetForm">
<ul>
<ui:repeat value="#{searchBean.facetResults}" var="facet">
<li><h:commandLink value="#{facet.value}" action="#{searchBean.addFacet(facet)}">
<f:ajax render="#all" />
</h:commandLink> (#{facet.count})</li>
</ui:repeat>
</ul>
</h:form>
</div>
Not sure where exactly you call addFacet, but it looks like you just run the query provided by facet#getFacetQuery. This won't work. The facet query is supposed to be applied on top of the existing query. Either via a boolean query or via a FacetSelection (which acts on top of the original query). The documentation has an example for that:
// create a fulltext query
Query luceneQuery = builder.all().createQuery(); // match all query
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, clazz );
// retrieve facet manager and apply faceting request
FacetManager facetManager = fullTextQuery.getFacetManager();
facetManager.enableFaceting( priceFacetingRequest );
// get the list of Cd
List<Cd> cds = fullTextQuery.list();
assertTrue(cds.size() == 10);
// retrieve the faceting results
List<Facet> facets = facetManager.getFacets( "priceFaceting" );
assertTrue(facets.get(0).getCount() == 2)
// apply first facet as additional search criteria
FacetSelection facetSelection = facetManager.getFacetGroup( "priceFaceting" );
facetSelection.selectFacets( facets.get( 0 ) );
// re-execute the query
cds = fullTextQuery.list();
assertTrue(cds.size() == 2);
See http://docs.jboss.org/hibernate/search/5.3/reference/en-US/html_single/#_restricting_query_results
Related
Given we have following documents in database
{id:0, SID:0, STATUS:"UNDONE", UID:1, AT:122}
{id:1, SID:1, STATUS:"DONE", UID:1, AT:123}
{id:2, SID:1, STATUS:"DONE", UID:2, AT:124}
{id:3, SID:2, STATUS:"PEN", UID:3, AT:125}
{id:4, SID:2, STATUS:"PEN", UID:4, AT:126}
{id:5, SID:3, STATUS:"DONE", UID:5, AT:127}
{id:6, SID:4, STATUS:"DONE", UID:6, AT:128}
I'm trying to get write a query using spring data mongodb to skip 'm' distinct SID and get next 'n' distinct SID (Sorted by AT field) matching the filter and return documents corresponding to those SIDs.
For this example m = 1 and n = 2 would return these documents
{id:3, SID:2, STATUS:"PEN", UID:3, AT:125}
{id:4, SID:2, STATUS:"PEN", UID:4, AT:126}
{id:5, SID:3, STATUS:"DONE", UID:5, AT:127}
So far I've managed to write this.
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.project("STATUS", "AT", "SID"),
Aggregation.match(Criteria.where("STATUS").in("DONE", "PEN")),
Aggregation.sort(Sort.Direction.DESC, "AT"),
Aggregation.group("SID"),
Aggregation.skip(skip),
Aggregation.limit(limit));
AggregationResults<Map> results = mongoOperations.aggregate(aggregation, MyClass.class, Map.class);
List<String> SIDs = results.getMappedResults().stream().map(it -> it.get("_id").toString()).collect(Collectors.toList());
Query query = new Query();
query.addCriteria(Criteria.where("SID").in(SIDs));
return mongoOperations.find(query, MyClass.class);
This is returning unpredictable results ie every call returns different results and therefore not sorted as intended.
What am I missing here?
I've a feeling the sort stage in pipeline is incorrectly placed.
I'm using JCypher 4.2.0 with Neo4J Server 4.0.2 with the builtin Movie graph.
I'm trying to unite couple simple, independent queries to one bigger for my needs.
first query: MATCH (people:Person) RETURN people.name, second query: MATCH (m:Movie) WHERE m.title = "Apollo 13" RETURN m.title.
and in JCypher:
JcNode
people = new JcNode("people"),
m = new JcNode("m");
JcQuery
query1 = new JcQuery(new IClause[]{
MATCH.node(people).label("Person"),
RETURN.value(people.property("name"))
}),
query2 = new JcQuery(new IClause[]{
MATCH.node(m).label("Movie"),
WHERE.valueOf(m.property("title")).EQUALS("Apollo 13")
RETURN.value(m.property("title"))
});
Of course, m.title should return only 1 result.
is it possible?
I have observed an odd behaviour but I don't see what I am doing wrong.
I created via multiple BooleanQueries the following query:
+(-(Request.zipCode:18055 Request.zipCode:33333 Request.zipCode:99999) +Request.zipCode:[* TO *]) *:*
...this is what I get via toString
Update: this way I created a part of the BooleanQuery which is responsible to create this snippet +Request.zipCode:[* TO *])
Query fieldOccursQuery = new TermQuery(new Term(queryFieldName, "[* TO *]"));
I have created exaclty same (per my understanding) Query via QueryParser like this:
String querystr = "+(-(Request.zipCode:18055 Request.zipCode:33333 Request.zipCode:99999) +Request.zipCode:[* TO *]) *:*";
Query query = new QueryParser(Version.LUCENE_46, "title", LuceneServiceI.analyzer).parse(querystr);
I processed both of them the same way like this:
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
int max = reader.maxDoc();
TopScoreDocCollector collector = TopScoreDocCollector.create(max > 0 ? max : 1, true);
searcher.search(query, collector);
....
ScoreDoc[] hits = collector.topDocs().scoreDocs;
Map<Integer, Document> docMap = new TreeMap<Integer, Document>();
for (int i = 0; i < hits.length; i++) {
docMap.put(hits[i].doc, indexSearcher.doc(hits[i].doc));
}
Different results
On a index like: stored,indexed,tokenized,omitNorms,indexOptions=DOCS_ONLY<Request.zipCode:04103>
The Query via QueryParser deliver one document as expected
The Query via BooleanQuery does not deliver 1 expected document
Questions
Are there possibilities that both same queries deliver different results? Set certain attributes to my BooleanQuery etc.
How can I get the same wanted result for BooleanQuery?
I could not found anything about differences only in concern of performance (http://www.gossamer-threads.com/lists/lucene/java-user/144374)
I found the solution to my problem.
Instead of creating this for the BooleanQuery:
Query fieldOccursQuery = new TermQuery(new Term(queryFieldName, "[* TO *]"));
I used this:
ConstantScoreQuery constantScoreQuery = new ConstantScoreQuery(new FieldValueFilter(queryFieldName));
query.add(constantScoreQuery, Occur.MUST);
Now my query looks different but I only get documents with fields with my queryFieldName.
Issue seems to be the leading wildcard in my first solution:
Find all Lucene documents having a certain field
I have a query which selects persons from a table.
SelectConditionStep<PersonRecord> select = context
.selectFrom(Tables.PERSON)
.where(Tables.PERSON.ISDELETED.eq(false));
if(searchValue != null && searchValue.length() > 0){
select.and(Tables.PERSON.LASTNAME.likeIgnoreCase(String.format("%%%s%%", searchValue)));
}
List<PersonRecord> dbPersons = select
.orderBy(Tables.PERSON.LASTNAME, Tables.PERSON.FIRSTNAME, Tables.PERSON.ID)
.limit(length).offset(start)
.fetch();
This code works pretty well. Because I display the data in a datatables table I need to have optional / dynamic sorting capability. I did not find a solution so far.
found the solution myself now:
Collection<SortField<?>> sortFields = new ArrayList<>();
sortFields.add(Tables.PERSON.FIRSTNAME.asc());
List<PersonRecord> dbPersons = select
.orderBy(sortFields)
.limit(length).offset(start)
.fetch();
The result list is perfect but the getResultSize() is incorrect.
I've knocked up some code to illustrate.
Criteria criteria2 = this.getSession().createCriteria(Film.class);
Criterion genre = Restrictions.eq("genreAlias.genreName", details.getSearch().getGenreName());
criteria2.createAlias("genres", "genreAlias", CriteriaSpecification.INNER_JOIN);
criteria2.add(genre);
criteria2.setMaxResults(details.getMaxRows())
.setFirstResult(details.getStartResult());
FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.createFullTextEntityManager(entityManager);
org.apache.lucene.queryParser.QueryParser parser2 = new QueryParser("title", new StopAnalyzer() );
org.apache.lucene.search.Query luceneQuery2 = parser2.parse( "title:"+details.getSearch()");
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery2, Film.class);
fullTextQuery.setCriteriaQuery(criteria2);
fullTextQuery.getResultList()); // Returns the correctly filtered list
fullTextQuery.getResultSize()); // Returns the retsult size without the genre resrtiction
From http://docs.jboss.org/hibernate/search/3.3/api/org/hibernate/search/jpa/FullTextQuery.html
int getResultSize()
Returns the number of hits for this search Caution: The number of results might be slightly different from getResultList().size() because getResultList() may be not in sync with the database at the time of query.
You should try to use some of the more specialized queries like this one:
Query query = new FuzzyQuery(new Term("title", q));
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, Film.class);
int filmCount = fullTextQuery.getResultSize();
and this is how you do pagination requests (I'm guessing you have improperly implemented your paggination):
FullTextQuery hits = Search.getFullTextSession(getSession()).createFullTextQuery(query, Film.class)
.setFirstResult((pageNumber - 1) * perPageItems).setMaxResults(perPageItems);
The above works for me every time. You should keep in mind that the result of getResultSize() more of estimate. I use pagination a lot and I have experienced the number changing between pages. So you should say "about xxxx" results.