GraphQL: idiomatic way to accept an ordered list of sort parameters? - graphql

I'm building a GraphQL API. I'm want to allow users to specify how records should be sorted, using multiple sorts.
What should be possible
If I expose the ability to sort monsters by name, birthdate, and id, users should be able to get the API to sort by any combination of those, including (in SQL terms):
ORDER BY name ASC
ORDER BY name ASC, birthdate DESC, id ASC
... etc
What won't work
A single map, like this:
sorts: [{name: DESC, id: ASC}]
...would not tell me the order in which the sorts should be applied (maps are unordered).
What works OK, but isn't ideal
Currently, I accept a list of maps, like this:
sorts: [{name: DESC}, {id: ASC}]
Each map represents an input object, which has fields like name and id, which are enums with possible values ASC and DESC. I expect only one field to be filled per input object. ~But I don't know a way to enforce that.~
This is awkward because:
It would be easy for users to typo their sort parameters as a single map
I can't specify a default (like ASC for id) without having it added to every input object map
Is there a more idiomatic way of accepting an ordered list of sort parameters?
Update
I've now added a user-facing explanatory error when there is more than one key per map. With this change, I think this strategy is OK, but I'm still happy to hear of better ways in the future if they arise.

I think the short answer is "no".
Long answer: You can introspect your types at startup, and generate the schema dynamically according to some made-up convention using custom scalars to represent sorting directive, for example:
monsters(sortBy: [name___ASC, birthdate___DESC, id___ASC]) { name }
but this is also "just a convention". The "list of maps" ("array of objects") model that you listed a non-ideal might be the best option at this point:
# the query
monsters(
sortBy: [
{name: asc},
{birthdate: desc_nulls_last},
{id: asc}
]
) {
name
}
BUT, irrespective of which way you choose, avoid the temptation of starting hacking these things in manually - your server code will become convoluted due to this cross-cutting concern, as will your schema.
Instead, I have seen some GraphQL-to-ORM-bridging libraries make use of Directives to control the runtime schema generation (one example of this). That should be much more viable than hand-carving stuff like this.

Related

Can I sort a bunch of values without retaining the actual content of the string? Two-Key sort one from on-premise another in the cloud

What do I want to do
I want to sort a bunch of strings, simple enough.
What are my constraints
I have the original text stored on-premises which has the real text which I want to sort, the cloud has some other "columns" of data which is not on-premises and for security reasons I cannot take the original text from on-premises to the cloud.
The real constraint is that I cannot have all the data in one place which causes sorting, paging on values across on-premises & cloud data difficult.
What I thought of (and where I need help)
Maybe I can take a hash or some other way of extracting certain data from the string in such a way that the original string cannot be reproduced (takes care of the security thing) but the extracted string would be enough that I can do sorting on it.
Example
on-premises data:
[
{
"id": 1,
"name": "abcd"
}
]
cloud data:
[
{
"id": 1,
"price": "20"
}
]
I need to sort on both price and name in the above example (imagine a 100,000 rows of such data).
What you need to do is to store pairs of a string and the corresponding id, e.g. in two lists/arrays (whatever the programming language of your choice offers).
Then start sorting the strings, but each time you move a string, move the id the same way.
Alternatively, most programming languages offer constructs which allow to make pairs and then you sort those pairs according to strings, which will automatically move the ids around.
Both ways mean that after sorting, you can still find the id for each string, then with that id you can access the corresponding cloud data as usual.
As an example, the programming language C offers the compound data type construct
struct IdStringPair
{
int id;
char* string;
/* actually just the address of where the full string is stored,
but basically what you probably want to use */
};
Hardly any programming language exists which does not offer something similar.
If conversely the data to sort by is in the cloud, then sorting has to take place in the cloud, i.e. by something being able to execute the sorting algorithm. Make sure that you sort the id along with the key. Then finding the non-cloud string is again the same as before. Whatever you previously did to find a string to an id, do it with the id you got from the cloud sorted data.
This is the same as the first situation/solution, just mirrored.
The core concept is to always sort the ids along with the key (and other data) and thereby dispose of the need to have the data from the other side of the gap between clould and premise. That is applicable to all versions of the sorting of separated data.

Freemarker Help: Pass Data to List Directive for Sorting

First time poster and a Freemarker novice. I'm hoping someone can assist on this. I am currently referencing data from a 1-to-many table using the <#data> directive. I want to sort the results in a particular field order, but I think that can only be done by the <#list> directive. Here's what I have so far:
There's a CART_ABANDONMENT table with the following fields:
CUSTOMER_ID_
PRODUCT_ID
PRODUCT_NAME
PRODUCT_PRICE
ABANDONED_DATE
<#data CART_ABANDONMENT as abandonment><br>
<#filter CUST_ID="${CONTACTS_LIST.CUSTOMER_ID_}"><br>
<#fields PRODUCT_ID PRODUCT_NAME PRODUCT_PRICE ABANDONED_DATE><br>
${abandonment.PRODUCT_NAME} ${abandonment.PRODUCT_PRICE}<br>
</#data>
What I want to do is to list all related results (by CUSTOMER_ID_) and sort them by PRODUCT_PRICE, descending.
It may be something simple, but I haven't been able to find the answer.
Any guidance would be appreciated.
Freemarker is powerful tool tool but it's just template engine.
It has the sort directive for list, but it can be applied only to built-in types: String, Number and Date.
You may convert list of you complex type to one or more lists of built-in types and use ?sort in template.
Another way is to pass already sorted(as you want) list to template before processing.
What you want sounds like something that should be solved on the database (SQL) level, especially where you say "list all related results (by CUSTOMER_ID_)". FreeMarker is only the V (for View) in MVC.

Iterate through items on a given date within date range rails

I kind of have the feeling this has been asked before, but I have been searching, but cannot come to a clear description.
I have a rails app that holds items that occur on a specific date (like birthdays). Now I would like to make a view that creates a table (or something else, divs are all right as well) that states a specified date once and then iterates over the related items one by one.
Items have a date field and are, of course, not related to a date in a separate table or something.
I can of course query the database for ~30 times (as I want a representation for one months worth of items), but I think it looks ugly and would be massively repetitive. I would like the outcome to look like this (consider it a table with two columns for the time being):
Jan/1 | jan1.item1.desc
| jan1.item2.desc
| jan1.item3.desc
Jan/2 | jan2.item1.desc
| etc.
So I think I need to know two things: how to construct a correct query (but it could be that this is as simple as Item.where("date > ? < ?", lower_bound, upper_bound)) and how to translate that into the view.
I have also thought about a hash with a key for each individual day and an array for the values, but I'd have to construct that like above(repetition) which I expect is not very elegant.
Using GROUP BY does not seem to get me anything different (apart from the grouping, of course, of the items) to work with than other queries. Just an array of objects, but I might do this wrong.
Sorry if it is a basic question. I am relatively new to the field (and programming in general).
If you're making a calendar, you probably want to GROUP BY date:
SELECT COUNT(*) AS instances, DATE(`date`) AS on_date FROM items GROUP BY DATE(`date`)
This is presuming your column is literally called date, which seeing as how that's a SQL reserved word, is probably a bad idea. You'll need to escape that whenever it's used if that's the case, using ``` here in MySQL notation. Postgres and others use a different approach.
For instances in a range, what you want is probably the BETWEEN operator:
#items = Item.where("`date` BETWEEN ? AND ?", lower_bound, upper_bound)

Writing LDAP query filter

I have trouble writing a filter for LDAP query.
I have two object classes - Person and Service. Database consists of a number of Persons, each having zero or more services as children. Each person has an identifier, personNumber attribute. I want to select several persons and all their services given person numbers. Is it possible to do so in one query?
For example, if we have the following set of objects:
personNumber=1,ou=root,o=org
serviceNumber=1,personNumber=1,ou=root,o=org
serviceNumber=2,personNumber=1,ou=root,o=org
personNumber=2,ou=root,o=org
serviceNumber=3,personNumber=2,ou=root,o=org
personNumber=3,ou=root,o=org
serviceNumber=4,personNumber=3,ou=root,o=org
, is it possible, given person numbers 1 and 2, to retrieve these objects:
personNumber=1,ou=root,o=org
serviceNumber=1,personNumber=1,ou=root,o=org
serviceNumber=2,personNumber=1,ou=root,o=org
personNumber=2,ou=root,o=org
serviceNumber=3,personNumber=2,ou=root,o=org
but not these:
personNumber=3,ou=root,o=org
serviceNumber=4,personNumber=3,ou=root,o=org
, using one query only? It is an example; it is possible to have more than two identifiers to load. They are not known a priori.
Also, is there a way to specify that attribute value should be in some collection of values, like IN (..) clause in SQL, other than generating big (|(a=..)(a=..)(a=..)..) filter?
The answer is No, per RFC https://www.rfc-editor.org/rfc/rfc2254, there is NO such filter.If the IN list is very large and you have lots of people in ldap, you need write a simple paged query to get all results using (objectClass=Person) filter, and filter the result after retrial. if your code is written in Java, you can checkout unboundid LDAP SDK
If person doesn't have a multivalued attribute holding the service there is no way this can be returned in one ldapsearch. You'll need at least a two stage rocket: first select person, for each person check on childnodes.
AFAIK there is no IN operator in LDAP filters. The RFC is clear about that. So you're stuck with your tedious (|(a=s1)(a=s2)(a=s3)...) construct.

How to properly organize search of the person?

Let's say I have list of persons in my datastore. Each person there may have the following fields:
last name (*)
first name
middle name
id (*)
driving licence id (*)
another id (*)
date of birth
region
place of birth
At least one of the fields marked with (*) must exist.
Now user provides me with the same list of fields (and again at least one of the fields marked with (*) must be provided). I should search for the person user provided. But not all fields should be matched. I should display to the user somehow how I am sure in the results of search. Something like:
if person matched by id and last name (and user provided just these 2 fields for the search), then I am sure that result is correct (100%);
if person matched by id and last name (and user provided other fields, which were found in the database, but were not matched), then I am sure that result is almost correct by 60%;
etc.
(numbers are provided just as example)
How can I organize such search? Is there any standard algorithm? I also would like to minimize number of requests to the database.
P.S. I can not provide user with the actual field values from the database.
It sounds like your logic for determining the quality of a match will be too complex to handle at the database layer. I think you'll get the best performance by retrieving all of the records that match at least one of the mandatory keys, calculating the match score for each of them in memory, and returning the best score. For example, if the user provides you with an id, last name and place of birth, your query would look something like:
SELECT * FROM users WHERE id = `the_id` OR last_name = `the_last_name`;
This could be a performance problem if you have a VERY large dataset with lots of common last names but otherwise I would expect not to see too many collisions. You can check this on your own dataset outside of GAE. You could also get better performance if all mandatory fields MUST match by changing the OR to an AND.

Resources