Hey just started using Lucene.NET, any was wondering if anyone had a working example of Lucene.NET with a faceted search.
I know this below link http://www.devatwork.nl/articles/lucenenet/faceted-search-and-drill-down-lucenenet/
Which looked great, but all it does is tell me the number of results in the faceted search but not actually how I can retrieve the index and details of those results. i.e as in a normal search in Lucene.NET.
I.e from that link he has the following snippet
private static void FacetedSearch(string indexPath, string genre, string term){
var searcher = new IndexSearcher(indexPath);
// first get the BitArray result from the genre query
var genreQuery = new TermQuery(new Term("genre", genre));
var genreQueryFilter = new QueryFilter(genreQuery);
BitArray genreBitArray = genreQueryFilter.Bits(searcher.GetIndexReader());
Console.WriteLine("There are " + GetCardinality(genreBitArray) + " document with the genre " + genre);
// Next perform a regular search and get its BitArray result
Query searchQuery = MultiFieldQueryParser.Parse(term, new[] {"title", "description"}, new[] {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD}, new StandardAnalyzer());
var searchQueryFilter = new QueryFilter(searchQuery);
BitArray searchBitArray = searchQueryFilter.Bits(searcher.GetIndexReader());
Console.WriteLine("There are " + GetCardinality(searchBitArray) + " document containing the term " + term);
// Now do the faceted search magic, combine the two bit arrays using a binary AND operation
BitArray combinedResults = searchBitArray.And(genreBitArray);
Console.WriteLine("There are " + GetCardinality(combinedResults) + " document containing the term " + term + " and which are in the genre " + genre);
}
Which will tell me i.e there is 2 records for the search term "Dublin" and which are in genre "Financial" which is perfect but the article seems to skip the part where it says how I can retrieve the indexes of those results and display on screen.
He does explain this in the link below for a normal search but not facete search..
i.e Normal Search
private static void Search(string indexPath, string term)
{
// create searcher
var searcher = new IndexSearcher(indexPath);
// create a query which searches through the title and description, the term can be in the title or the description
Query searchQuery = MultiFieldQueryParser.Parse(term, new[] {"title", "description"}, new[] {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD}, new StandardAnalyzer());
// perform the search
Hits hits = searcher.Search(searchQuery);
// loop through all the hits and show their title
for (int hitIndex = 0; hitIndex < hits.Length(); hitIndex++)
{
// get the corresponding document
Document hitDocument = hits.Doc(hitIndex);
// write its title to the console
Console.WriteLine(hitDocument.GetField("title").StringValue());
}
}
http://www.devatwork.nl/articles/lucenenet/search-basics-lucenenet/
Any help would be greatly appreciated
Edit :
Or should I do a search query and then do a Filter on the results ?
The BitArray represents hits. Each 1 has an index, that is equal to document id
So 1001001 means that documents with position 0, 3 and 6 in index match your search. You just have to retrieve them from lucene index.
var searcher = new IndexSearcher(indexPath);
// get document at position 0
var doc = searcher.Doc( 0 );
Related
I am having trouble with displaying the results in the search activity of my app. I wonder where it went wrong.
The aim of the function below is to search the input query of the user and find it in every files listed. But the results only matches one data eventhough the query is also present in the other files. Here is the code.
public void searchFiles(File[] filelist, String query, String querysearch, String[] namesOfFiles){
querysearch = "SELECT * FROM Data WHERE ObjectID = ? ";
int temp2 = filelist.length;
for (int i = (temp2-1); i >= 0; i--) {
if(!(filelist[i].getName().equals("DataObjectDB.db")) && !(filelist[i].getName().endsWith("-journal"))){
temp1 = filelist[i].getName();
namesOfFiles[i] = temp1.replaceAll(".db$", "");
Toast.makeText(getApplicationContext(),"Searching " + query + " in: " + namesOfFiles[i], Toast.LENGTH_SHORT).show();
DatabaseHelper db1 = new DatabaseHelper(getApplicationContext(),namesOfFiles[i]);
SQLiteDatabase sqldb = db1.getWritableDatabase();
cursor = sqldb.rawQuery(querysearch, new String[]{query});
Toast.makeText(getApplicationContext(),cursor.toString(), Toast.LENGTH_SHORT).show();
}
}
final ListView listView = (ListView) findViewById(R.id.results_listview);
SearchAdapter adapter = new SearchAdapter(this, R.layout.results_column, cursor,0 );
listView.setAdapter(adapter);
}
The searchFiles() function passes the filelist, query, querysearch and namesOfFiles where 1) filelist contains the list of files in the source folder 2) query is the user input he/she wants to search 3) querysearch is the select statement 3) namesofFiles is just an empty string.
I indicate a toast to see if the code traverses through all the folders. And yes it is. But I don't know why it is not displaying all the results.
Any help? Thanks!
Found an answer on different posts. Basically, you just have to use hashmap and arraylist first before setting up the adapter directly.
I made some tables in Entity Framework 5.0 and generated an SQL Server Database from it. The tables are as follows:
Title (TitleId, TitleName, TitlePrice, ISBN...)
Author (AuthorId, FirstName, LastName)
Category (CategoryId, CategoryName)
The relationships between Title and Author and Title and Category are many to many.
Now, here is how I access what categories are assigned to each title and what authors have contributed to a title.
//for authors
var queryAuthList = from au in ebs.Authors
from t in au.Titles
where t.TitleId == TitleId
select new
{
Name = string.Concat(au.FirstName, " ", au.LastName)
};
//for categories
var queryCatList = from cat in ebs.Categories
from t in cat.Titles
where t.TitleId == TitleId
select new
{
cat.CategoryName
};
Now, how do I display the contents of queryAuthList or queryCatList in a simple text label?
I want the output to be like AuthorA, AuthorB, AuthorC for authors and CategoryX, CategoryY, CategoryZ for categories assigned to a single title.
I tried declaring a List<String> and adding the items to the list in a foreach loop, and then printing the list using String.Join(",",strList). However the output appears as
{Name = AuthorA}, {Name = AuthorB}, {Name = AuthorC}
{CategoryName = CategoryX}, {CategoryName = CategoryY}, {CategoryName = CategoryZ}
The {Name = AuthorA} you see is the default ToString() for an anonymous type.
You don't want that, so do not wrap it in a anonymous type:
var queryAuthList = from au in ebs.Authors
from t in au.Titles
where t.TitleId == TitleId
//select new
//{
// Name = string.Concat(au.FirstName, " ", au.LastName)
//};
select string.Concat(au.FirstName, " ", au.LastName);
And since queryAuthList now is an IEnumerable<string>,
string result = String.Join(",", queryAuthList);
I have a gridview that i want to sort. I wrote the following method for it:
private void SortGridView(string sortExpression, string direction)
{
var constr = new AdminRequirementEF();
string sort = string.Concat("it.", sortExpression, " ", direction);
int pageSize = Convert.ToInt32(ddPageSize.SelectedItem.Text);
var results = constr.Projects;
int totalRecords = results.Count();
this.PopulatePager(totalRecords, pageIndex);
var sortedResults = constr.Projects.OrderBy(sort).Skip((pageIndex - 1) * pageSize).Take(pageNum).ToList();
grdMain.DataSource = sortedResults;
grdMain.DataBind();
}
The problem is sorting is applied on totalrecords not on per page filtered records. I want to use OrderBy(sort) after applying skip and take but it gives me an error stating skip can not be applied before orderby clause. Any help will be much appreciated.
You can get the constr.Projects collection sorted on its primary key
var results = constr.Projects.OrderBy(p => p.ProjectId)
and then apply the skip and take on the 'results' collection with sorting.
results = results.Skip((pageIndex - 1) * pageSize).Take(pageNum).OrderBy(sort).ToList();
This way you will get the records for particular page sorted as required.
I am getting data from these two tables using linq to entities, relationships exist between tables on primary foriegn key basis, result set is coming but every row is repeating multiple times in reult however in Db there are not duplicate rows. Don't understand how to resolve this.
here is piece of code:
StringBuilder sb = new StringBuilder();
string text = txtBoxSearch.Text;
OLSContainer ols = new OLSContainer();
var result = from tex in ols.COURSEs
from another in ols.UNITs
where tex.courseName.Contains(text) || tex.description.Contains(text) || another.unitName.Contains(text)
select new { tex,another };
foreach (var cours in result)
{
sb.AppendLine("<h2 id='" + cours.tex.courseID + "'><a href='admin.aspx?id='" + cours.tex.courseID + "''>" + cours.tex.courseName + "</a></h2>");
}
foreach (var cours in result)
{
sb.AppendLine("<h2 id='" + cours.another.unitID + "'><a href='admin.aspx?id='" + cours.another.unitID + "''>" + cours.another.unitName + "</a></h2>");
}
The problem is this:
var result = from tex in ols.COURSEs
from another in ols.UNITs
It is a cross join. It matches every course with every unit. It doesn't use any FK/PK because no relation (navigation property) is used in this query. To use the relation you have to modify it to:
var result = from tex in ols.COURSEs
from another in tex.SomeNavigationProperty // tex
Hello everyone this is my little Frankenstein code, don't make fun of it, it works!
So you would pass in the table name and a data as an Associative array which are objects.
I'm pretty sure this is not good code as I was and still am learning ActionScript. So what can I change or how would you guys make it better?
public function save(table:String,data:Object):void
{
var conn:SQLConnection = new SQLConnection();
var folder:File = File.applicationStorageDirectory;
var dbFile:File = folder.resolvePath("task.db");
conn.open(dbFile);
var stat:SQLStatement=new SQLStatement();
stat.sqlConnection=conn;
//make fields and values
var fields:String="";
var values:String="";
for(var sRole:String in data)
{
fields=fields+sRole+",:";
stat.parameters[":"+sRole]=data[sRole];
}
//trim off white space
var s:String=new String(fields);
var cleanString:String=s.slice( 0, -2 );
//over here we add : infront of the values I forget why
var find:RegExp=/:/g;
var mymyField:String=new String(cleanString.replace(find,""));
cleanString=":"+cleanString;
var SQLFields:String=mymyField;
var SQLValues:String=cleanString;
stat.text="INSERT INTO "+table+" ("+SQLFields+")VALUES("+SQLValues+")";
stat.execute();
}
The part where you build your query is quite a horror, to be honest. Half the code removes junk you added just a few lines before. This makes it hard to read and understand. Which is a sign of poor code quality. The following is far shorter and simpler:
//make fields and values
var fields:Array = [];
for(var field:String in data) {
fields.push(field);
stat.parameters[":"+field]=data[fieldName];
}
var sqlFields:String = fields.join(",");
var sqlValues:String = ":"+fields.join(",:");
stat.text="INSERT INTO "+table+" ("+sqlFields+")VALUES("+sqlValues+")";
stat.execute();
Someone once told me that a stupid idea that works isn't stupid. As programmer's our first goal is (often) to solve business issues; and as long as our code does that then we are successful. You don't need to apologize for code that works.
In terms of what I would do to change your snippet; I might just encapsulate it a bit more. Can the folder, dbFile, and db file name (task.db) be added either as properties to your class or arguments to the method?
Can you separate out the creation of the SQL Statement from the connection handling from your data parsing?
Some remarks,
As said before you can factorise out all the db connection so you can reuse the function without rewriting it if you need to change the db name.
Don't use new String() you can avoid it
it's not usefull to clean white space between your field :a, :b is the same as :a,:b
Some convention don't begin your local var name with an uppercase, and it's not usefull to reassign them to another var
If i don't get wrong after your //make fields and values can be rewritten for example as :
//make fields and values
var fields:String = "";
var values:String = "";
var fieldSeparator:String = "";
for(var sRole:String in data)
{
fields += fieldSeparator + sRole;
var paramName:String = ":" + sRole;
values += fieldSeparator + paramName;
stat.parameters[paramName] = data[sRole];
fieldSeparator = ",";
}
stat.text = "INSERT INTO " + table +" (" + fields + ") VALUES (" + values + ")";
stat.execute();