SailsJS, Waterline populate records with select - orm

I have looked at a lot of old SO questions which have broken GitHub links and SailsJS Trello, however I am still unclear.
Is it possible, to populate a field (one-to-one relationship) in SailsJS and return only specific fields (either via select or omit).
await Document.find({id: id}).populate('createdBy', {select: ['name']})
I am getting
UsageError: Invalid populate(s).
Details:
Could not populate `createdBy` because of ambiguous usage. This is a singular ("model") association, which means it never refers to more than _one_ associated record. So passing in subcriteria (i.e. as the second argument to `.populate()`) is not supported for this association
, since it generally wouldn't make any sense. But that's the trouble-- it looks like some sort of a subcriteria (or something) _was_ provided!
(Note that subcriterias consisting ONLY of `omit` or `select` are a special case that _does_ make sense. This usage will be supported in a future version of Waterline.)
Here's what was passed in:
{ select: [ 'name' ] }
In models,
createdBy: {
model: 'user',
description: 'Who is this document assigned to'
},
I am using sails 1.1.0, waterline 0.13.5-0
Am I doing this right? Is there a way to do this?

I solved the problem and made a pull request. Since the pull request has not yet been accepted, be careful and use it on your ask.
Go to
node_modules/waterline/lib/waterline/utils/query/forge-stage-two-query.js
Go to this section
// If this is a singular ("model") association, then it should always have
// an empty dictionary on the RHS. (For this type of association, there is
// always either exactly one associated record, or none of them.)
if (populateAttrDef.model) {....}
Change it to:
if (populateAttrDef.model) {
// Tolerate a subcriteria of `{}`, interpreting it to mean that there is
// really no criteria at all, and that we should just use `true` (the
// default "enabled" value for singular "model" associations.)
if (_.isEqual(query.populates[populateAttrName], {})) {
query.populates[populateAttrName] = true;
}
// Otherwise, this simply must be `true`. Otherwise it's invalid.
else {
if (query.populates[populateAttrName] !== true && (_.isUndefined(query.populates[populateAttrName].select) && _.isUndefined(query.populates[populateAttrName].omit))) {
throw buildUsageError(
'E_INVALID_POPULATES',
'Could not populate `'+populateAttrName+'` because of ambiguous usage. '+
'This is a singular ("model") association, which means it never refers to '+
'more than _one_ associated record. So passing in subcriteria (i.e. as '+
'the second argument to `.populate()`) is not supported for this association, '+
'since it generally wouldn\'t make any sense. But that\'s the trouble-- it '+
'looks like some sort of a subcriteria (or something) _was_ provided!\n'+
'(Note that subcriterias consisting ONLY of `omit` or `select` are a special '+
'case that _does_ make sense. This usage will be supported in a future version '+
'of Waterline.)\n'+
'\n'+
'Here\'s what was passed in:\n'+
util.inspect(query.populates[populateAttrName], {depth: 5}),
query.using
);
}//-•
else {
query.populates[populateAttrName] = {
select: query.populates[populateAttrName].select? query.populates[populateAttrName].select : undefined,
omit: query.populates[populateAttrName].omit? query.populates[populateAttrName].omit : undefined
};
}
}//>-•
}
This is the pull request to see exactly what you should change:
https://github.com/balderdashy/waterline/pull/1613

When u use One-to-one association u can't use subcriteria like error say.
So passing in subcriteria (i.e. as the second argument to `.populate()`) is not supported for this association
U can use customToJSON function on model createdBy to omit data.
customToJSON: function() {
return _.omit(this, ['createdAt', 'updatedAt', 'id'])
}

Related

FaunaDB: how to fetch a custom column

I'm just learning FaunaDB and FQL and having some trouble (mainly because I come from MySQL). I can successfully query a table (eg: users) and fetch a specific user. This user has a property users.expiry_date which is a faunadb Time() type.
What I would like to do is know if this date has expired by using the function LT(Now(), users.expiry_date), but I don't know how to create this query. Do I have to create an Index first?
So in short, just fetching one of the users documents gets me this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z")
}
But I would like to get this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z"),
has_expired: true,
}
I have this FQL query now (ignore oauthInfo):
Query(
Let(
{
oauthInfo: Select(['data'], Get(Ref(Collection('user_oauth_info'), refId))),
user: Select(['data'], Get(Select(['user_id'], Var('oauthInfo'))))
},
Merge({ oauthInfo: Var('oauthInfo') }, { user: Var('user') })
)
)
How would I do the equivalent of the mySQL query SELECT users.*, IF(users.expiry_date < NOW(), 1, 0) as is_expired FROM users in FQL?
Your use of Let and Merge show that you are thinking about FQL in a good way. These are functions that can go a long way to making your queries more organized and readable!
I will start with some notes, but they will be relevant to the final answer, so please stick with me.
The Query function
https://docs.fauna.com/fauna/current/api/fql/functions/query
First, you should not need to wrap anything in the Query function, here. Query is necessary for defining functions in FQL that will be run later, for example, in the User-Defined Function body. You will always see it as Query(Lambda(...)).
Fauna IDs
https://docs.fauna.com/fauna/current/learn/understanding/documents
Remember that Fauna assigns unique IDs for every Document for you. When I see fields named id, that is a bit of a red flag, so I want to highlight that. There are plenty of reasons that you might store some business-ID in a Document, but be sure that you need it.
Getting an ID
A Document in Fauna is shaped like:
{
ref: Ref(Collection("users"), "101"), // <-- "id" is 101
ts: 1641508095450000,
data: { /* ... */ }
}
In the JS driver you can use this id by using documentResult.ref.id (other drivers can do this in similar ways)
You can access the ID directly in FQL as well. You use the Select function.
Let(
{
user: Get(Select(['user_id'], Var('oauthInfo')))
id: Select(["ref", "id"], Var("user"))
},
Var("id")
)
More about the Select function.
https://docs.fauna.com/fauna/current/api/fql/functions/select
You are already using Select and that's the function you are looking for. It's what you use to grab any piece of an object or array.
Here's a contrived example that gets the zip code for the 3rd user in the Collection:
Let(
{
page: Paginate(Documents(Collection("user")),
},
Select(["data", 2, "data", "address", "zip"], Var("user"))
)
Bring it together
That said, your Let function is a great start. Let's break things down into smaller steps.
Let(
{
oauthInfo_ref: Ref(Collection('user_oauth_info'), refId)
oauthInfo_doc: Get(Var("oathInfoRef")),
// make sure that user_oath_info.user_id is a full Ref, not just a number
user_ref: Select(["data", "user_id"], Var("oauthInfo_doc"))
user_doc: Get(Var("user_ref")),
user_id: Select("id", Var("user_ref")),
// calculate expired
expiry_date: Select(["data", "expiry_date"], Var("user_doc")),
has_expired: LT(Now(), Var("expiry_date"))
},
// if the data does not overlap, Merge is not required.
// you can build plain objects in FQL
{
oauthInfo: Var("oauthInfo_doc"), // entire Document
user: Var("user_doc"), // entire Document
has_expired: Var("has_expired") // an extra field
}
)
Instead of returning the auth info and user as separate points if you do want to Merge them and/or add additional fields, then feel free to do that
// ...
Merge(
Select("data", Var("user_doc")), // just the data
{
user_id: Var("user_id"), // added field
has_expired: Var("has_expired") // added field
}
)
)

Can I update a FaunaDB document without knowing its ID?

FaunaDB's documentation covers how to update a document, but their example assumes that I'll have the id to pass into Ref:
Ref(schema_ref, id)
client.query(
q.Update(
q.Ref(q.Collection('posts'), '192903209792046592'),
{ data: { text: "Example" },
)
)
However, I'm wondering if it's possible to update a document without knowing its id. For instance, if I have a collection of users, can I find a user by their email, and then update their record? I've tried this, but Fauna returns a 400 (Database Ref expected, String provided):
client
.query(
q.Update(
q.Match(
q.Index("users_by_email", "me#example.com")
),
{ name: "Em" }
)
)
Although Bens comments are correct, (that's the way you do it), I wanted to note that the error you are receiving is because you are missing a bracket here: "users_by_email"), "me#example.com"
The error is logical if you know that Index takes an optional database reference as second argument.
To clarify what Ben said:
If you do this you'll get another error:
Update(
Match(
Index("accounts_by_email"), "test#test.com"
),
{ data: { email: "test2#test.com"} }
)
Since Match could potentially return more then one element. It returns a set of references called a SetRef. Think of setrefs as lists that are not materialized yet. If you are certain there is only one match for that e-mail (e.g. if you set a uniqueness constraint) you can materialize it using Paginate or Get:
Get:
Update(
Select(['ref'], Get(Match(
Index("accounts_by_email"), "test#test.com"
))),
{ data: { email: 'test2#test.com'} }
)
The Get returns the complete document, we need to specify that we require the ref with Select(['ref']..
Paginate:
Update(
Select(['data', 0],
Paginate(Match(
Index("accounts_by_email"), "test#test.com"
))
),
{ data: { email: "testchanged#test.com"} }
)
You are very close! Update does require a ref. You can get one via your index though. Assuming your index has a default values setting (i.e. paging a match returns a page of refs) and you are confident that the there is a single match or the first match is the one you want then you can do Select(["ref"], Get(Match(Index("users_by_email"), "me#example.com"))) to transform your set ref to a document ref. This can then be passed into update (or to any other function that wants a document ref, like Delete).

Is there a way to test response is contained in schema?

In Karate, I'd like to have a schema variable which is a superset of the response data so that I can test multiple requests with the same schema.
This should be specially useful for GraphQL, where the request itself defines the returned fields.
Expected schema:
{
id: '#notnull',
name: '#notnull',
description: '##string',
nonNullStringField: '#string'
...
}
Given url ...
When request ...
Then match response.data <contained in> '#(mySchema)'
Response.data:
{
id: 'someId',
name: 'some name'
}
In this case, all keys returned by the response.data should be in the schema, but any key in the schema not in the response.data should be ignored.
Is there a way to do that in Karate or some plan to add this feature going forward?
Edit: updated the example, since the only attribute being missed was a nullable one.
I'm not convinced an enhancement is needed, because the optional marker ##foo was designed for this purpose, and this already works:
* def schema = { id: '#notnull', name: '#notnull', description: '##string' }
* def response = { id: 'someId', name: 'some name' }
* match response == schema
EDIT: but since you want to limit your schema to the keys in the response in a "generic" way, you can do this:
* def expected = {}
* def fun = function(k, v){ expected.put(k, schema[k]) }
* eval karate.forEach(response, fun)
* match response == expected
You should be able to easily create a re-usable JS or Java utility that achieves the above. A few reasons I'm not in favor of adding another syntax / match keyword is that nested JSON may have some interesting edge cases that will make this complex. And I don't want to complicate match any further. As I said in the comments, IMO schema validation is the last thing you need to test for in GraphQL, it is pretty much guaranteed. This is the first time anyone has requested this in 2 years, so there's that. You could consider submitting a PR of course :)

How to check if a key element exists and if present update it?

I am using mule community edition. So no Dataweave. I am consuming a rest service in my application. In the json response from this backend rest service. I need to check if a particular element exists inside muliple multiple elements of an array and wherever it exists I need to update its value.
E.g. (sample)
Input Request : [ { "id" : "1", "item" : "car", "make" : "Tonda" }, { "id" : "1", "item" : "car" } ]
using foreach to iterate array . Inside for each need to do something like below in expression component.
if( payload.make exists) { payload.make = "Tero"; }
But I do not know , how to check "if element exists"" condition. I tried below in expression component
if( payload.make != empty) { payload.make = "Tero"; }
But it did not work and gives error "Execution of the expression failed (org.mule.api.expression.ExpressionRuntimeException)"" for each array element wherever the particular key(make) is not present
inside foreach use the expression: #[payload.containsKey('make')] to check if the json key make exists or not. This will return true or false
Easy and simple!
Ref: https://forums.mulesoft.com/questions/71478/how-to-check-if-a-key-element-exists-and-if-presen.html?childToView=71502#answer-71502
Tested in a flow, if your payload is a single object and not a collection you can do MEL:
#[(payload.?make != empty) ? "EXISTS" : " OPS NO"]
Just change "EXISTS" with your true condition and "OPS NO" with the false one.
According to documentation: https://docs.mulesoft.com/mule-user-guide/v/3.7/mule-expression-language-reference
Null Safety
To access properties in a null safe manner, add the .? operator before one or more objects in a chain. In the following expression, if fieldA is null, the expression evaluates to null instead of a NullPointerException.

query for Time Stamp in mongo [duplicate]

I have a problem when querying mongoDB with nested objects notation:
db.messages.find( { headers : { From: "reservations#marriott.com" } } ).count()
0
db.messages.find( { 'headers.From': "reservations#marriott.com" } ).count()
5
I can't see what I am doing wrong. I am expecting nested object notation to return the same result as the dot notation query. Where am I wrong?
db.messages.find( { headers : { From: "reservations#marriott.com" } } )
This queries for documents where headers equals { From: ... }, i.e. contains no other fields.
db.messages.find( { 'headers.From': "reservations#marriott.com" } )
This only looks at the headers.From field, not affected by other fields contained in, or missing from, headers.
Dot-notation docs
Since there is a lot of confusion about queries MongoDB collection with sub-documents, I thought its worth to explain the above answers with examples:
First I have inserted only two objects in the collection namely: message as:
> db.messages.find().pretty()
{
"_id" : ObjectId("5cce8e417d2e7b3fe9c93c32"),
"headers" : {
"From" : "reservations#marriott.com"
}
}
{
"_id" : ObjectId("5cce8eb97d2e7b3fe9c93c33"),
"headers" : {
"From" : "reservations#marriott.com",
"To" : "kprasad.iitd#gmail.com"
}
}
>
So what is the result of query: db.messages.find({headers: {From: "reservations#marriott.com"} }).count()
It should be one because these queries for documents where headers equal to the object {From: "reservations#marriott.com"}, only i.e. contains no other fields or we should specify the entire sub-document as the value of a field.
So as per the answer from #Edmondo1984
Equality matches within sub-documents select documents if the subdocument matches exactly the specified sub-document, including the field order.
From the above statements, what is the below query result should be?
> db.messages.find({headers: {To: "kprasad.iitd#gmail.com", From: "reservations#marriott.com"} }).count()
0
And what if we will change the order of From and To i.e same as sub-documents of second documents?
> db.messages.find({headers: {From: "reservations#marriott.com", To: "kprasad.iitd#gmail.com"} }).count()
1
so, it matches exactly the specified sub-document, including the field order.
For using dot operator, I think it is very clear for every one. Let's see the result of below query:
> db.messages.find( { 'headers.From': "reservations#marriott.com" } ).count()
2
I hope these explanations with the above example will make someone more clarity on find query with sub-documents.
The two query mechanism work in different ways, as suggested in the docs at the section Subdocuments:
When the field holds an embedded document (i.e, subdocument), you can either specify the entire subdocument as the value of a field, or “reach into” the subdocument using dot notation, to specify values for individual fields in the subdocument:
Equality matches within subdocuments select documents if the subdocument matches exactly the specified subdocument, including the field order.
In the following example, the query matches all documents where the value of the field producer is a subdocument that contains only the field company with the value 'ABC123' and the field address with the value '123 Street', in the exact order:
db.inventory.find( {
producer: {
company: 'ABC123',
address: '123 Street'
}
});