Using multi-layer queries in Solr - search

The Solr "qf" parameter works as follows:
Let's say I have: query = "sid" and qf = [field1, field1_edge, field2, field2_edge].
The Solr score is calculated as follows:
max(f1, f1_e, f2, f2_e) + tie * (sum of other 3 fields) where: "tie" lies in [0,1]
Let's call: winner1 = field with max(f1, f1_e) and
winner2 = field with max(f2, f2_e)
I would like to score a given query in Solr as follows:
score1 = winner1_score + tie_1 * loser1_score
score2 = winner2_score + tie_1 * loser2_score
final score = score1 + tie_2 * score2
Effectively, I want to apply qf in two layers (taking tie_1 = 0 and tie_2 = 1). What are my options to implement this idea of relevance? I think neither "qf" parameter nor function boosts support this.
Thanks!

It seems to me that's the way to do it is to use the query function which allows you to apply functions to queries.
You combine this with nested query parsers which allows you to run multiple dismax queries.
You can do something like this (where you set tie1 and tie2 according to what you want):
q=_val_:"add(query($qq1),product(query($qq2),${tie2}))"
qq1={!edismax qf='field1 field1_edge' v='sid' tie=${tie1}}
qq2={!edismax qf='field2 field2_edge' v='sid' tie=${tie1}}
tie1=0.5
tie2=0.3
If you used Solr 7.2 (or higher) you also need to set uf=_query_ * in order for the _val_ hook to work.
P.S: it should be possible (though I haven't tested it) to move the content of q into the qf parameter and that way you don't have to use the _val_ hook:
qf=add(query($qq1),product(query($qq2),${tie2}))

Related

Azure search - custom function for result boosting

I'm trying to move "complex" function to Azure Search. This function calculates score per each result element base on filter data (from search query) and data stored in result element. Score is use for reasult boosting. Base on my research Azure Search provides result boosting, but it's too simple for mine requirement.
Example function:
//filterElementsIds - ids taken from search query filter
public double Score(IEnumerable<string> filterElementsIds, ResultElement element)
{
double score = 0;
foreach(var elem in element.ScoreForFilters)
if (filterElementsIds.Any(x => x == elem.Key))
score += elem.Value * 1.5;
return score;
}
Currently, I'm iterating through each result returned by Azure Search - calculating score and sorting elements inside my application.
Is it possible to implement such function in Azure Search to improve process of boosting results?
I'm not sure I fully understand your question, but it appears like you are trying to boost the score of certain document if their key is equal to any of the IDs in your collection of "filterElements". If that's so, you could use the lucene query language to craft a query which does that:
https://learn.microsoft.com/en-us/azure/search/search-query-lucene-examples
You could do a search that looks like this
OriginalSearchTerm OR (OriginalSearchTerm AND key:("filterID1" OR "filterID2" OR "filterID3"))
That way, documents that match both the original search term as well as having one of the filter ID as part of the "key" field will match higher than documents that only match the original search term. You can also term boosting to give a specific boost to the key field in this case
If that's so, could you use "term boosting" to achieve this?
https://learn.microsoft.com/en-us/azure/search/search-query-lucene-examples#example-5-term-boosting
OriginalSearchTerm OR (OriginalSearchTerm AND key:("filterID1" OR "filterID2" OR "filterID3")^2)

How to check if ArangoDB query is not empty?

I would like to make an exists PostgreSQL query.
Let's say I have a Q ArangoDB query (AQL). How can I check if Q returns any result?
Example:
Q = "For u in users FILTER 'x#example.com' = u.email"
What is the best way to do it (most performant)?
I have ideas, but couldn't find an easy way to measure the performance:
Idea 1: using Length:
RETURN LENGTH(%Q RETURN 1) > 0
Idea 2: using Frist:
RETURN First(%Q RETURN 1) != null
Above, %Q is a substitution for the query defined at the beginning.
I think the best way to achieve this for a generic selection query with a structure like
Q = "For u in users FILTER 'x#example.com' = u.email"
is to first add a LIMIT clause to the query, and only make it return a constant value (in contrast to the full document).
For example, the following query returns a single match if there is such document or an empty array if there is no match:
FOR u IN users FILTER 'x#example.com' == u.email LIMIT 1 RETURN 1
(please note that I also changed the operator from = to == because otherwise the query won't parse).
Please note that this query may benefit a lot from creating an index on the search attribute, i.e. email. Without the index the query will do a full collection scan and stop at the first match, whereas with the index it will just read at most a single index entry.
Finally, to answer your question, the template for the EXISTS-like query will then become
LENGTH(%Q LIMIT 1 RETURN 1)
or fleshed out via the example query:
LENGTH(FOR u IN users FILTER 'x#example.com' == u.email LIMIT 1 RETURN 1)
LENGTH(...) will return the number of matches, which in this case will either be 0 or 1. And it can also be used in filter conditions like as follows
FOR ....
FILTER LENGTH(...)
RETURN ...
because LENGTH(...) will be either 0 or 1, which in context of a FILTER condition will evaluate to either false or true.
Do you need and AQL solution?
Only the count:
var q = "For u in users FILTER 'x#example.com' = u.email";
var res = db._createStatement({query: q, count: true}).execute();
var ct = res.count();
Is the fastest I can think of.

Raw results from ServiceStack.OrmLite query

I'm wondering if there's a way to get "raw" results from a OrmLite query in ServiceStack.
I'll explain... I know I can use:
var results = Db.SqlList<MyModel>("SELECT * FROM TableName");
passing the model of my output results, but if I don't know it?
Can I get "raw" results without know the types of the data I'm reading?
Thank you
Have a look at the support of Dynamic Result sets in OrmLite.
Where you can access an un-typed schema with a List<object>, e.g:
var results = Db.SqlList<List<object>>("SELECT * FROM TableName");
Or if you want the column names as well you can use:
var results = db.Select<Dictionary<string,object>>("SELECT * ...");
OrmLite also has a version of Dapper embedded if you prefer to access the results using dynamic instead, e.g:
IEnumerable<dynamic> results = db.Query("SELECT * FROM TableName");

Grails search mechanism

For my website, i need to do a search mechanism, in which some of the entry field would be: Country, City, Between Dates (with or without year field), Keywords, etc etc.
My problem is, the user must decide what they wanna search for. For example, if they want to introduce just date, or date and city, or city and keyword.. etc. I dont really know how to do that, i mean, i know how to search for one thing at a time, but i'm not sure how can do this all-in-one.
a) Would i need like something like this: (if-else, if-else) and than write the code for each combination, or there is an easier way to do that?
b )Bytheway, my search mechanism is done the folowing way (i'v never done a search mechanism before, so i dont know if it is the best aproach, would apreciate some comments here also and suggestions):
class book{
String a
String b
...
Date z
String allAttributesTogether() {
a + b + c + ... + z
}
}
then in my controller, i do a double for statment and cross-match the introduced words for the search and the result of allAttributesTogether().
Thanks in advanced, VA
Check out the filter pane plugin.
When you say "search", comes to my mind search engines. But I think you are asking about querying the database, right?
If you are talking about search mechanisms, search engines are a great tool. You can take a look at Lucene, Compass, and ElasticSearch (ES) to name a few. Compass and ES are based on lucene, but are much higher in the abstraction level (easier to use).
I have been using ElasticSearch with great satisfaction.
If you are talking about querying the database, then you can just build a HQL query dynamically. The method bellow should be in a Controller, as it uses the params attribute. It is not tested ok?
List allAttributesTogether() {
def query = " select book from Book book "
def queryParams = [:]
def needsAnd = false
if(params.a || params.b || params.z ){
query += " where "
}
if(params.a){
query += " book.a = :a "
queryParams['a'] = params.a
needsAnd = true
}
if(params.b){
if(needsAnd) query += " and "
query += " book.b = :b "
queryParams['b'] = params.b
needsAnd = true
}
if(params.a){
if(needsAnd) query += " and "
query += " book.z = :z "
queryParams['z'] = params.z
}
return Book.executeQuery(query, queryParams)
}
There is also the alternative of using Criteria builder. You can also use "if" to add clauses to your Criteria clauses.

Search query with Subsonic

Ok,
Today I am trying to learn Subsonic. Pretty cool stuff.
I am trying to build some search functionality into my website but am struggling about how I might achieve this in Subsonic.
I have one search field that could contain multiple keywords. I want to return results that match all of the keywords. The target on the search is a single text column.
So far I have this (it runs but never returns results):
return new SubSonic.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(InfopathArchive.XmlDocColumn).Like(keywords)
.ExecuteTypedList<Visit>();
There is a one to one mapping between the Visit table and the InfoPathArchive table. I just want to return the collection of Visits that have the keywords in the related XMLDocColumn.
If I could get that working it would be great. Now the second problem is that if someone searches for 'australia processmodel' then obviously the above code should only return that exact phrase. How can I create a query that splits up my search term so that it must return documents that contain ALL of the individual search terms?
Any help appreciated.
Edit: Ok, so the basic search works, but the multiple keyword search doesnt. I did what Adam suggested but it seems Subsonic only uses one parameter for the query.
Here is the code:
List<string> wordsInQueryList = keywords.Split(' ').ToList();
SqlQuery q = Select.AllColumnsFrom<Visit>()
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(Visit.IsDeletedColumn).IsEqualTo(false);
foreach(string wordInQuery in wordsInQueryList)
{
q = q.And(InfopathArchive.XmlDocColumn).Like("%" + wordInQuery + "%");
}
return q.ExecuteTypedList();
Then if I look at the query that Subsonic generates:
SELECT (bunch of columns)
FROM [dbo].[Visit]
INNER JOIN [dbo].[InfopathArchive] ON [dbo].[Visit].[VisitId] = [dbo].[InfopathArchive].[VisitId]
WHERE [dbo].[Visit].[IsDeleted] = #IsDeleted
AND [dbo].[InfopathArchive].[XmlDoc] LIKE #XmlDoc
AND [dbo].[InfopathArchive].[XmlDoc] LIKE #XmlDoc
So it ends up that only the last keyword is being searched for.
Any ideas?
First question:
return new SubSonic.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(InfopathArchive.XmlDocColumn).Like("%" + keywords + "%")
.ExecuteTypedList<Visit>();
Second question:
Pass a List of words in your query to a function that builds a SubSonic query as follows
SqlQuery query = DB.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where("1=1");
foreach(string wordInQuery in wordsInQueryList)
{
query = query.And(InfopathArchive.XmlDocColumn).Like("%" + wordInQuery + "%")
}
return query.ExecuteTypedList<Visit>();
Obviously this is untested but it should point you in the right direction.
You can do what Adam is suggesting or with 2.2 you can simply use "Contains()" instead of Like("%...%"). We also support StartsWith and EndsWith() :)

Resources