I am trying to get specific object value from items array. I am updating array by pushing new items into items array and then I want to get last updated item from an array. I have _id of the item that is last updated and using this query but it's returning complete record with all items.
let repData = Items.findOne({'_id': rid, 'items._id': newRepId },{ "items.$": 1 });
{
"errorCode": false,
"data": {
"_id": "NhNpaN8EHn6uJXfg5",
"cText": "Let me know you views",
"aId": "YxEjFqsE3czZZJvgP",
"cId": "EwDS5iYSEuGThHE38",
"datePosted": "2019-07-11T07:13:59.177Z",
"items": [
{
"reText": "pppppp sssssssss",
"reOwn": "FK7KQ7eMs7QvX5uHh",
"datePosted": "2019-07-11T07:19:17.722Z",
"_id": "b87e532807ce37ff83d37a09"
},
{
"reText": "bbbbbb vvvvvvvv",
"reOwn": "FK7KQ7eMs7QvX5uHh",
"datePosted": "2019-07-11T07:22:36.089Z",
"_id": "28d2bf66a517bfcfaa0fabca"
},
{
"reText": "mmmmmnnnnnn bbbbbbvvvvv",
"reOwn": "FK7KQ7eMs7QvX5uHh",
"datePosted": "2019-07-11T07:23:20.587Z",
"_id": "85f52e3e3a8ae6d18e98dbb2"
},
}
}
The Mongo search will always get you the complete record, you can restrict the elements that you fetch, but you can't filter the items array, you will have to do that afterwards, something like this:
const lastItem = repData.items
.sort((a, b) => new Date(a.datePosted)-new Date(b.datePosted))
.pop()
console.log(lastItem)
Basically it sorts the array by date (perhaps not necessary), and then pops the last element into lastItem
Related
I'm trying with the following code
{
ExpressionAttributeNames: {
"#items": "items"
},
ExpressionAttributeValues: {
":item": [slug]
},
Key: {
listId: listId,
userId: userData.userId,
},
UpdateExpression: "SET #items = list_append(#items,:item)",
ConditionExpression: "NOT contains (#items, :item)",
TableName: process.env.listsTableName,
}
but the item is still added even if string already exists in the list. What am I doing wrong?
The list structure is like so:
{
Item: {
userId: userData.userId,
listId: crypto.createHash('md5').update(Date.now() + userData.userId).digest('hex'),
listName: 'Wishlist',
items: [],
},
TableName: process.env.listsTableName,
};
Later Edit: I know I should use SS as it does the condition for me but SS doesn't work in my context because SS can't be empty.
As the documentation explains, the contains() function only works on a string value (checking for a substring) or a set value (checking for membership). But in your case, you don't have a set but rather a list - with are different things in DynamoDB.
If all the items which you want to add to this list are strings, and you anyway don't want duplicates in the list, the most efficient way would be to stop using a list, and instead use the set-of-strings (a.k.a. SS) type. To add an item to the set (without duplicates), you would simply use "ADD #items :item" (no need for any additional condition - duplicates will not be added).
Referring to the default sample schema mentioned in https://hasura.io/hub/project/hasura/hello-world/data-apis i.e. to the following two tables:
1) author: id,name
2) article: id, title, content, rating, author_id
where article:author_id has an array relationship to author:id.
How do I make a query to select authors who have written at least one article? Basically, something like select author where len(author.articles) > 0
TL;DR:
There's no length function that you can use in the Hasura data API syntax right now. Workaround 1) filter on a property that is guaranteed to be true for every row. Like id > 0. 2) Build a view and expose APIs on your view.
Option 1:
Use an 'always true' attribute as a filter.
{
"type": "select",
"args": {
"table": "author",
"columns": [
"*"
],
"where": {
"articles": {
"id": {
"$gt": "0"
}
}
}
}
}
This reads as: select all authors where ANY article has id > 0
This works because id is an auto-incrementing int.
Option 2:
Create a view and then expose data APIs on them.
Head to the Run SQL window in the API console and run a migration:
CREATE VIEW author_article_count as (
SELECT au.*, ar.no_articles
FROM
author au,
(SELECT author_id, COUNT(*) no_articles FROM article GROUP BY author_id) ar
WHERE
au.id = ar.author_id)
Make sure you mark this as a migration (a checkbox below the RunSQL window) so that this gets added to your migrations folder.
Now add data APIs to the view, by hitting "Track table" on the API console's schema page.
Now you can make select queries using no_articles as the length attribute:
{
"type": "select",
"args": {
"table": "author_article_count",
"columns": [
"*"
],
"where": {
"no_articles": {
"$gt": "0"
}
}
}
}
Consider the following document item / syntax in a DynamoDB table:
{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1",
"controllers": [
{
"id": "93014cf0-bb05-4fbb-9466-d56ff51b1d22",
"routes": [
{
"direction": "N",
"cars": 0,
"sensors": [
{
"id": "e82c45a3-d356-41e4-977e-f7ec947aad46",
"light": true,
},
{
"id": "78a6883e-1ced-4727-9c94-2154e0eb6139",
}
]
}
]
}
]
}
My goal is to update a single attribute in this JSON representation, in this case cars.
My approach
I know all the sensors IDs. So, the easiest way to reach that attribute is to find, in the array, the route which has a sensor with any of the ids. Having found that sensor, Dynamo should know which object in the routes array he has to update. However, I cannot run this code without my condition being rejected.
In this case, update attribute cars, where the route has a sensor with id e82c45a3-d356-41e4-977e-f7ec947aad46 or 78a6883e-1ced-4727-9c94-2154e0eb6139.
var params = {
TableName: table,
Key:{
"id": "0f00b15e-83ee-4340-99ea-6cb890830d96",
"name": "region-1"
},
UpdateExpression: "set controllers.intersections.routes.cars = :c",
ConditionExpression: ""controllers.intersections.routes.sensors.id = :s",
ExpressionAttributeValues:{
":c": 1,
":s": "e82c45a3-d356-41e4-977e-f7ec947aad46"
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, ...);
How can I achieve this?
Unfortunately, you can't achieve this in DynamoDB without knowing the array index. You have very complex nested structure. The DynamoDB API doesn't have a feature to handle this scenario.
I think you need the array index for controllers, routes and sensors to get the update to work.
Your approach may work in other databases like MongoDB. However, it wouldn't work on DynamoDB. Generally, it is not recommended to have this complex structure in DynamoDB especially if your use case has update scenario.
TableName : 'tablename',
Key : { id: id},
ReturnValues : 'ALL_NEW',
UpdateExpression : 'set someitem['+`index`+'].somevalue = :reply_content',
ExpressionAttributeValues : { ':reply_content' : updateddata }
For updating nested array element need to fing out array index . Then you can update nested array element in dynamo db.
I have a meteor publication in which I was trying to use .findOne() to access the fields from the mongodb. But as I cannot return .findOne() from a meteor publish, I am now using just .find(). The problem is that it returns a cursor instead of a document so I am not able to read the values inside that cursor in my publish function.
Below is my mongodb query
var question = Question.find({ "_id": quesId },
{ fields: {"pages": 1, "settings.name": 1, "status": 1 }},
{limit: 1});
And I want to use the value of pages that I get from the above query inside the same publish function
you can set an observer on your cursor and get a hook into the results. i do that frequently to transform the result.
e.g. (i am making the assumption that the publish name is "questions")
let cursor = Question.find({ "_id": quesId }, { "pages": 1, "settings.name": 1, "status": 1 }, {limit: 1});
let handle = cursor.observeChanges({
added: (id, fields) => {
// look at fields
this.added('questions', id, fields);
},
changed: (id, fields) => {
// look at fields
this.changed('questions', id, fields);
},
removed: (id) => {
this.removed('questions', id);
}
});
this.ready();
this.onStop(() => {
handle.stop();
});
this will give you a hook into the records that are added at the initial publish, and changed later. call ready() when your publication is ready to be published (you can see the this.ready() in the sample).
As far as I understand your question, you want to access the single document in the cursor without doing a redundant findOne() in your code. You can transform the result into an array with .fetch() on the cursor, get the first entry with [0] and then directly get the pages attribute.
const question = Question.find(quesId,
{ fields: { "pages": 1, "settings.name": 1, "status": 1 }});
const pages = question.fetch()[0].pages;
Note also that when searching on _id you don't have to specify {_id: quesId} in your filter, you can directly use the value you want to search on as the filter parameter, mongo assumes that you're searching on _id. Furthermore the {limit: 1} is redundant since you're searching on a scalar _id value and so you're guaranteed that the cursor length will be one.
I'm successfully able to GET data from
GET /mycollection?ql=select data.visitor.badges where data.visitor._id = 'f33498'
Which returns
{
"action": "get",
"application": "313hhlkhj77080",
"params": {
"ql": [
"select data.visitor.badges where data.visitor._id = 'f33498'"
]
},
"path": "/mycollection",
"uri": "http://xxxx/appservices/xxxxxx/mycollection",
"list": [
[
[
"New Visitor",
"Cart Abandoner"
]
],
[
[
"New Visitor",
"Repeat Visitors",
"Cart Abandoner"
]
],
[
[
"New Visitor",
"Repeat Visitors",
"Browse Abandoner"
]
]
],
"timestamp": 1407968065207,
"duration": 35,
"organization": "visitor-baas",
"applicationName": "sandbox",
"count": 3
}
However, I cannot figure out how to modify the following query to allow me to narrow the result set to only those containing a "Cart Abandoner" value in the data.user.badges array.
Is this possible? I've tried:
GET /mycollection?ql=select data.visitor.badges where data.visitor.badges = 'Cart Abandoner'
This appears to return data.visitor.badges arrays where 'Cart Abandoner' is the last position of the array.
GET /mycollection?ql=select data.visitor.badges where data.visitor.badges contains 'Cart Abandoner'
This appears to return nothing.
What am I missing?
Unfortunately there's currently no way to query arrays. Your best option is to store it as an object instead.
Couple things: I query elements of arrays all the time, but the ql query string is a little temperamental. element = 'string' should return the entire JSON payload if the 'string' is contained anywhere in the array 'element' so the fact you're getting mixed results may be due to the complexity of your nested arrays.
That said, The ql query string allows you to restrict the resources that get returned (like your first example where id = 'xxx'). There isn't any way to return anything other than the entire JSON payload from that resource (such as truncating your array based on the query restriction).
So, if what you're trying to do is pull just the times that your customer returned, I would suggest creating a separate resource called something like "visitorbadges" and connect it to the user record. So instead of querying with the id and trying t query the array you'd have something like:
https://api.usergird.org/{yourorg}/{yourapp}/users/{userid}/vistorbadges
If you use the BaaS userid rather than your own you can go to /users/uuid or, you could also store the userid with the label 'name' ({"name" : "f33498"}) which will let you go to /users/f33498/visitorbadges
See the Apige docs for how to connect resources:
http://apigee.com/docs/app-services/content/connecting-users-other-data