contentful.net
contentful.net copied to clipboard
Add Selector to QueryBuilder to allow filtering returned fields
Hi,
I'd like to retrieve just one field for all my entries of a given type. The HTTP API supports this via the selector query param, but it's not part of the .NET API.
To get around this missing feature, I built my query and concatenated the selector string.
var query = QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Build() + "&select=fields.urlStub";
var landers = await _contentful.GetEntries<Lander1>(query);
var landerUrlStubs = landers.Select(lander => lander.UrlStub);
I'd the .NET SDK to provide an API like this:
var query = QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Select(lander => lander.UrlStub);
var landers = await _contentful.GetEntries<Lander1>(query);
var landerUrlStubs = landers.Select(lander => lander.UrlStub);
Hi @carlin-q-scott
Thanks for the suggestion. I feel that the case of just including a single field in a query is quite unusual. It wouldn't hurt to have a Select
option for the query builder, but I think we need to find a good way to express where you want to include multiple fields as well.
I was looking at this a long time ago and couldn't find a great way, but I'll have a look again.
Entity Framework and most of the other ORMs I've used in .NET have great support for selecting one or several fields. The general term for it is a "projection query". I don't know how it is implemented in those libraries, but the linq expression looks like this:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Select(lander => new Lander { lander.UrlStub, lander.Title })
You can also do that with an anonymous object:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Select(lander => new { lander.UrlStub, lander.Title })
@carlin-q-scott yeah, but that's projecting into a type. What we're talking about here is transforming the expression into a query string value that Contentful understands.
If you just want to project into a different type you could just use QueryBuilder<Lander>
directly, instead of QueryBuilder<Lander1>
.
The api I'm considering would be something like .Select(l => l.UrlStub, l.Title, l.SomethingElse
. Haven't had the time to look into if there's a good way to implement an infinite number of lambda parameters like this, but perhaps it's possible to use params[]
. I'll give it a look.
Entity framework converts that projection into a SQL SELECT statement. It’s the same situation. I don’t know how they do it, but they do.
- Carlin
On Mar 27, 2021, at 8:41 AM, Robert Linde @.***> wrote:
@carlin-q-scott yeah, but that's projecting into a type. What we're talking about here is transforming the expression into a query string value that Contentful understands.
If you just want to project into a different type you could just use QueryBuilder<Lander> directly, instead of QueryBuilder<Lander1>.
The api I'm considering would be something like .Select(l => l.UrlStub, l.Title, l.SomethingElse. Haven't had the time to look into if there's a good way to implement an infinite number of lambda parameters like this, but perhaps it's possible to use params[]. I'll give it a look.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
It is not terribly difficult, it requires doing a lightweight visitor that visits the lambda from the expression, and visits on just member expression which gives an auto drill down to the named properties from the selector. -- The result could just build on an upsert workflow of adding the field(s) to the select for each call, so it could be used multiple times safely. I could submit a suggested PR for it
Hi, what is the status of this feature?
Hi, what is the status of this feature?
Hi @kamilpitula I'm afraid this isn't highly prioritised at the moment, but if the community wish to add a PR with this feature I could have a look.
I can look at it this weekend, and possibly submit something over the next week or so / since I also develop full time.
I pushed up a pull request from a fork off of master on 2023-01-23, so if anyone is watching this issue -- I am new to github pull-requests and did not know if this issue should have been tagged on the pull-request. I leave that in the hands of the maintainers -- but am letting others know if this is accepted, does allow for anonymous type selecting fields from the entry a-la ef core like syntax
Thanks!
@nteague22 great work! Thank you!
I will have a look as soon as I get a second and get it merged.
Entity Framework and most of the other ORMs I've used in .NET have great support for selecting one or several fields. The general term for it is a "projection query". I don't know how it is implemented in those libraries, but the linq expression looks like this:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Select(lander => new Lander { lander.UrlStub, lander.Title })
You can also do that with an anonymous object:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").Select(lander => new { lander.UrlStub, lander.Title })
Just as an FYI, the addition I built will use the anonymous type, since the type that is being partially fetched is implicit to the query builder. (Since that is the contentType being filtered against). So just wanted to make sure it was clear -- the syntax per the example would now be:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").SelectFields(lander => new { lander.UrlStub, lander.Title });
and also now, if not specifying sys props. it won't auto add them per entry (might increase performance in some cases). To keep them still, there is one overload to pass a bool for all sys props:
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").SelectFields(lander => new { lander.UrlStub, lander.Title }, true);
or to collect a subset of sys props,
QueryBuilder<Lander1>.New.ContentTypeIs("lander1").SelectFields(lander => new { lander.UrlStub, lander.Title }, sys => new { sys.Id, sys.Name });
just keep in mind, the http API will still not let you select something deeper than immediate sys/fields children -- so sys.contentType.sys.id will throw on the .Net implementation, because it will fail the HTTP API as well. To get the whole sys gamut, use the first overload with boolean