mongoid adding additional results to a query - ruby-on-rails-4

So in Mongoid, I am sending as JSON a series of custom report templates. But the client also has some report types, which are always included.
So a User has many Groups and each group has many ReportTemplates
So when I run #user.report_templates I am given a hash like so ->
id: "the-snake-report"
name: "The Snake Report"
icon: null
location: null
active: "2014-06-20"
-field_templates: [
-{
-_id: {
$oid: "5391794d4d6163fcea360000"
}
mandatory: false
name: "How long was it?"
options: null
}
-{
-_id: {
$oid: "5391794d4d6163fcea370000"
}
mandatory: false
name: "Did it have any legs?"
options: null
}
-{
-_id: {
$oid: "5391794d4d6163fcea380000"
}
mandatory: false
name: "Did it any languages?"
-options: [
"English"
"Spanish"
"French"
]
}
]
}
-{
id: "bad-kids"
name: "Bad kids "
icon: null
location: null
active: "2014-06-19"
-field_templates: [
-{
-_id: {
$oid: "539167714d6163fcea2b0000"
}
mandatory: false
name: "fish"
-options: [
"one"
"two"
"three"
]
}
-{
-_id: {
$oid: "539167714d6163fcea2c0000"
}
mandatory: false
name: "The cat in the hat?"
options: null
}
-{
-_id: {
$oid: "539167714d6163fcea2d0000"
}
mandatory: true
name: "Are you ok?"
options: null
}
-{
-_id: {
$oid: "539167714d6163fcea2e0000"
}
mandatory: true
name: "Is it a house?"
-options: [
"A car"
"a boat"
]
}
]
}
etc.
So I would like to take that method such as #user.report_templates.includes(ReportTemplate.simple_report)
and have it include onto that hash. (Instead of keeping it in the DB, I would prefer to keep it as a method)
But I am not really sure what to do with the error:
TypeError: wrong argument type Hash (expected Module)
So I have also tried to make a helper (module) called SimpleReport
#user.report_templates.includes(ReportTemplate.simple_report)
But then I get a MOPED query
Thank you for helping.

Ok So I figured it out.
Basically taking the results.to_json and appending onto it my results.
render json: #template.as_json << ReportTemplate.simple_report

Related

Writing an if statement for the value in a dictionary

I'm taking an intro game design class and am writing a text adventure. I've set up a dictionary for the game map. For example:
"HALLWAY": {
"name": "HALLWAY",
"desc": "This is a hallway in a maze."
"exits": [{
"exit": "NORTH",
"target": "DEADEND"
}],
"items": []
Early on in the game you have the option to "TAKE" an item. IF the player has taken the item I want the target to be a different location (Not a dead end) ELSE the target is a dead end. How can I write the if/else statement into the value of the target? Or is there a more simple way of doing this?
<!DOCTYPE html>
<script>
'use strict';
let input_data = {
"HALLWAY":
{
"name": "HALLWAY"
, "desc": "This is a hallway in a maze."
, "exits": [{ "exit": "NORTH", "target": "DEADEND" }]
, "items": []
}};
//input_data["HALLWAY"]["exits"][0]["exit"] = "SOUTH";
// ABOVE STATEMENT CAN BE USED TO UPDATE THE VALUE
alert("Value of exit: " + input_data["HALLWAY"]["exits"][0]["exit"]);
alert("Value of target: " + input_data["HALLWAY"]["exits"][0]["target"]);
# Now assume that somehow the value of items has changed
# I am doing it manually
input_data["HALLWAY"]["exits"][0]["items"] = { "item1": "value1" };
alert("Value of items: " + input_data["HALLWAY"]["exits"][0]["items"]["item1"]);
# --One way is to ----- this is the condition that you check if the item has changed
if(input_data["HALLWAY"]["exits"][0]["items"] != null)
input_data["HALLWAY"]["exits"][0]["target"] = "target changed";
# ---- the other way --- would be to check the count of items list whether it has changed.
alert("Value of target: " + input_data["HALLWAY"]["exits"][0]["target"]);

How do I insert an optional field as null using AppSync Resolvers and Aurora?

I have an optional String field, notes, that is sometimes empty. If it's empty I want to insert null, otherwise I want to insert the string.
Here is my resolver -
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
#set($notes = $util.defaultIfNullOrEmpty($context.arguments.notes, 'null'))
"payload": {
"sql":"INSERT INTO things VALUES ('$id', :NOTES)",
"variableMapping": {
":NOTES" : $notes
},
"responseSQL": "SELECT * FROM things WHERE id = '$id'"
}
}
With this graphql
mutation CreateThing{
createThing() {
id
notes
}
}
I get -
{
"data": {
"createRoll": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "null"
}
}
}
when I really want null without the quotes.
And with this graphql -
mutation CreateThing{
createThing(notes: "Here are some notes") {
id
notes
}
}
I get -
{
"data": {
"createThing": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "Here are some notes"
}
}
}
which is what I want.
How do I get a quoteless null and a quoted string into the same field?
TL;DR you should use $util.toJson() to print the $context.arguments.notes correctly. Replace your $notes assignment with
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Explanation:
The reason is VTL prints whatever the toString() method returns and your call to
$util.defaultIfNullOrEmpty($context.arguments.notes, 'null') will return the string "null", which will be printed as "null".
If you replace with $util.defaultIfNullOrEmpty($context.arguments.notes, null) then it will return a null string. However, VTL will print $notes because that is the way it handles null references. In order to print null, which is the valid JSON representation of null, we have to serialize it to JSON. So the correct statement is:
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Full test:
I'm assuming you started with the RDS sample provided in the AWS AppSync console and modified it. To reproduce, I updated the content field in the Schema to be nullable:
type Mutation {
...
createPost(author: String!, content: String): Post
...
}
type Post {
id: ID!
author: String!
content: String
views: Int
comments: [Comment]
}
and I modified the posts table schema so content can also be null there: (inside the Lambda function)
function conditionallyCreatePostsTable(connection) {
const createTableSQL = `CREATE TABLE IF NOT EXISTS posts (
id VARCHAR(64) NOT NULL,
author VARCHAR(64) NOT NULL,
content VARCHAR(2048),
views INT NOT NULL,
PRIMARY KEY(id))`;
return executeSQL(connection, createTableSQL);
}
This is the request template for the createPost mutation:
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
"payload": {
"sql":"INSERT INTO posts VALUES ('$id', :AUTHOR, :CONTENT, 1)",
"variableMapping": {
":AUTHOR" : "$context.arguments.author",
":CONTENT" : $util.toJson($util.defaultIfNullOrEmpty($context.arguments.content, null))
},
"responseSQL": "SELECT id, author, content, views FROM posts WHERE id = '$id'"
}
}
and response template:
$util.toJson($context.result[0])
The following query:
mutation CreatePost {
createPost(author: "Me") {
id
author
content
views
}
}
returns:
{
"data": {
"createPost": {
"id": "b42ee08c-956d-4b89-afda-60fe231e86d7",
"author": "Me",
"content": null,
"views": 1
}
}
}
and
mutation CreatePost {
createPost(author: "Me", content: "content") {
id
author
content
views
}
}
returns
{
"data": {
"createPost": {
"id": "c6af0cbf-cf05-4110-8bc2-833bf9fca9f5",
"author": "Me",
"content": "content",
"views": 1
}
}
}
We were looking into the same issue. For some reason, the accepted answer does not work for us. Maybe because it's a beta feature and there is a new resolver version (2018-05-29 vs 2017-02-28, changes here: Resolver Mapping Template Changelog).
We use this for the time being using NULLIF():
{
"version": "2018-05-29",
"statements": [
"INSERT INTO sales_customers_addresses (`id`, `customerid`, `type`, `company`, `country`, `email`) VALUES (NULL, :CUSTOMERID, :TYPE, NULLIF(:COMPANY, ''), NULLIF(:COUNTRY, ''), :EMAIL)"
],
"variableMap": {
":CUSTOMERID": $customerid,
":TYPE": "$type",
":COMPANY": "$util.defaultIfNullOrEmpty($context.args.address.company, '')",
":COUNTRY": "$util.defaultIfNullOrEmpty($context.args.address.country, '')",
":EMAIL": "$context.args.address.email"
}
}

route53 list hosted zones output throws "does not support indexing" error

I can't parse the output of the response below.
When I include the line:
"fmt.Println(*r["HostedZones"][0])"
it throws:
"type *route53.ListHostedZonesOutput does not support indexing".
I'd like to retrieve the "Id" and "Name" of each zone in the output. If the type doesn't support indexing, how can I retrieve the parts of the output I need?
Thank you.
package main
import (
"log"
"fmt"
"reflect"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)
func main() {
r53 := route53.New(session.New())
r, err := r53.ListHostedZones(&route53.ListHostedZonesInput{})
if err != nil {
log.Fatal(err)
}
log.Println(r)
fmt.Println(reflect.TypeOf(r))
fmt.Println(*r["HostedZones"][0])
}
sample output:
{
HostedZones: [{
CallerReference: "5E95CADD-59E-A6",
Config: {
PrivateZone: false
},
Id: "/hostedzone/Z1Q1TZTO",
Name: "testzone.local.",
ResourceRecordSetCount: 4
},{
CallerReference: "39895A3C-9B8B-95C2A3",
Config: {
PrivateZone: false
},
Id: "/hostedzone/Z2MXJQ7",
Name: "2.168.192.in-addr.arpa.",
ResourceRecordSetCount: 3
}],
IsTruncated: false,
MaxItems: "100"
}
This is an example of how to get the Id:
fmt.Println(*r.HostedZones[0].Id)
Name:
fmt.Println(*r.HostedZones[0].Name)
you are probably trying to index with the pointer to your struct, your r is a pointer, you can get the deferenced value by doing (*r)["yourkeyhere"]["index"].

How to resolve parent to child relationship with AppSync

I have schema looking like below
type Post {
id: ID!
creator: String!
createdAt: String!
like: Int!
dislike: Int!
frozen: Boolean!
revisions:[PostRevision!]
}
type PostRevision {
id: ID!
post: Post!
content: String!
author: String!
createdAt: String!
}
type Mutation {
createPost(postInput: CreatePostInput!): Post
}
I would like to be able to batch insert Post and PostRevision at the same time when i run createPost mutation; however, VTL is giving me a much of hard time.
I have tried below
## Variable Declarations
#set($postId = $util.autoId())
#set($postList = [])
#set($postRevisionList = [])
#set($post = {})
#set($revision = {})
## Initialize Post object
$util.qr($post.put("creator", $ctx.args.postInput.author))
$util.qr($post.put("id", $postId))
$util.qr($post.put("createdAt", $util.time.nowEpochMilliSeconds()))
$util.qr($post.put("like", 0))
$util.qr($post.put("dislike", 0))
$util.qr($post.put("frozen", false))
## Initialize PostRevision object
$util.qr($revision.put("id", $util.autoId()))
$util.qr($revision.put("author", $ctx.args.postInput.author))
$util.qr($revision.put("post", $postId))
$util.qr($revision.put("content", $ctx.args.postInput.content))
$util.qr($revision.put("createdAt", $util.time.nowEpochMilliSeconds()))
## Listify objects
$postList.add($post)
$postRevisionList.add($revision)
{
"version" : "2018-05-29",
"operation" : "BatchPutItem",
"tables" : {
"WHISPR_DEV_PostTable": $util.toJson($postList),
"WHISPR_DEV_PostRevisionTable": $util.toJson($postRevisionList)
}
}
So basically I am reconstructing the document in the resolver of createPost so that I can add Post then also add ID of the post to postReivision However when I run below code
mutation insertPost{
createPost(postInput:{
creator:"name"
content:"value"
}){
id
}
}
I get following error
{
"data": {
"createPost": null
},
"errors": [
{
"path": [
"createPost"
],
"data": null,
"errorType": "MappingTemplate",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "Expected JSON object but got BOOLEAN instead."
}
]
}
What am I doing wrong?
I know it would be easier to resolve with lambda function but I do not want to double up the cost for no reason. Any help would be greatly appreciated. Thanks!
If anyone still needs the answer for this (this question is still the #1 google hit for the mentioned error message):
The problem is the return value of the add() method, which returns a boolean value.
To fix this, just wrap the add() methods into $util.qr, as you are already doing for the put() methods:
$util.qr(($postList.add($post))
$util.qr(($postRevisionList.add($revision))
It looks like you are missing a call to $util.dynamodb.toDynamoDBJson which is causing AppSync to try to put plain JSON objects into DynamoDB when DynamoDB requires a DynamoDB specific input structure where each attribute instead of being a plain string like "hello world!" is an object { "S": "hello world!" }. The $util.dynamodb.toDynamoDBJson helper handles this for you for convenience. Can you please try adding the toDynamoDBJson() to these lines:
## Listify objects
$postList.add($util.dynamodb.toDynamoDBJson($post))
$postRevisionList.add($util.dynamodb.toDynamoDBJson($revision))
Hope this helps :)

Regex in Mongodb for ISO Date field

How can I pick all the dates with time value as 00:00:00 despite the date value? Regex doesn't work for me.
{
"_id" : ObjectId("59115a92bbf6401d4455eb21"),
"name" : "sfdfsdfsf",
"create_date" : ISODate("2013-05-13T02:34:23.000Z"),
}
something like :
db.myCollection.find({"create_date": /*T00:00:00.000Z/ })
You need to first convert created date into string of time, and if time is 00:00:00:000, then include the document.
db.test.aggregate([
// Part 1: Project all fields and add timeCriteria field that contain only time(will be used to match 00:00:00:000 time)
{
$project: {
_id: 1,
name: "$name",
create_date: "$create_date",
timeCriteria: {
$dateToString: {
format: "%H:%M:%S:%L",
date: "$create_date"
}
}
}
},
// Part 2: match the time
{
$match: {
timeCriteria: {
$eq: "00:00:00:000"
}
}
},
// Part 3: re-project document, to exclude timeCriteria field.
{
$project: {
_id: 1,
name: "$name",
create_date: "$create_date"
}
}
]);
From MongoDB version >= 4.4 we can write custom filters using $function operator.
Note: Donot forget to chage the timezone to your requirement. Timezone is not mandatory.
let timeRegex = /.*T00:00:00.000Z$/i;
db.myCollection.find({
$expr: {
$function: {
body: function (createDate, timeRegex) {
return timeRegex.test(createDate);
},
args: [{ $dateToString: { date: "$create_date", timezone: "+0530" } }, timeRegex],
lang: "js"
}
}
});