Why QueryContainer is not updating from a Descriptor NESt C# - elasticsearch

Hi I have following descriptors in my NEST query...
queryContainer.DateRange(b => dateRangeDescriptor);
queryContainer.MatchPhrase(b => matchPhraseDescriptor);
And finally I use this QueryContainerDescriptor in the following BoolQueryDescriptor
boolDescriptor.Must(q => queryContainer);
The problem is although I could see values in my dateRangeDescriptor as well as matchPhraseDescriptor, it is not available in side queryContainer.
Not sure what is going wrong here.

Must has the following overloads (in NEST 2.x)
public BoolQueryDescriptor<T> Must(
params Func<QueryContainerDescriptor<T>, QueryContainer>[] queries)
{
// impl
}
public BoolQueryDescriptor<T> Must(
IEnumerable<Func<QueryContainerDescriptor<T>, QueryContainer>> queries)
{
// impl
}
public BoolQueryDescriptor<T> Must(
params QueryContainer[] queries)
{
// impl
}
So you need to pass a collection of queries to apply multiple must clauses rather than adding them all to one QueryContainer.

Related

How does one organize more than a few mutations in GraphQL .Net GraphType First?

In GraphQL .Net most of the example code has one top level mutations graph object that has many actual mutations defined within it.
Here's an example from the GraphQL .NET mutations page:
public class StarWarsSchema : Schema
{
public StarWarsSchema(IServiceProvider provider)
: base(provider)
{
Query = provider.Resolve<StarWarsQuery>();
Mutation = provider.Resolve<StarWarsMutation>();
}
}
public class StarWarsMutation : ObjectGraphType
{
public StarWarsMutation(StarWarsData data)
{
Field<HumanType>(
"createHuman",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<HumanInputType>> {Name = "human"}
),
resolve: context =>
{
var human = context.GetArgument<Human>("human");
return data.AddHuman(human);
});
}
}
And that seems fine when you have 1-5 mutations, but overtime in some larger projects one could conceivably end up with dozens of mutations. Putting them in one big class together seems sufficient to work, although it also seems like there is some organization lacking. I tried putting a child mutation GraphTypeObject into a field on the parent mutation, but I had a little trouble calling the sub-mutation. Perhaps I had it configured wrong.
That just leads me to wonder, certainly there must be a user out there with more than a dozen mutations who might have organized their mutations beyond putting all their mutations in a single top level mutations object.
How does one organize more than a few mutations in GraphQL .Net GraphType First?
https://graphql-dotnet.github.io/docs/getting-started/query-organization
You can "group" queries or mutations together by adding a top level field. The "trick" is to return an empty object in the resolver.
public class StarWarsSchema : Schema
{
public StarWarsSchema(IServiceProvider provider)
: base(provider)
{
Query = provider.Resolve<StarWarsQuery>();
Mutation = provider.Resolve<StarWarsMutation>();
}
}
public class StarWarsMutation : ObjectGraphType
{
public StarWarsMutation(StarWarsData data)
{
Field<CharacterMutation>(
"characters",
resolve: context => new { });
}
}
public class CharacterMutation : ObjectGraphType
{
public CharacterMutation(StarWarsData data)
{
Field<HumanType>(
"createHuman",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<HumanInputType>> {Name = "human"}
),
resolve: context =>
{
var human = context.GetArgument<Human>("human");
return data.AddHuman(human);
});
}
}
This organization is reflected in how the mutations (or queries) are called. If you simply want them externally to appear as a flat list (which would equate to one giant file), you can also break it up into as many files as you want using partial classes.

How can I do a WpGraphQL query with a where clause?

This works fine
query QryTopics {
topics {
nodes {
name
topicId
count
}
}
}
But I want a filtered result. I'm new to graphql but I see a param on this collection called 'where', after 'first', 'last', 'after' etc... How can I use that? Its type is 'RootTopicsTermArgs' which is likely something autogenerated from my schema. It has fields, one of which is 'childless' of Boolean. What I'm trying to do, is return only topics (a custom taxonomy in Wordpress) which have posts tagged with them. Basically it prevents me from doing this on the client.
data.data.topics.nodes.filter(n => n.count !== null)
Can anyone direct me to a good example of using where args with a collection? I have tried every permutation of syntax I could think of. Inlcuding
topics(where:childless:true)
topics(where: childless: 'true')
topics(where: new RootTopicsTermArgs())
etc...
Obviously those are all wrong.
If a custom taxonomy, such as Topics, is registered to "show_in_graphql" and is part of your Schema you can query using arguments like so:
query Topics {
topics(where: {childless: true}) {
edges {
node {
id
name
}
}
}
}
Additionally, you could use a static query combined with variables, like so:
query Topics($where:RootTopicsTermArgs!) {
topics(where:$where) {
edges {
node {
id
name
}
}
}
}
$variables = {
"where": {
"childless": true
}
};
One thing I would recommend is using a GraphiQL IDE, such as https://github.com/skevy/graphiql-app, which will help with validating your queries by providing hints as you type, and visual indicators of invalid queries.
You can see an example of using arguments to query terms here: https://playground.wpgraphql.com/#/connections-and-arguments

Call custom filtering method on IQueryable

I have this linq query (using linq 4.3.0):
return ExecuteODataQuery(Db.AdvisorFees, f => f.Advisor);
Implemented as follows:
protected IQueryable<TType> ExecuteODataQuery<TType>(IQueryable<TType> queryFunc, Func<TType, Advisor> advisorFunc) where TType : Entity
{
var systemAccount = GetSystemAccountThrow();
var results = queryFunc.Where(x => x.Active).AsEnumerable().Where(x => CheckAdvisorOrChannel(systemAccount, advisorFunc(x)));
return results;
}
This is my custom method:
public static bool CheckAdvisorOrChannel(SystemAccount systemAccount, Advisor advisor)
{
return (systemAccount.IsChannelSystemAccount ? systemAccount.Channel.Code == advisor.Channel.Code : systemAccount.Advisor.Code == advisor.Code);
}
This currently works. If I remove the AsEnumerable() I get a LinqToEntities not supported exception on my custom method. I cannot use AsEnumerable() as I have too many records and do not want to do the rest of the filtering in memory. Is it possible to call a custom method on IQueryable instead? I've been playin with extension methods and expressions but have not managed to get this working yet. Any pointers?
I would prefer to not use expression trees as much as possible.

Linq Parsing Error when trying to create seperation of concerns

I am in the middle of a refactoring cycle where I converted some extension methods that used to look like this:
public static IQueryable<Family> FilterOnRoute(this IQueryable<Family> families, WicRoute route)
{
return families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
}
to a more fluent implementation like this:
public class SimplifiedFamilyLinqBuilder
{
private IQueryable<Family> _families;
public SimplifiedFamilyLinqBuilder Load(IQueryable<Family> families)
{
_families = families;
return this;
}
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
return this;
}
public IQueryable<Family> AsQueryable()
{
return _families;
}
}
which I can call like this: (note this is using Linq-to-Nhibernate)
var families =
new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
this produces the following SQL which is fine with me at the moment: (of note is that the above Linq is being translated to a SQL Query)
select ... from "Family" family0_
where exists (select pods1_.PODID from "POD" pods1_
inner join Route wicroute2_ on pods1_.RouteID=wicroute2_.RouteID
where family0_.FamilyID=pods1_.FamilyID
and wicroute2_.RouteID=#p0);
#p0 = 1
my next effort in refactoring is to move the query part that deals with the child to another class like this:
public class SimplifiedPODLinqBuilder
{
private IQueryable<POD> _pods;
public SimplifiedPODLinqBuilder Load(IQueryable<POD> pods)
{
_pods = pods;
return this;
}
public SimplifiedPODLinqBuilder OnRoute(WicRoute route)
{
_pods = _pods.Where(pod => pod.Route.RouteID == route.RouteID);
return this;
}
public IQueryable<POD> AsQueryable()
{
return _pods;
}
}
with SimplifiedFamilyLinqBuilder changing to this:
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam =>
_podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any()
);
return this;
}
only I now get this error:
Remotion.Linq.Parsing.ParserException : Cannot parse expression 'value(Wic.DataTests.LinqBuilders.SimplifiedPODLinqBuilder)' as it has an unsupported type. Only query sources (that is, expressions that implement IEnumerable) and query operators can be parsed.
I started to implement IQueryable on SimplifiedPODLinqBuilder(as that seemed more logical than implementing IEnumberable) and thought I would be clever by doing this:
public class SimplifiedPODLinqBuilder : IQueryable
{
private IQueryable<POD> _pods;
...
public IEnumerator GetEnumerator()
{
return _pods.GetEnumerator();
}
public Expression Expression
{
get { return _pods.Expression; }
}
public Type ElementType
{
get { return _pods.ElementType; }
}
public IQueryProvider Provider
{
get { return _pods.Provider; }
}
}
only to get this exception (apparently Load is not being called and _pods is null):
System.NullReferenceException : Object reference not set to an instance of an object.
is there a way for me to refactor this code out that will parse properly into an expression that will go to SQL?
The part fam => _podLinqBuilder.Load(fam.PODs.AsQueryable() is never going to work, because the linq provider will try to parse this into SQL and for that it needs mapped members of Family after the =>, or maybe a mapped user-defined function but I don't know if Linq-to-Nhibernate supports that (I never really worked with it, because I still doubt if it is production-ready).
So, what can you do?
To be honest, I like the extension methods much better. You switched to a stateful approach, which doesn't mix well with the stateless paradigm of linq. So you may consider to retrace your steps.
Another option: the expression in .Any(pod => pod.Route.RouteID == route.RouteID)); could be paremeterized (.Any(podExpression), with
OnRoute(WicRoute route, Expression<Func<POD,bool>> podExpression)
(pseudocode).
Hope this makes any sense.
You need to separate methods you intend to call from expressions you intend to translate.
This is great, you want each of those methods to run. They return an instance that implements IQueryable<Family> and operate on that instance.
var families = new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
This is no good. you don't want Queryable.Where to get called, you want it to be an expression tree which can be translated to SQL. But PodLinqBuilder.Load is a node in that expression tree which can't be translated to SQL!
families = _families
.Where(fam => _podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any();
You can't call .Load inside the Where expression (it won't translate to sql).
You can't call .Load outside the Where expression (you don't have the fam parameter).
In the name of "separation of concerns", you are mixing query construction methods with query definition expressions. LINQ, by its Integrated nature, encourages you to attempt this thing which will not work.
Consider making expression construction methods instead of query construction methods.
public static Expression<Func<Pod, bool>> GetOnRouteExpr(WicRoute route)
{
int routeId = route.RouteID;
Expression<Func<Pod, bool>> result = pod => pod.Route.RouteID == route.RouteID;
return result;
}
called by:
Expression<Func<Pod, bool>> onRoute = GetOnRouteExpr(route);
families = _families.Where(fam => fam.PODs.Any(onRoute));
With this approach, the question is now - how do I fluidly hang my ornaments from the expression tree?

Using eager loading with specification pattern

I've implemented the specification pattern with Linq as outlined here https://www.packtpub.com/article/nhibernate-3-using-linq-specifications-data-access-layer
I now want to add the ability to eager load and am unsure about the best way to go about it.
The generic repository class in the linked example:
public IEnumerable<T> FindAll(Specification<T> specification)
{
var query = GetQuery(specification);
return Transact(() => query.ToList());
}
public T FindOne(Specification<T> specification)
{
var query = GetQuery(specification);
return Transact(() => query.SingleOrDefault());
}
private IQueryable<T> GetQuery(
Specification<T> specification)
{
return session.Query<T>()
.Where(specification.IsSatisfiedBy());
}
And the specification implementation:
public class MoviesDirectedBy : Specification<Movie>
{
private readonly string _director;
public MoviesDirectedBy(string director)
{
_director = director;
}
public override
Expression<Func<Movie, bool>> IsSatisfiedBy()
{
return m => m.Director == _director;
}
}
This is working well, I now want to add the ability to be able to eager load. I understand NHibernate eager loading can be done by using Fetch on the query.
What I am looking for is whether to encapsulate the eager loading logic within the specification or to pass it into the repository, and also the Linq/expression tree syntax required to achieve this (i.e. an example of how it would be done).
A possible solution would be to extend the Specification class to add:
public virtual IEnumerable<Expression<Func<T, object>>> FetchRelated
{
get
{
return Enumerable.Empty<Expression<Func<T, object>>>();
}
}
And change GetQuery to something like:
return specification.FetchRelated.Aggregate(
session.Query<T>().Where(specification.IsSatisfiedBy()),
(current, related) => current.Fetch(related));
Now all you have to do is override FetchRelated when needed
public override IEnumerable<Expression<Func<Movie, object>>> FetchRelated
{
get
{
return new Expression<Func<Movie, object>>[]
{
m => m.RelatedEntity1,
m => m.RelatedEntity2
};
}
}
An important limitation of this implementation I just wrote is that you can only fetch entities that are directly related to the root entity.
An improvement would be to support arbitrary levels (using ThenFetch), which would require some changes in the way we work with generics (I used object to allow combining different entity types easily)
You wouldn't want to put the Fetch() call into the specification, because it's not needed. Specification is just for limiting the data that can then be shared across many different parts of your code, but those other parts could have drastically different needs in what data they want to present to the user, which is why at those points you would add your Fetch statements.

Resources