Multiple facets with SolrNet - solrnet

I want to do a query with two facets with SolrNet, City And Category.
But each facet has diferrent properties. For example mincount for city is zero and for category is 1.
How can I do this with SolrNet?
I know FacetParametes has Queries property that can be an array of queries, but what about mincount?
Thanks.

solr.Query("something", new QueryOptions {
Facet = new FacetParameters {
Queries = new[] {
new SolrFacetFieldQuery("city") { MinCount = 0 },
new SolrFacetFieldQuery("category") { MinCount = 1 },
}
}
});

Related

How to read distance from Elasticsearch response

I am using Elasticsearch V6, NEST V6.
I am searching ES as below and I am using ScriptFields to calculate the distance and include it the result.
var searchResponse = _elasticClient.Search<MyDocument>(new SearchRequest<MyDocument>
{
Query = new BoolQuery
{
Must = new QueryContainer[] { matchQuery },
Filter = new QueryContainer[] { filterQuery },
},
Source = new SourceFilter
{
Includes = resultFields // fields to be included in the result
},
ScriptFields = new ScriptField
{
Script = new InlineScript("doc['geoLocation'].planeDistance(params.lat, params.lng) * 0.001") // divide by 1000 to convert to km
{
Lang = "painless",
Params = new FluentDictionary<string, object>
{
{ "lat", _center.Latitude },
{ "lng", _center.Longitude }
}
}
}
});
Now, I am trying to read the search result and I am not sure how to read the distance from the response, this is what I have tried:
// this is how I read the Document, all OK here
var docs = searchResponse.Documents.ToList<MyDocument>();
// this is my attempt to read the distance from the result
var hits = searchResponse.Hits;
foreach (var h in hits)
{
var d = h.Fields["distance"];
// d is of type Nest.LazyDocument
// I am not sure how to get the distance value from object of type LazyDocument
}
While debugging I can see the distance value, I am just not sure how to read the value?
I found the answer here
To read search document and distance:
foreach (var hit in searchResponse.Hits)
{
MyDocument doc = hit.Source;
double distance = hit.Fields.Value<double>("distance");
}
And if you are only interested in distance:
foreach (var fieldValues in searchResponse.Fields)
{
var distance = fieldValues.Value<double>("distance");
}

elasticsearch nest : get number results of SearchRequest

I am looking for how to do an elasticsearch _count for nest :
in elastic seatch it would be:
i am looking for the equivalent of:
var request = new SearchRequest<type>()
{
Query = new BoolQuery
{
//Should = ...
//Must = ...
},
MinScore = 1
//....
};
var nbResult = client.Count(request);
If you know how to do it and if you have a tip for having a count of results with the fastest way it would help me a lot.
Use client.Count<T>( ... )
var request = new CountRequest<Document>
{
Query = new MatchAllQuery()
};
var nbResult = client.Count<Document>(request);
which yields the following request
POST http://localhost:9200/default-index/document/_count
{
"query": {
"match_all": {}
}
}
I found in sources. Its not solution because i cant test it locally but at least direction.
Look to this test and client source

Find / Count Redundant Records in a List<T>

I am looking for a way to identify duplicate records...only I want / expect to see them.
So the records aren't duplicated completely but the unique fields I am unconcerned with at this point. I just want to see if they have made X# payments of the exact same amount, via the exact same card, to the exact same person. (Bogus example just to illustrate)
The collection is a List<> further whatever X# is the List<>.Count will be X#. In other words all the records in the list match (again just the fields I am concerned with) or I will reject it.
The best I can come up with is to take the first record get value of say PayAmount and LINQ the other two to see if they have the same PayAmount value. Repeat for all fields to be matched. This seems horribly inefficient but I am not smart enough to think of a better way.
So any thoughts, ideas, pointers would be greatly appreciated.
JB
Something like this should do it.
var duplicates = list.GroupBy(x => new { x.Amount, x.CardNumber, x.PersonName })
.Where(x => x.Count() > 1);
Working example:
class Program
{
static void Main(string[] args)
{
List<Entry> table = new List<Entry>();
var dup1 = new Entry
{
Name = "David",
CardNumber = 123456789,
PaymentAmount = 70.00M
};
var dup2 = new Entry
{
Name = "Daniel",
CardNumber = 987654321,
PaymentAmount = 45.00M
};
//3 duplicates
table.Add(dup1);
table.Add(dup1);
table.Add(dup1);
//2 duplicates
table.Add(dup2);
table.Add(dup2);
//Find duplicates query
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
foreach (var item in query)
{
Console.WriteLine("{0}, {1}, {2}, {3}", item.name, item.cardNumber, item.amount, item.count);
}
Console.ReadKey();
}
}
public class Entry
{
public string Name { get; set; }
public int CardNumber { get; set; }
public decimal PaymentAmount { get; set; }
}
The meat of which is this:
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
You're unique entries are based off of the 3 criteria of Name, Card Number, and Payment Amount so you group by them and then use .Count() to count how many of those unique values exist. where g.Count() > 1 filters the group to duplicates only.

lucene.net search multiple fields with one value AND other field with another value

I have a Lucene doc with various fields; Name, BriefData, FullData, ParentIDs (comma delimted string), ProductType, Experience.
I have a search form with a text box, drop down of parents, dropdown of product types, dropdown of experience.
If I search from the text box I get the results I should. If I search from any of dropdowns (or all of them) I get the results I want. If I use the dropdowns AND the textbox I get all results as a search of textbox OR dropdowns. What I want is textbox AND dropdowns.
So, my search builds something like so:
if (string.IsNullOrWhiteSpace(searchTerm))
{
searchTerm = "";
if (!string.IsNullOrWhiteSpace(Request.QueryString["textbox"]))
{
string tester = Request.QueryString["query"];
searchTerm += tester;
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["parent"]))
{
searchTerm += searchTerm.Length > 0 ? " " : "";
searchTerm += "+ParentIDs:" + Request.QueryString["parent"];
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["product"]))
{
ProductTypes pt = db.ProductTypes.Find(int.Parse(Request.QueryString["product"]));
if (pt != null) {
searchTerm += searchTerm.Length > 0 ? " " : "";
searchTerm += "+ProductType:" + pt.TypeName;
}
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["experience"]))
{
searchTerm += searchTerm.Length > 0 ? " " : "";
searchTerm += "+Experience:" + Request.QueryString["experience"];
}
}
if (!Directory.Exists(Helper.LuceneSearch._luceneDir))
Directory.CreateDirectory(Helper.LuceneSearch._luceneDir);
_searchResults = string.IsNullOrEmpty(searchField)
? Helper.LuceneSearch.Search(searchTerm).Distinct()
: Helper.LuceneSearch.Search(searchTerm, searchField).Distinct();
return View(_searchResults.Distinct());
If I am searching just textbox and dropdown parent I get a searchterm of "north +ParentIDs:62"
What I want is the search to ONLY return results with a parent of 62 AND (Name OR BriefData OR FullData of "north").
I have tried creating a searchTerm of "+(Name:north BriefData:north FullData:north) +ParentIDs:62" and "Name:north BriefData:north FullData:north +ParentIDs:62". The first returns no results and the second returns the same as just searching +ParentIDs:62.
I think the logic behind this is pretty simple. However, I have no idea what it is that I need to write in code.
Please help. :)
Thanks to JF Beaulac giving me cause to look at the Lucene.Net code I had included (Helper.LuceneSearch.Search(searchTerm).Distinct()) I rewrote my search to essentially not bother using that but instead to somewhat duplicate it.
I did this by using the MultiFieldQueryParser for the, oddly enough, multi-field search I wanted. I then used the TermQuery for single field queries. These were all added to a BooleanQuery and my search was executed against said BooleanQuery.
var hits_limit = 1000;
var analyzer = new StandardAnalyzer(Version.LUCENE_29);
BooleanQuery bq = new BooleanQuery();
if (string.IsNullOrWhiteSpace(searchTerm))
{
searchTerm = "";
if (!string.IsNullOrWhiteSpace(Request.QueryString["textbox"]))
{
string tester = Request.QueryString["textbox"];
var parser = new MultiFieldQueryParser(Version.LUCENE_29, new[] { "Name", "BriefData", "FullData" }, analyzer);
var query = Helper.LuceneSearch.parseQuery(tester.Replace("*", "").Replace("?", ""), parser);
bq.Add(query, BooleanClause.Occur.MUST);
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["parent"]))
{
bq.Add(new TermQuery(new Term("ParentIDs", Request.QueryString["parent"])), BooleanClause.Occur.MUST);
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["product"]))
{
ProductTypes pt = db.ProductTypes.Find(int.Parse(Request.QueryString["product"]));
if (pt != null) {
bq.Add(new TermQuery(new Term("ProductType", pt.TypeName)), BooleanClause.Occur.MUST);
}
}
if (!string.IsNullOrWhiteSpace(Request.QueryString["experience"]))
{
bq.Add(new TermQuery(new Term("Experience", Request.QueryString["experience"])), BooleanClause.Occur.MUST);
}
}
if (!System.IO.Directory.Exists(Helper.LuceneSearch._luceneDir))
System.IO.Directory.CreateDirectory(Helper.LuceneSearch._luceneDir);
var searcher = new IndexSearcher(Helper.LuceneSearch._directory, false);
var hits = searcher.Search(bq, null, hits_limit, Sort.RELEVANCE).ScoreDocs;
var results = Helper.LuceneSearch._mapLuceneToDataList(hits, searcher).Distinct();
analyzer.Close();
searcher.Close();
searcher.Dispose();
return View(results);
It should be noted that to get the product and experience fields to work I had to set them to "Field.Index.NOT_ANALYZED" when adding them to the index. I'm guessing this was because they would only ever have a single value per document. The other searched fields are "Field.Index.ANALYZED".

How do I write a LINQ query that inverts the grouping of a hierarchical data source?

How would one write a LINQ query which takes a hierarchical source data and transforms it so that the grouping is inverted?
Say I have a list of Topic objects each of which contains a collection of Tags which represent meta-data tags on that topic. What I need is to write a LINQ query to basically flip the hierarchy inside out so that I have a list of Tags each of which have a collection of topics that are tagged with that particular tag.
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Tag { Name = "Contraversial", Color = "Red" }
Tag { Name = "Politics", Color = "LightBlue" }
Topic { Title = "iPhone to support SiliverLight!", Posted = 02/23/2009 }
Tag { Name = "BleedingEdge", Color = "LightBlue" }
Tag { Name = "Contraversial", Color = "Red" }
Tag { Name = ".NET", Color = "LightGreen" }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = "Politics", Color = "LightBlue" }
Tag { Name = "Contraversial", Color = "Red" }
I want the above data to look instead like the results below.
Tag { Name = "Contraversial", Color = "Red" }
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Topic { Title = "iPhone to support SiliverLight!", Posted = 23/02/2009 }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = "Politics", Color = "LightBlue" }
Topic { Title = "Political Debate #1", Posted = 01/02/2008 }
Topic { Title = "Fed Chairman admits guilt for causing second Great Depression", Posted = 06/15/2010 }
Tag { Name = ".NET", Color = "LightGreen" }
Topic { Title = "iPhone to support SiliverLight!", Posted = 23/02/2009 }
You can assume that any repeated piece of data is referentially unique in that that is a single instance in memory and these there are just several references to that same object. Also it is reasonable for the answer to use anonymous classes to produce the projection since I realize the shape of the classes may be slightly different after the inversion.
UPDATE: I added the code below which sets up the example data. I'm playing around with the answers posted and some of my own ideas in LinqPad.
var tags = new[]
{
new { Name = "Contraversial", Color = "Red" },
new { Name = "Politics", Color = "LightBlue" },
new { Name = ".NET", Color = "LightGreen" },
new { Name = "BleedingEdge", Color = "LightBlue" }
};
var topics = new[]
{
new
{
Title = "Political Debate #1",
Posted = DateTime.Parse("01/02/2008"),
Tags = (from t in tags where new []{"Contraversial", "Politics"}.Contains(t.Name) select t),
},
new
{
Title = "iPhone to support SiliverLight!",
Posted = DateTime.Parse("02/23/2009"),
Tags = (from t in tags where new []{"BleedingEdge", "Contraversial", ".NET", }.Contains(t.Name) select t),
},
new
{
Title = "Fed Chairman admits guilt for causing second Great Depression",
Posted = DateTime.Parse("06/15/2010"),
Tags = (from t in tags where new []{"Contraversial", "Politics"}.Contains(t.Name) select t),
},
};
What you're looking for is a Pivot.
Is it possible to Pivot data using LINQ?
This source contains C# code for a Linq Pivot extension method:
public static class LinqExtensions
{
public static Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>> Pivot<TSource, TFirstKey, TSecondKey, TValue>(this IEnumerable<TSource> source, Func<TSource, TFirstKey> firstKeySelector, Func<TSource, TSecondKey> secondKeySelector, Func<IEnumerable<TSource>, TValue> aggregate)
{
var retVal = new Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>>();
var l = source.ToLookup(firstKeySelector);
foreach (var item in l)
{
var dict = new Dictionary<TSecondKey, TValue>();
retVal.Add(item.Key, dict);
var subdict = item.ToLookup(secondKeySelector);
foreach (var subitem in subdict)
{
dict.Add(subitem.Key, aggregate(subitem));
}
}
return retVal;
}
}
IDictionary<Topic, IList<Tag>> data;
var n = data.SelectMany(x => x.Value.Select(y => new { Topic = x.Key, Tag = y }))
.GroupBy(x => x.Tag, x => x.Topic);
After playing around in LinqPad a bit I think I may have found a suitable solution.
Here is the simple example.
var topicsByTags =
from topic in topics
from tag in topic.Tags
group topic by tag;
And in order to get rid of the redundant Tags collection under each topic we can do the following.
var topicsByTags =
from topic in topics
from tag in topic.Tags
group new
{
Title = topic.Title,
Color = topic.Posted,
} by tag into g
select new
{
g.Key.Name,
g.Key.Color,
Topics = g,
};
UPDATE: Below is another alternative which takes advantage of the grouping itself in the projection. Upside is slightly cleaner query, downside is that the group Key sticks around with the group even if it's not going to be used.
var topicsByTags =
from topic in topics
from tag in topic.Tags
group new
{
Title = topic.Title,
Color = topic.Posted,
} by tag into g
select new
{
g.Key.Name,
g.Key.Color,
Topics = g,
};
I'll hold off accepting my own answer to allow some debate over which solution solves the problem that I posed the best.

Resources