$match aggregate in mongoose - http

I am using $match to find a userid that is reference in my schema. When I pass in http://localhost:3001/api/orders/5d6f1611d3bcd442bc273211/2018-02-01/2019-09-01 I get the following:
5d6f1611d3bcd442bc273211
[0] 2018-02-01
[0] 2019-09-01
[0] ReferenceError: $match is not defined
Here is my code:
getOrderTotal: function (req, res) {
const userID = req.params.userid;
const firstDate = req.params.firstdate;
const secondDate = req.params.seconddate;
console.log(userID);
console.log(firstDate);
console.log(secondDate);
Order
.aggregate([{ $match: { 'user': userID } }])
.find({ "created_at": { "$gte": firstDate, "$lt": secondDate } })
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
}
When I remove the line containing $match, I get the expected results. When I leave it in, I get an empty array.

convert userId to ObjectID
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(userID);
Then use id in your $match aggregation
or
.aggregate([{ $match: { 'user': mongoose.Types.ObjectId(userID) } }])

You can use $find and $match at one time using expr.
const userID = req.params.userid;
const firstDate = req.params.firstdate;
const secondDate = req.params.seconddate;
console.log(userID);
console.log(firstDate);
console.log(secondDate);
Order.find({
"$expr": {
"$and": [
{ "$eq": ["$user", userID] },
{ "$gte": [{ "$created_at": firstDate }]},
{ "$lt": [{ "$created_at": secondDate }]}
]
}
}).exec((err , found)=>{
});

I am guessing this is what you want to achieve
Find document that has user matching userId
and has date greater than or equal to firstDate and less than secondDate
If you insist on using the aggregate pipeline plus $match operator, you must then be aware of the following
Note:
The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned). More info here: https://mongoosejs.com/docs/api/aggregate.html
Example:
getOrderByDate: async (req, res) => {
// Get the parameter data from req.params
let { user, firstDate, secondDate } = req.params;
// Convert the user data type to mongoose ObjectId
user = mongoose.Types.ObjectId(user);
// Convert both firstDate and SecondDate to date
firstDate = new Date(firstDate);
firstDate.setHours(0, 0, 0, 0);
secondDate = new Date(secondDate);
secondDate.setHours(23, 59, 59, 999);
// See the output in your console if you will...
console.log({ user, firstDate, secondDate });
// Using async (you can still use promises if you will)
try {
const matchedDocuments = await OrderDocument.aggregate([{ $match: { user: user } }]);
// Filter returned document
const result = matchedDocuments.filter((docs) => {
if (docs.created_at >= firstDate && docs.created_at < secondDate) {
return true;
}
});
res.status(200).json({ result });
} catch (error) {
res.status(500).json({ error, message: 'Something went wrong' });
}
}

Related

alexa sdk: can't get persitentAttributes

i'm trying to add persistent attributes to my lambda function.
i created a dynamoDB table and added it to the triggers of my lambda function.
i copied a sample code from github, but when i try to launch the skill i get an error. The console log shows:
{
"errorMessage": "Could not read item (amzn1.ask.account.AGIIYNRXWDLBD6XEPW72QS2BHGXNP7NWYBEWSH2XLSXZP64X3NCYEMVK233VFDWH77ZB6DAK6YJ53SZLNUFVQ56CYOVCILS7QFZI4CIRDWC3PAHS4QG27YUY5PTT6QEIK46YFNTJT54YAKNGOWV2UO66XZACFDQ5SEXKJYOBNFNIZNUXKNTIAAYZG4R5ZU4FMLPDZZN64KLINNA) from table (Spiele): The provided key element does not match the schema",
"errorType": "AskSdk.DynamoDbPersistenceAdapter Error",
"stackTrace": [
"Object.createAskSdkError (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/utils/AskSdkUtils.js:22:17)",
"DynamoDbPersistenceAdapter.<anonymous> (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:123:49)",
"step (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:44:23)",
"Object.throw (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:25:53)",
"rejected (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:17:65)",
"<anonymous>",
"process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
the table contains a primary key "name" and sort key "UserId". is that wrong?
here is my index.js:
const Alexa = require('ask-sdk');
// Define the skill features
let skill;
/**
* If this is the first start of the skill, grab the user's data from Dynamo and
* set the session attributes to the persistent data.
*/
const GetUserDataInterceptor = {
process(handlerInput) {
let attributes = handlerInput.attributesManager.getSessionAttributes();
if (handlerInput.requestEnvelope.request.type === 'LaunchRequest' && !attributes['isInitialized']) {
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((attributes) => {
attributes['isInitialized'] = true;
saveUser(handlerInput, attributes, 'session');
resolve();
})
.catch((error) => {
reject(error);
})
});
}
}
};
function saveUser(handlerInput, attributes, mode) {
if(mode === 'session'){
handlerInput.attributesManager.setSessionAttributes(attributes);
} else if(mode === 'persistent') {
console.info("Saving to Dynamo: ",attributes);
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((persistent) => {
delete attributes['isInitialized'];
handlerInput.attributesManager.setPersistentAttributes(attributes);
resolve(handlerInput.attributesManager.savePersistentAttributes());
})
.catch((error) => {
reject(error);
});
});
}
}
const LaunchHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
console.info("LaunchRequest");
let attributes = handlerInput.attributesManager.getSessionAttributes();
console.info("Test the load: " + attributes['isInitialized']);
attributes['FOO'] = "BAR";
saveUser(handlerInput, attributes, 'persistent');
return handlerInput.responseBuilder
.speak('Hello')
.reprompt('Hello')
.getResponse();
}
}
exports.handler = Alexa.SkillBuilders.standard()
.addRequestHandlers(
LaunchHandler
)
.addRequestInterceptors(GetUserDataInterceptor)
.withTableName('Spiele')
.withAutoCreateTable(true)
.withDynamoDbClient()
.lambda();
can anyone tell me what i'm doing wrong?
please confirm the partition key is 'userId' not 'UserId' (notice the uppercase U).
Also I would suggest using 'this' object.
Let me know if that helps.
Cheers
Below code is for python lambda function
from ask_sdk_core.skill_builder import CustomSkillBuilder
from ask_sdk_dynamodb.adapter import DynamoDbAdapter
sb = SkillBuilder()
sb = CustomSkillBuilder(persistence_adapter = dynamodb_adapter)

How to access deep value of realtime db with cloud function?

In childSnapshot.val().k I have this with cloud function:
{ '-LdmZIlKZh3O9cR8MOBU':
{ id: 'ceccomcpmoepincin3ipwc',
k: 'test',
p: 'somepath',
t: 1556700282278,
u: 'username' },
'-Llkocp3ojmrpemcpo3mc':
{ id: '[epc[3pc[3m,',
k: 'test2',
p: 'somepath2',
t: 1556700292290,
u: 'username2' }
}
I need each path value so I can delete that file from storage. How to access this value?
My cloud function for refreshing states, removing and deleting files from storage:
var db = admin.database();
var ref = db.ref('someref');
ref.once("value").then((snapshot) => {
var updates = {};
var patObject = {
fid: null,
ft: null,
ftr: null,
fu: null,
id: null,
lid: null,
lt: null,
ltr: null,
lu: null,
t: null,
tr: null,
v: null,
g: null,
l: null,
k: null
};
snapshot.forEach((childSnapshot) => {
if(childSnapshot.numChildren() >= 14){
var t = childSnapshot.val().t;
if((t===1 || t===5) && childSnapshot.val().tr > 0) {
if(childSnapshot.val().tr - 12 > 0){
updates[childSnapshot.key + '/tr'] = childSnapshot.val().tr - 12;
if(childSnapshot.val().k !== ""){
console.log('path: ', childSnapshot.val().k);
childSnapshot.val().k.snapshot.forEach(kpath => {
console.log('path: ', "path");
});
}
} else {
updates[childSnapshot.key] = patObject;
}
}
if(childSnapshot.val().tr<=0){
updates[childSnapshot.key] = patObject;
}
} else {
updates[childSnapshot.key] = patObject;
}
});
ref.update(updates);
res.send("");
return "";
}).catch(reason => {
res.send(reason);
})
return "";
If you want to delete all the files corresponding to the values of the ps, you need to use Promise.all() to execute in parallel the asynchronous deletion tasks (Since the delete() method returns a Promise). You need to iterate over the object that contains the p paths.
It is not easy to understand your code, so you'll find below the part corresponding to the above explanations. It's up to you to integrate it in your code!
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const defaultStorage = admin.storage(); //Note this line
//.....
exports.date = functions.https.onRequest((req, res) => { //I understand that you use an HTTP Cloud Function
//....
.then(...
// Somehow you get the object you mention in your question, through childSnapshot.val().k
const kObject = childSnapshot.val().k;
const bucket = defaultStorage.bucket(yourFileBucket);
const promises = [];
Object.keys(kObject).forEach(
//The values of the path p are obtained via kObject[key].p
//Based on that we push the Promise returned by delete() to the promises array
promises.push(bucket.file(kObject[key].p).delete());
);
return Promise.all(promises)
.then(results => {
//Here all the Promises that were in the promises array are resolved, which means that all the files are deleted
res.send({result: results.length + ' files(s) deleted'});
})
.catch(error => {
res.status(500).send(error);
});
});
Watch may be interested by watching the following official Firebase video by Doug Stevenson: https://youtu.be/7IkUgCLr5oA

How to dynamically update an attribute in a dynamodb item?

I created an item in dynamodb using Node js, the item has multiple attributes such as brand, category, discount, validity, etc. I am using uuid to generate ids for each item. Now let's say I want to update the validity attribute of the item, in which case I am currently sending the entire json object with the value of validity modified to the new value.
This is definitely not optimal, please help me find an optimal solution.
const params = {
TableName: process.env.PRODUCT_TABLE,
Key: {
id: event.pathParameters.id,
},
ExpressionAttributeNames: {
'#discount': 'discount',
},
ExpressionAttributeValues: {
':brand': data.brand,
':category': data.category,
':discount': data.discount,
':denominations': data.denominations,
":validity": data.validity,
":redemption": data.redemption
},
UpdateExpression: 'SET #discount = :discount, denominations = :denominations, brand = :brand, category = :category, validity = :validity, redemption = :redemption',
ReturnValues: 'ALL_NEW',
};
I want to send just the attribute I want to update with the new value, if I want to change the validity from 6 months to 8 months, I should just send something like:
{
"validity": "8 months"
}
And it should update the validity attribute of the item.
Same should apply to any other attribute of the item.
'use strict';
const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.update = (event, context, callback) => {
const data = JSON.parse(event.body);
let attr = {};
let nameobj = {};
let exp = 'SET #';
let arr = Object.keys(data);
let attrname = {};
arr.map((key) => {attr[`:${key}`]=data[key]});
arr.map((key) => {
exp += `${key} = :${key}, `
});
arr.map((key) => {nameobj[`#${key}`]=data[key]});
attrname = {
[Object.keys(nameobj)[0]] : nameobj[Object.keys(nameobj)[0]]
}
const params = {
TableName: process.env.PRODUCT_TABLE,
Key: {
id: event.pathParameters.id,
},
ExpressionAttributeNames: attrname,
ExpressionAttributeValues: attr,
UpdateExpression: exp,
ReturnValues: 'ALL_NEW',
};
// update the todo in the database
dynamoDb.update(params, (error, result) => {
// handle potential errors
if (error) {
console.error(error);
callback(null, {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t update the card',
});
return;
}
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(result.Attributes),
};
callback(null, response);
});
};
Contrary to others comments, this is very possible, use the UpdateItem action.
Language agnostic API docs
JavaScript specific API docs
If you want to dynamically create the query, try something like this:
const generateUpdateQuery = (fields) => {
let exp = {
UpdateExpression: 'set',
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
}
Object.entries(fields).forEach(([key, item]) => {
exp.UpdateExpression += ` #${key} = :${key},`;
exp.ExpressionAttributeNames[`#${key}`] = key;
exp.ExpressionAttributeValues[`:${key}`] = item
})
exp.UpdateExpression = exp.UpdateExpression.slice(0, -1);
return exp
}
let data = {
'field' : { 'subfield': 123 },
'other': '456'
}
let expression = generateUpdateQuery(data)
let params = {
// Key, Table, etc..
...expression
}
console.log(params)
Output:
{
UpdateExpression: 'set #field = :field, #other = :other',
ExpressionAttributeNames: {
'#field': 'field',
'#other': 'other'
},
ExpressionAttributeValues: {
':field': {
'subfield': 123
},
':other': '456'
}
}
Using Javascript SDK V3:
Import from the right package:
import { DynamoDBClient PutItemCommandInput, UpdateItemCommandInput, UpdateItemCommand } from '#aws-sdk/client-dynamodb';
Function to dynamically do partial updates to the item:
(the code below is typescript can be easily converted to Javascript, just remove the types!)
function updateItem(id: string, item: any) {
const dbClient = new DynamoDBClient({region: 'your-region-here });
let exp = 'set ';
let attNames: any = { };
let attVal: any = { };
for(const attribute in item) {
const valKey = `:${attribute}`;
attNames[`#${attribute}`] = attribute;
exp += `#${attribute} = ${valKey}, `;
const val = item[attribute];
attVal[valKey] = { [getDynamoType(val)]: val };
}
exp = exp.substring(0, exp.length - 2);
const params: UpdateItemCommandInput = {
TableName: 'your-table-name-here',
Key: { id: { S: id } },
UpdateExpression: exp,
ExpressionAttributeValues: attVal,
ExpressionAttributeNames: attNames,
ReturnValues: 'ALL_NEW',
};
try {
console.debug('writing to db: ', params);
const command = new UpdateItemCommand(params);
const res = await dbClient.send(command);
console.debug('db res: ', res);
return true;
} catch (err) {
console.error('error writing to dynamoDB: ', err);
return false;
}
}
And to use it (we can do partial updates as well):
updateItem('some-unique-id', { name: 'some-attributes' });
What i did is create a helper class.
Here is a simple function : Add all the attribute and values that goes into, if the value is null or undefined it won't be in the expression.
I recommande to create a helper class with typescript and add more functions and other stuff like generator of expressionAttributeValues , expressionAttributeNames ... , Hope this help.
function updateExpression(attributes, values) {
const expression = attributes.reduce((res, attribute, index) => {
if (values[index]) {
res += ` #${attribute}=:${attribute},`;
}
return res;
}, "SET ");
return expression.slice(0, expression.length - 1)
}
console.log(
updateExpression(["id", "age", "power"], ["e8a8da9a-fab0-55ba-bae3-6392e1ebf624", 28, undefined])
);
You can use code and generate the params object based on the object you provide. It's just a JavaScript object, you walk through the items so that the update expression only contains the fields you have provided.
This is not really a DynamoDB question in that this is more a general JS coding question.
You can use UpdateItem; to familiarize yourself with DynamoDb queries I would suggest you DynamoDb NoSQL workbench:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html
It can generate snippets for you based on your queries.
DynamoDb NoSQL workbench screenshot query

Firestore query with multiple where clauses based on parameters

I want to query a Firestore database with multiple where clauses based on the parameters that are passed in. The following block of code works:
getProducts2(accountId: string, manufacturer?: string, materialType?: string): Promise<Product[]> {
return new Promise<Product[]>((resolve, reject) => {
const productCollection2: AngularFirestoreCollection<FreightRule> = this.afs.collection('products');
const query = productCollection2.ref
.where('materialType', '==', materialType)
.where('manufacturer', '==', manufacturer);
query.get().then(querySnapshot => {
if (querySnapshot.size > 0) {
const data = querySnapshot.docs.map(documentSnapshot => {
return documentSnapshot.data();
}) as Product[];
resolve(data);
} //todo else...
});
});
}
But what I really want to do is conditionally include the where clauses based on the optional parameters. The following is what I want, but it doesn't work:
getProducts2(accountId: string, manufacturer?: string, materialType?: string): Promise<Product[]> {
return new Promise<Product[]>((resolve, reject) => {
const productCollection2: AngularFirestoreCollection<FreightRule> = this.afs.collection('products');
const query = productCollection2.ref;
if (manufacturer) {
query.where('manufacturer', '==', manufacturer);
}
if (materialType) {
query.where('materialType', '==', materialType);
}
query.get().then(querySnapshot => {
if (querySnapshot.size > 0) {
const data = querySnapshot.docs.map(documentSnapshot => {
return documentSnapshot.data();
}) as Product[];
resolve(data);
} //todo else...
});
});
}
While valid, this code just returns all of the products with no filtering.
Is there a way to structure this so I can filter based on the optional parameters?
edit: I realize I can do something like:
let query;
if (manufacturer && materialType) {
query = productCollection2.ref.where(....).where(....)
} else if (manufacturer) {
query = productCollection2.ref.where(....)
} else if (materialType) {
query = productCollection2.ref.where(....)
}
I was just hoping for something a little more elegant.
Build upon the prior query, don't repeat the prior query:
let query = collection // initial query, no filters
if (condition1) {
// add a filter for condition1
query = query.where(...)
}
if (condition2) {
// add a filter for condition2
query = query.where(...)
}
// etc
If using different query structure, You can try below ways:
db.collection("subscriptions").where("email", '==', req.body.email,"&&","subscription_type","==","free").get();
OR
db.collection("subscriptions").where("email", '==', req.body.email).where("subscription_type", '==', 'free111').get();

How do i convert a single json record retrieved from firebase into an array

I am trying to take a single record from firebase to use in vuejs but I cant find out how to convert it to an array, if thats even what i should be doing.
my mutation
GET_CASE(state, caseId) {
state.caseId = caseId;
},
My action
getCase ({ commit, context }, data) {
return axios.get('http' + data + '.json')
.then(res => {
const convertcase = []
convertcase.push({ data: res.data })
//result below of what is returned from the res.data
console.log(convertcase)
// commit('GET_CASE', convertcase)
})
.catch(e => context.error(e));
},
I now get the following returned to {{ myCase }}
[ { "data": { case_name: "Broken laptop", case_status: "live", case_summary: "This is some summary content", contact: "", createdBy: "Paul", createdDate: "2018-06-21T15:20:22.932Z", assessor: "Gould", updates: "" } } ]
when all i want to display is Broken Laptop
Thanks
Example let obj = {a: 1, b: 'a'); let arr = Object.values(obj) // arr = [1, 'a']
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', Object.values(data))
} catch (error) {
context.error(error)
}
}
But as I'm reading your post again, I think you don't want array from object. You want array with one object. So, maybe this is what you want:
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', [data])
} catch (error) {
context.error(error)
}
}
Put this inside / after your .then
Object.keys(data).forEach(function(k, i) {
console.log(k, i);
});
With a response from Axios, you can get your data as:
res.data.case_name
res.data.case_number
....
Just build JavaScript object holding these properties and pass this object to your mutation. I think it is better than using an array.
const obj = {};
Object.assign(obj, res.data);
commit('GET_CASE', obj)
And in your mutation you do as follows:
mutations: {
GET_CASE (state, payload) {
for (var k in payload) {
if (payload.hasOwnProperty(k)) {
state[k] = payload[k]
}
}
}
}
Alternatively you can code your store as follows:
state: {
case: {},
...
},
getters: {
getCase: state => {
return state.case
},
....
},
mutations: {
GET_CASE (state, payload) {
state.case = payload
}
}
and you call the value of a case field form a component as follows:
const case = this.$store.getters.getCase
..... = case.case_name

Resources