Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Selector to QueryBuilder to allow filtering returned fields #221

Closed
carlin-q-scott opened this issue Feb 8, 2021 · 13 comments
Closed

Comments

@carlin-q-scott
Copy link

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);
@Roblinde
Copy link
Collaborator

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.

@carlin-q-scott
Copy link
Author

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 })

@Roblinde
Copy link
Collaborator

@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.

@carlin-q-scott
Copy link
Author

carlin-q-scott commented Mar 28, 2021 via email

@nteague22
Copy link
Contributor

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

@NetMelon22
Copy link

Hi,
what is the status of this feature?

1 similar comment
@kamilpitula
Copy link

Hi,
what is the status of this feature?

@Roblinde
Copy link
Collaborator

Roblinde commented Jan 18, 2023

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.

@nteague22
Copy link
Contributor

I can look at it this weekend, and possibly submit something over the next week or so / since I also develop full time.

@nteague22
Copy link
Contributor

nteague22 commented Jan 23, 2023

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

@nteague22
Copy link
Contributor

Thanks!

@Roblinde
Copy link
Collaborator

@nteague22 great work! Thank you!

I will have a look as soon as I get a second and get it merged.

@nteague22
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants