Using a key condition expression that includes conditions on the sortkey (AWS DynamoDB with Serverless Framework) - aws-lambda

I want to retrieve just ONE item from a DynamoDB table ("todosTable") with partitionKey = userID and sortKey = todoID.
Just to show you something, below there is an example that correctly gets ALL the todo items for a user with userId=userId
async getAllTodos(userId: string): Promise<TodoItem[]>{
const result = await this.docClient.query({
TableName: this.todosTable,
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId
}
}).promise()
return result.Items as TodoItem[]
}
But that is not what I want. I want just one item.
As per the documentation, I will use a key condition expression that acts upon Primary keys(partition and/or sort key)
So I tried to do it this way but this is incorrect
async getTodoItem1(userId: string, todoId: string): Promise<TodoItem>{
const result = await this.docClient.get({
TableName: this.todosTable,
KeyConditionExpression: 'userId = :userId',
AND todId = :todoId,
ExpressionAttributeValues: {
':userId': userId,
':todoId': todoId
}
}).promise()
return result.Item
}
Could someone please help me get the correct query that retrieves just one item, where partition key = userID and sort key = todoID ?
Is the correct pattern to use a GlobalSecondaryIndex in this case?
Thanks

You'll need to add the secondary key to the KeyConditionExpression as well, should look something like this:
KeyConditionExpression: 'userId = :userId AND todoId = :todoId'
The sort key is more flexible and allows for other operations like BEGINS_WITH, BETWEEN etc

Related

Return custom field based on other not requested field?

Let's say that I want to get a person's age using this query:
{
getUser(id: "09d14db4-be1a-49d4-a0bd-6b46cc1ceabb") {
full_name
age
}
}
I resolve my getUser query like this (I use node.js, type-graphql and knex):
async getUser(getUserArgs: GetUserArgs, fields: UserFields[]): Promise<User> {
// Return ONLY ASKED FIELDS
const response = await knex.select(this.getKnexFields(fields)).from(USER).whereRaw('id = ?', [getUserArgs.id]);
// returns { full_name: 'John Smith' }
return response[0];
}
The problem is that then I can't calculate age field, because I did not get born_at (datetime field stored in a db) in the first place:
#FieldResolver()
age(#Root() user: User, #Info() info: GraphQLResolveInfo): number {
console.log(user); // { full_name: 'John Smith' } no born_at field - no age, so error
// calculate age from born_at
return DateTime.fromJSDate(user.born_at).diff(DateTime.fromJSDate(new Date()), ['years']).years;
}
Is there some fancy graphql-build-in way / convention to predict that born_at will be needed instead of doing it manually through info / context?
You should always return full entity data from the query-level resolvers, so they are available for field resolvers.
The other solution is to manually maintain a list of required fields for field resolvers, so your "fields to knex" layer can always include them additionally".
Further improvements might be to can a list of additional columns based on the requested fields (thus the field resolvers that will be triggered).

Sequelize callback before save() is finished

here is my code:
//Models definition
var User = sequelize.define('user', ...);
var Address = sequelize.define('address', ...);
var Club = sequelize.define('club', ...);
//Club scopes
scopes: {
withUser: {
include: [
{ model: User }
]
},
//Models associations
User.hasOne(Address);
Club.hasOne(User);
Club.hasOne(Address);
//Main
//Create address
var addressToCreate = {};
if(body.address) addressToCreate.address = body.address;
if(body.city) addressToCreate.city = body.city;
if(body.zipCode) addressToCreate.zipCode = body.zipCode;
//Get user from db
var user = await User.findByPk(body.user);
var clubToCreate = { name: body.name, phone: body.phone };
//Persist Address in db
return Address.create(addressToCreate)
.then(address => {
//Persist club in db
return Club.create(clubToCreate)
.then(club => {
//Associate User and Address to Club
club.setAddress(address);
club.setUser(user);
//Save club with associated models
return club.save()
})
.then(club => Club.scope('withUser').findByPk(club.id))
.then(club => { console.log(club); return club; })
})
In my db, table address contains userId and clubId and table user contains clubId;
This code seems to work to create and associate models. But the final club displayed by console.log shows user: null
However, in db there is the good row in table user with the good foreign key who reference the club id
My logs show that the request select (from Club.findByPk) is done before the update (from club.save). Like .then is executed before promise is resolved
Sry for my bad english, hope someone can help
You are using async/await but mixing it up with the older promise style based on your code that fetches Users. Below is a copy of your code in async/await style, although you may not need to refetch the club object, etc, so continue to debug.
const address = await Address.create(addressToCreate);
let club = await Club.create(clubToCreate);
console.log('created club', club);
club.setAddress(address);
club.setUser(user);
await club.save();
console.log('saved club', club);
club = await Club.scope('withUser').findByPk(club.id);
console.log('reloaded club', club);
return club;

clean way to get same field by different key

Here is the problem. I can get member by ID and my query looks like below:
{
member(memberId:[1,2]) {
firstName
lastName
contacts {
}
}
}
Now I need to add few more query to get member by name and email like below
{
member(email:["abc#xy.com","adc#xy.com"]) {
firstName
lastName
contacts {
}
}
}
{
member(name:["abc","adc"]) {
firstName
lastName
contacts {
}
}
}
How do I design my graphQL query and schema? Should my query have just 1 field with multiple optional arguments? like below
Field("member", ListType(Member),
arguments = ids :: email :: name,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Or should I have multiple query with different field under a schema. like below
Field("memberById", ListType(Member),
arguments = Id :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByEmail", ListType(Member),
arguments = email :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Field("memberByName", ListType(Member),
arguments = name :: Nil,
resolve = (ctx) => {
val id : Seq[Int] = ctx.arg("memberId")
ctx.ctx.getMemberDetails(id)
})
Thank you in advance. let me know in case you need more details.
You should think about advantanges and disadvantages of both solutions.
If you will prepare separate fields, you will get a lot of boilerplate.
On the other hand you can set all possible inputs as OptionalInputType, it makes schema field only. Disadvantage of this solutions is that Sangria cannot validate a field that at least one argument should be required, so you have to cover this case with proper response or whatever.
The third option is to make generic solution at Schema level. You can create a query with two arguments filterName and filterValues, first would be EnumType for Id, Email, Name, the second would be a list of strings.
Such solution avoid disadvantages of both previous solutions, it has required fields and it doesn't need spreading fields in schema for every filter. Additionally if you want to add any additional function you have only edit FilterName enum and a resolver function to cover this.
Finally you schema will looks like this:
enum FilterName {
ID
EMAIL
NAME
}
type Query {
member(filterName: FilterName!, filterValues: [String]!): Member!
}

SQLite Swift db.prepare Optional() in statement values

In the SQLite Swift documentation there is reference to getting statement results directly. I have a lot of SQL queries prepared and I don't really want to refactor them. I would sooner use them as they are using db.prepare, as per below.
Statements with results may be iterated over.
let stmt = try db.prepare("SELECT id, email FROM users")
for row in stmt {
print("id: \(row[0]), email: \(row[1])")
// id: Optional(1), email: Optional("alice#mac.com")
}
The return values always have the "Optional()" around them. Is there a way we can just get the raw row values back without this?
Unwrap the values using ! after the variable as #stephencelis said:
let stmt = try db.prepare("SELECT id, email FROM users")
for row in stmt {
print("id: \(row[0]!), email: \(row[1]!)")
}
You may want to use https://github.com/groue/GRDB.swift. It lets you extract optionals or non-optionals, just as you wish:
for row in Row.fetch(db, "SELECT id, email FROM users") {
let id: Int64 = row.value(atIndex: 0)
let email: String = row.value(atIndex: 1)
print(id, email)
}
The type-safe API lets you declare expressions of non-optional types that, when pulled back from a statement, are not wrapped.
From the README:
let users = Table("users")
let id = Expression<Int64>("id")
let name = Expression<String?>("name")
let email = Expression<String>("email")
try db.run(users.create { t in
t.column(id, primaryKey: true)
t.column(name)
t.column(email, unique: true)
})
// CREATE TABLE "users" (
// "id" INTEGER PRIMARY KEY NOT NULL,
// "name" TEXT,
// "email" TEXT NOT NULL UNIQUE
// )
let insert = users.insert(name <- "Alice", email <- "alice#mac.com")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice#mac.com')
for user in db.prepare(users) {
println("id: \(user[id]), name: \(user[name]), email: \(user[email])")
// id: 1, name: Optional("Alice"), email: alice#mac.com
}
Note that both id and email, which are non-optional, are returned as such.

How to get linq result as string array?

I would like to convert a list of User's properties into strings array (for json receiver) like:
List<User> users = <..list of users from db...>
var jsonData = (
from user in users
select new { user.Id, user.Person.Lastname, user.Person.Firstname });
return Json(jsonData)
The result is an array named fields
[{"Id":1,"Lastname":"Doe","Firstname":"John"},{"Id":2,"Lastname":"Smith","Firstname":"Adam"},...]
but I would like it to be the array of arrays of plain strings like:
[["1","Doe","John"]
["2","Smith","Adam"], ...]
How to cast linq result to strings array?
var jsonData = from user in users
select new[] { user.Id.ToString(),
user.Person.Lastname,
user.Person.Firstname };
Alternatively, you can use the lambda syntax:
var jsonData = users.Select(user => new[] { user.Id.ToString(),
user.Person.Lastname,
user.Person.Firstname });
I was using an IQueryable as the starting point of creating an array of values, not a List<>, but in either case you can serialise an anonymous object array, instead of a string array, to get the same result without casting values to strings.:
e.g.
var jsonData = users.Select(user => new object[] {
user.Id,
user.Person.Lastname,
user.Person.Firstname
});
In my instance I was also using the result client-side with jQuery dataTable, so the result needed to be wrapped in another anonymous object (with the property name aadata).
e.g.
return Json(new { aaData = jsonData }, JsonRequestbehavior.AllowGet);
Hopefully this will help others that find this question looking for dataTable specific version of this problem (as I was).

Resources