how to add dynamic facets in Elasticsearch and Nest - elasticsearch

i'm have a movies and music db. based on if the user is in movies or music, the facets need to change.
i've tried using .OnFields(string[]) to pass in an array (which changes based on movies/music) but i'm getting an error.
here's the code i'm using which generates the error. what am i missing?
string[] facetFields = new []{"genres","format","decades","price"};
var searchResult = client.Search<MyData>(s => s
.MatchAll()
.FacetTerm(t => t
.OnFields(facetFields)
.Order(TermsOrder.term)
.AllTerms()
));
the error is:
"Couldn't infer name for facet of type TermFacetDescriptor`1"

found the answer. you have to name the facet (i knew that but wasn't sure where) like so ...
see 'MyFacet' below...
var searchResult = client.Search<MyData>(s => s
.MatchAll()
.FacetTerm("MyFacet", t => t
.OnFields(facetFields)
.Order(TermsOrder.term)
.AllTerms()
));
if this is in the documentation somewhere - could someone point me to it?
Thanks!

Related

How to search for substring in a list of strings in episerver find

I have a list of strings like this
"Users": [
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
]
In my episerver/optimizely code I want to match items. I have written this line of code
searchResult.Filter(x => x.Users.MatchContained(k=> k.Split('|')[3], "0"));
I am trying to get all Users where after splitiing on 'pipe' I get 0 at index 3. I am not getting the result, infact I get an exception.
What am I doing wrong here?
Since you left out A LOT of details these are my assumptions
Users is an IList<string> Users
You are trying to get all Users within that list/array where index 3 equals 0
What we don't know is, among others
Is there more than one page instance that have the Users instance filled?
Anyway, this cannot be solved using any Find API with the current system design. Instead you need to rely on linq to parse the result, but then the Find implementation may not be necessary.
var searchClient = SearchClient.Instance;
var search = searchClient.Search<BlogPage>();
var result = search.GetContentResult();
var usersResult = result
.SelectMany(x => x.Users)
.Where(x => x.Split('|')[3].Equals("0"));
This would create and return an object similar to this, since I'm using SelectMany the array would contain all users throughout the systems where there are matched pages from the search result.
If you for whatever reason would like to keep or see some page properties there are alternative approaches where you construct a new object within a select and remove any object where there where not matched users
var searchClient = SearchClient.Instance;
var search = searchClient.Search<BlogPage>();
var result = search.GetContentResult();
var usersResult = result
.Select(p => new
{
PageName = p.Name,
ContentLink = p.ContentLink.ToString(),
Users = p.Users.Where(x => x.Split('|')[3].Equals("0"))
})
.Where(x => x.Users.Any());
If you like to keep using Find for this kind of implementations you must store the data in a better way than strings with delimiters.

NEST MultiGet search all types possible?

I have got unique document ids (across all types) and I would like to check which document already exists in elasticsearch index. I try to search
var duplicateCheck = _elasticClient
.MultiGet(m => m.GetMany<object>(notices.Select(s => s.Id)).Fields("Id"));
but it returns wrong result - every document has set found property to false.
update
there is workaround here
var exisitngDocIds = _elasticClient.Search<ZPBase>(s => s
.AllTypes()
.Query(q => q.Ids(notices.Select(z=>z.Id)))
.Fields("Id")
.Take(notices.Count)
);
notices = notices.Where(q => !exisitngDocIds.Hits.Any(s => s.Id == q.Id)).ToList();
From the Multi Get API documentation I realized that you can use something similar to the following code to solve your problem:
var response = _elasticClient.MultiGet(m => m
.Index(MyIndex)
.Type("")
.GetMany<ZPBase>(noticeIds));
Note the empty string passed as the Type.

How to construct filter for Elastic Search on Log Stash with NEST?

I have a logstash/elasticsearch/kibana system set up and structured json type logs are getting into elastic search (not from a C# application) and visible in kibana.
I am using NEST because I would like to slice the data from logstash.
The following code is producing "hits" results that I can inspect in the debugger.
ElasticClient client = new ElasticClient(settings);
var searchResults = client.Search( s => s
.From(0)
.Size(100)
.AllIndices()
.SortDescending("#timestamp")
);
However, if I try to expand the search to include something I believe to be present in the log (visible in Kibana), I get now results.
var searchResults = client.Search( s => s
.From(0)
.Size(100)
.AllIndices()
.Query(q => q
.Term("MySpecialFieldName", "ValueThatAppears")
)
.SortDescending("#timestamp")
);
I would also like to take advantage of the type safety and other mechanisms shown in the samples. I am not sure if that is expected to be related.
( I am working on figuring that out separately: Adding a class like "client.Search( to the search seems to prevent the results as well, I am assuming that something about the class is not aligned with the data and is therefore unable to deserialize or is otherwise filtering...)
Found the correct settings to make this work:
The logstash system puts this into a type (log or logs I think) when it gets indexed by elastic search. Also the term needs to be suffixed with ".raw". So the working example ends up being:
var searchResults = client.Search<MyClass>( s => s
.From(0)
.Size(100)
.AllIndices()
.AllTypes()
.Query(q => q
.Term("MySpecialFieldName.raw", "ValueThatAppears")
)
.SortDescending("#timestamp")
);

Delete from elasticsearch all items where field does not match a certan value

I'm trying to delete all items from Elasticsearch index where field time_crawl_started does NOT match a specific value. I'm using match_all query in combination with NOT filter.
This is what I got so far:
$client = new Elasticsearch\Client();
$params = Array(
'index' => ...,
'type' => ...
);
$params['body']['query']['filtered']['query']['match_all'] = Array();
$params['body']['query']['filtered']['filter']['not']['term']['time_crawl_started'] = $someDate;
$client->deleteByQuery($params);
The problem is that this deletes all items, even ones having time_crawl_started set to $someDate, which is simply a datetime such as "2014-02-17 19:13:31".
How should I change this to delete only the items that don't have the correct date?
The problem was that time_crawl_started field was analyzed and thus any comparison by value was wrong. I had to create index manually (as opposed to automagically by just inserting a new document into non-existing index) and specify mapping for my item type, setting 'index' => 'not_analyzed' for time_crawl_started.
And I ended up using script filter like this:
$params['body']['query']['filtered']['query']['match_all'] = Array();
$params['body']['query']['filtered']['filter']['script']['script'] = "doc['time_crawl_started'].value != \"" . $someDate . "\"";

How do dynamically set group by properties and aggregates in a LINQ query?

I assume I need to use the method call syntax instead of the query expression form, and I know the basics of grouping in the latter. Maybe some gurus can give caveats and advice on using group fields and aggregates obtained at runtime from a configuration, for use in a reporting like structure.
Have you looked at Dynamic Linq? It should do what you want. Have a look at this post from scottgu's blog.
If your data is in xml, linq to xml would allow you to write queries against it in which the certain inputs are strings.
For example:
System.Xml.Linq.XElement myData = GetData();
System.Xml.Linq.XElement result = new XElement("Result",
myData.Elements("Customer")
.GroupBy(e => e.Attributes("Name"))
.Select(g => new XElement("CustomerResult",
new XAttribute("Name" = g.Key,
new XAttribute("Count" = g.Count(),
new XAttribute("MinDate" = g.Min(e => e.Date)
)
);

Resources