how do i add dynamic highlight fields in nest for elasticsearch? - elasticsearch

i want to be able to dynamically add fields for highlighting in elasticsearch using nest. currently it looks like it's not a able to be iterated in any fashion.
i've tried iterating within the .OnFields function in order to produce a list of .OnField functions, but it says it's not iterable.
in this example, i want to dynamically add 'artist' and 'title' and add/remove others based on user input. is this possible?
s.Highlight(h => h
.OnFields(f => f
.OnField("artist")
.OnField("title")
.PreTags("<em>")
.PostTags("</em>")
));

Highlight takes an array of Action<HighlightFieldDescriptor<T>>. You are only passing a single Action<HighlightFieldDescriptor<T>> and calling OnField multiple times on it, which keeps replacing the last value.
It should be this instead:
s.Highlight(h => h
.OnFields(
f => f.OnField("artist").PreTags("<em>").PostTags("</em>"),
f => f.OnField("title").PreTags("<em>").PostTags("</em>")
));
From the code in your follow up post, here's a solution using LINQ:
s.Highlight(h => h
.OnFields(
SearchFields(searchDescriptor.SearchModifier).Select(x => new Action<HighlightFieldDescriptor>(f => f.OnField(x))).ToArray()
));

i realized i had confused a couple of types:
HighlightFieldDescriptor and HighlightDescriptor. sorry. here's my implementation (so i can mark as answered)
s.Highlight(h => h
.OnFields(f =>
GetFieldsHighligthDescriptor(searchDescriptor, f)
)
);
private void GetFieldsHighligthDescriptor(SearchQueryDescriptor searchDescriptor, HighlightFieldDescriptor<Product> f)
{
foreach (var b in SearchFields(searchDescriptor.SearchModifier))
{
f.OnField(b);
}
}
EDIT
actually, this isn't working because it's only return the last entry in my SearchFields array... back to the drawing board?

Related

Add non existing item to list

I am trying to fill a list with objects which haven't been added yet by random to a list. So I loop rInt times through a list and want to pick randomly objects and add them to the list if they does not already exist:
collectionList = new List<CollectionSccmCM>();
Random r = new Random();
int rInt = r.Next(0, 5);
for(int i=0; i<=rInt; i++){
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => collectionList.Any(y => y.CollectionID !=(x.collection_id.ToString()))).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
}
I seems that I have a mistake in the orderby and where part, but I cannot figure out the error. When I put a toList between I dont receive any syntax error anymore, but also doesn't work.
Any tip what I am doing wrong?
Thanks
Edit:
I did a mistake and had to use contains, but still not working:
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => collectionList.Any(y => !y.CollectionID.Contains(x.collection_id.ToString()))).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
Edit:
Got it working with a select, but not so happy with it and dont understand why the otherone wasnt working.
collectionList.Add(_context.CollectionApplications.OrderBy(x => Guid.NewGuid()).Where(x => !collectionList.Select(y => y.CollectionID).ToList().Contains(x.collection_id.ToString())).Select(x => new CollectionSccmCM(){CollectionID= x.collection_id.ToString(), Name=x.collection_name}).FirstOrDefault());
I would strongly suggest you consider adding some whitespace to your LINQ. I would break your first example down as follows:
collectionList.Add(
_context.CollectionApplications
.OrderBy(x => Guid.NewGuid())
.Where(x => collectionList.Any(y => y.CollectionID !=(x.collection_id.ToString())))
.Select(x => new CollectionSccmCM() {
CollectionID = x.collection_id.ToString(),
Name = x.collection_name
}).FirstOrDefault()
);
Looking at your Where call, you are including in the possible elements to add, those elements where any of the collection IDs doesn't match (collectionList.Any(y => ... )). That's all of them (unless you only have one element in collectionList).
You probably want to use All instead of Any -- where all of the collection IDs don't match:
.Where(x => collectionList.All(y => y.CollectionID != x.collection_id.ToString()))

Is it possible to query aggregations on NEST for multiple term fields (.NET)?

Below is the NEST query and aggregations:
Func<QueryContainerDescriptor<ConferenceWrapper>, QueryContainer> query =
q =>
q.Term(p => p.type, "conference")
// && q.Term(p => p.conference.isWaitingAreaCall, true)
&& q.Range(d => d.Field("conference.lengthSeconds").GreaterThanOrEquals(minSeconds))
&& q.DateRange(qd => qd.Field("conference.firstCallerStart").GreaterThanOrEquals(from.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")))
&& q.DateRange(qd => qd.Field("conference.firstCallerStart").LessThanOrEquals(to.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")));
Func<AggregationContainerDescriptor<ConferenceWrapper>, IAggregationContainer> waitingArea =
a => a
.Terms("both", t => t
.Field(p => p.conference.orgNetworkId) // seems ignore this field
.Field(p => p.conference.isWaitingAreaCall)
// .Field(new Field( p => p.conference.orgNetworkId + "-ggg-" + p.conference.networkId)
.Size(300)
.Aggregations(a2 => a2.Sum("sum-length", d2 => d2.Field("conference.lengthSeconds"))));
I have called .Field(p => p.conference.orgNetworkId) followed by .Field(p => p.conference.isWaitingAreaCall) But it seems the NEST client tries to ignore the first field expression.
Is is possible to have multiple fields to be the terms group by?
Elasticsearch doesn't support a terms aggregation on multiple fields directly; the calls to .Field(...) within NEST are assignative rather than additive, so the last call will overwrite any previously set values.
In order to aggregate on multiple fields, you can either
Create a composite field at index time that incorporates the values that you wish to aggregate on
or
Use a Script to generate the terms on which to aggregate at query time, by combining the two field values.
The performance of the first option will be better than the second.

Appending multiple bool filters to a NEST query

I'd like to append multiple bool filters with NEST, but I can't (practically) do it in a single statement as I want to build a set of Bool filters depending on different conditions.
Something like this pseudo code:
// Append a filter
searchDescriptor.Filter(f => f.Bool(b => b.Must(m => m.Term(i => i.SomeProperty, "SomeValue"))));
if (UserId.HasValue)
{
// Optionally append another filter (AND condition with the first filter)
searchDescriptor.Filter(f => f.Bool(b => b.Must(m => m.Term(i => i.AnotherProperty, "MyOtherValue"))));
}
var result = Client.Search(searchDescriptor);
Now it seems when the second optional filter is appended, it essentially replaces the first filter.
I'm sure I'm missing something syntactically, but I can't figure it out and the NEST documentation is a bit thin on the filter DSL. :)
The section on writing queries pretty much applies to filters as well:
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/writing-queries.html
The solution you ended up with is less than ideal since you are wrapping bool filters inside an and filter which have very different caching mechanisms (and filters don't use the cached bitsets so in most cases perform worse then regular bool filters).
Highly recommend reading http://www.elastic.co/blog/all-about-elasticsearch-filter-bitsets/ as it explains really well what the differences between and/or/not filters vs bool filters are.
You can actually rewrite this like so:
Client.Search(s=>s
.Filter(f=> {
BaseFilter ff = f.Term(i => i.MyProperty, "SomeValue");
if (UserId.HasValue)
ff &= f.Term(i => i.AnotherProperty, "AnotherValue");
return ff;
})
);
If the second term is searching using UserId you can take advantage of NEST's conditionless queries
Client.Search(s=>s
.Filter(f=>
f.Term(i => i.MyProperty, "SomeValue")
&& f.Term(i => i.AnotherProperty, UserId)
)
);
If UserId is null then the term query won't be generated as part of the query, nest in this case won't even wrap the single remaining term in a bool filter but just send it as a plain term filter instead.
Ah, something like this seems to work:
var filters = new List<BaseFilter>();
// Required filter
filters.Add(new FilterDescriptor<MyType>().Bool(b => b.Must(m => m.Term(i => i.MyProperty, "SomeValue"))));
if (UserId.HasValue)
{
filters.Add(new FilterDescriptor<MyType>().Bool(b => b.Must(m => m.Term(i => i.AnotherProperty, "AnotherValue"))));
}
// Filter with AND operator
searchDescriptor.Filter(f => f.And(filters.ToArray()));
var result = Client.Search(searchDescriptor);

How can I Check for item existence in 2 observableCollection linq

Just a bit rusty on the old linq.
If I have 2 collections EG NewCustomerList and OldCustomerList and see if a surname exists already how would I do it in linq .I am sure there are many ways. SelectMany rings a bell but forgot how to do it!
In a forEach I would do something like that. What is the equivalent in linq?
foreach (var oldCustomer in OldCustomerList)
{
foreach (var newCustomer in NewCustomerList.Where(x => x.Surname == oldCustomer.Surname))
{
break;
}
}
Any Suggestions? Thanks a lot
So you're trying to see whether any of the old customer surnames are in the new customer list?
One simple option: do a join and see if it's empty:
if (OldCustomerList.Join(NewCustomerList, x => x.Surname, x => x.Surname,
(x, y) => null).Any())
{
...
}
(I've used a null projection because we really don't care about the join result.)
Another option:
var oldSurnames = new HashSet<string>(OldCustomrList.Select(x => x.Surname));
if (NewSurnameList.Any(x => oldSurnames.Contains(x.Surname))
{
...
}
I suspect you may find that you actually want the result in terms of which surnames are in common though... if you can give us more context, we may be able to help you more.
You can do this by:
NewCustomerList.Where(n => OldCustomerList.Any(o => o.Surname == n.Surname))
Here's another approach, which also has O(n + m) complexity + quick-pass semantics:
OldCustomerList.Select(cust => cust.Surname)
.Intersect(NewCustomerList.Select(cust => cust.Surname))
.Any();
IMO, it is more readable than an explicit Enumerable.Join: "test if the projection of surnames from the old customer-list has any elements in common with the projection of surnames from the new customer-list", which is pretty close to the problem-statement.

Count in lambda expression

I am trying to run a query where I get the name of locations and the number of items in that location. So if i have a program that contains 3 locations I want to know how many programs are in that location..I need to use this with a lambda expression or linq to entities.
return Repository.Find(x => x.Location.Name.Count())...clearly missing something here.
we'll just assume I have a Program entity with ProgramID, ProgramName, LocationName...need to know how many programs are in at a location
You can do it like this:
return repository.Count(x => x.Location == "SomeLocation");
Do you want to know the counts for all locations at once?
var locCounts = Repository.GroupBy(prog => prog.Location.Name).ToLookup(g => g.key, g => g.Count());
if you will repositoryPattern, use this code
Clients.Where(p => p.DateOfArrival >= DateTime.Now.AddDays(-3) && p.DateOfArrival <= DateTime.Now.AddDays(3)).Select(p => p.ID).Count()
Repository Pattern

Resources