I am going with an online course, there's a part where the instructor says he is trying to find/ validate a user using User.findOne and passes some conditions to find the requested user. To pass the value, he uses object destructuring. Heres the specific code:
const token = req.header("Authorization").replace("Bearer ", "");
const decoded = jwt.verify(token, "secret key here!");
// issue is here, look at the second property of the findOne function's argument.
const user=await User.findOne({_id: decoded._id, 'tokens.token': token})
The instructor is using a string key in 'tokens.token'. He's saying that, mongodb will loop over the all the tokens available in specified user object to check if the given token matches.
And if you are wondering, here's an example of a single user which contains auth tokens:
{
"name": "Prottay",
"_id": "5e27f23b6b549b4c28b8ac35",
"password": "$2a$08$gUfMwk6TNWViHihrcxjKg.8EXD04lLkGIWXqzrf8wYokdLQXHxpdy",
"tokens": [
{
"_id": "5e27f23b6b549b4c28b8ac36",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZTI3ZjIzYjZiNTQ5YjRjMjhiOGFjMzUiLCJpYXQiOjE1Nzk2NzYyMTl9.-PWXzlEoPlEZn9F_awtzqrXOtByxUCW9RCdchHF1yKE"
},
{
"_id": "5e280429596e742dcc2f9e30",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZTI3ZjIzYjZiNTQ5YjRjMjhiOGFjMzUiLCJpYXQiOjE1Nzk2ODA4MDl9.7W-QZ55Cc3NFd_-NPyJ0VW_5F1UVrDWAV4xHX63D6tc"
},
{
"_id": "5e280435596e742dcc2f9e31",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZTI3ZjIzYjZiNTQ5YjRjMjhiOGFjMzUiLCJpYXQiOjE1Nzk2ODA4MjF9.vppisFiNNC_DYHtGK0IURzEOCCC5zcWl1v9yD6l1D4I"
}
],
"__v": 3
},
To me it looks like by using 'tokens.token': token instructor is trying to loop over on the user's tokens array to match the correct token.
Am I right? If I am how can he be using loop in object destructuring?
The instructor is not looping over an array using object destructuring but using the mongodb syntax for searching for a document within an array where you do not know the index before hand.
If you do not know the index position of the document nested in the array, concatenate the name of the array field, with a dot (.) and the name of the field in the nested document.
https://docs.mongodb.com/manual/tutorial/query-array-of-documents/#specify-a-query-condition-on-a-field-embedded-in-an-array-of-documents
Related
I'm building an app where I need to delete items stored in the database. Here's a (shortened) example of user data I have in my DynamoDB table called 'registeredUsers':
{
"userId": "f3a0f858-57b4-4420-81fa-1f0acdec979d"
"aboutMe": "My name is Mary, and I just love jigsaw puzzles! My favourite jigsaw category is Architecture, but I also like ones with plants in them.",
"age": 27,
"email": "mary_smith#gmail.com",
"favourites": {
"imageLibrary": [
{
"id": "71ff8060-fcf2-4523-98e5-f48127d7d88b",
"name": "bird.jpg",
"rating": 5,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/bird.jpg"
},
{
"id": "fea4fd2a-851b-411f-8dc2-1ae0e144188a",
"name": "porsche.jpg",
"rating": 3,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/porsche.jpg"
},
{
"id": "328b913f-b364-47df-929d-925676156e97",
"name": "rose.jpg",
"rating": 0,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/rose.jpg"
}
]
}
}
I want to be able to delete the item 'rose.jpg' in the user.favourites.imageLibrary array. In order to select the correct user, I can provide the userId as the primary key. Then, in order to select the correct image in the array, I can pass the AWS.DocumentClient the 'id' of the item in order to delete it. However, I'm having trouble understanding the AWS API Reference docs. The examples given in the developer guide do not describe how to delete an item by looking at one of it's attributes. I know I have to provide an UpdateExpression and an ExpressionAttributeValues object. When I wanted to change a user setting, I found it pretty easy to do:
const params = {
TableName: REGISTERED_USERS_TABLE,
Key: { userId },
UpdateExpression: "set userPreferences.difficulty.showGridOverlay = :d",
ExpressionAttributeValues: {
":d": !showGridOverlay
},
ReturnValues: "UPDATED_NEW"
};
To conclude, I need a suitable Key, UpdateExpression and ExpressionAttributeValues object to access the rose.jpg item in the favourites array.
Unfortunately, the UpdateExpression syntax is not as powerful as you would have liked. It supports entire nested documents inside the item, but not sophisticated expressions to search in them or to modify them. The only ability it gives you inside a list is to access or modify its Nth element. For example:
REMOVE #favorites.#imagelibrary[3]
Will remove the 3rd element of imagelibrary (note that the "#imagelibrary" will need to be defined in ExpressionAttributeNames), and you can also have a condition on #favorites.#imagelibrary[3].#id, for example, in ConditionExpression. But unfortunately, there is no way to specify more complex combinations of conditions and updates, such as "find me the i where #favorites.#imagelibrary[i].#id is equal something, and then REMOVE this specific element".
Your remaining option is to read the full value of the item (or with ProjectionExpression just the #favorties.#imagelibrary array), and then in your own code find which of the elements you want to remove (e.g., discover that it is the 3rd element), and then in a separate update, remove the 3rd element.
Note that if there's a possibility that some other parallel operation also changes the item, you must use a conditional update (both UpdateExpression and ConditionExpression) for the element removal, to ensure the element that you are removing still has the id you expected. If the condition fails, you need to repeat the whole operation again - read the modified item again, find the element again, and try to remove it again. This is an example of the so-called "optimistic locking" technique which is often used with DynamoDB.
i have following issue, i have part of json returned by api:
...
"name": "NEWS",
"products": {
"10432471": {
"id": 10432471,
"productConfig": [
...
]
},
"10432481": {
"id": 10432481
},
"17300241": {
"id": 17300241
},
"17300251": {
"id": 17300251,
"productConfig": [
...
]
},
"18420501": {
"id": 18420501
}
}
...
and i want to get random simple product id (simple product means that it dont have productConfig field inside)
for (i=0;i<res.body.menu.categories.length;i++){
if(res.body.menu.categories[i].name=='NEWS'){
availableProducts = Object.keys(res.body.menu.categories[i].products);
console.log ('avbl prods from news category: ',availableProducts);
for (j=0;;j++){
random=Math.floor(Math.random()*Object.keys(res.body.menu.categories[i].products).length);
if(assert.notInclude(res.body.menu.categories[i].products[random],'productConfig')){
availableProductIdForDelivery = res.body.menu.categories[i].products[random].id;
break;
}
}
}
}
This code gives me error in line with second if (if(assert.notInclude....)) Uncaught AssertionError: object tested must be an array, a map, an object, a set, a string, or a weakset, but undefined given.
So, inside "products" we dont have array, becouse there're {}, not []?
There's any other way to iterate beetween products (their numerical names are changed everyday) and save id to availableProductIdForDelivery
Take a look at the documentation: http://www.chaijs.com/api/assert/#method_notinclude
You see that the first parameter must be Array, String or haystack. Let's take a look at the error message you have given:
Uncaught AssertionError: object tested must be an array, a map, an
object, a set, a string, or a weakset, but undefined given.
This error message indicates that something was undefined instead of the possible expected types listed in the message. I guess it was the first parameter, but it might be the third as well, since you did not pass anything as the third parameter, so it is undefined.
So, inside "products" we dont have array, becouse there're {}, not []?
Correct. As far as I can tell, you are trying to apply an assertion to res.body.menu.categories[i].products[random], but because products is an object (and not a list) attempting to retrieve an index (as oppose to one of the keys defined in the object) with products[random] returns undefined.
If you want to create a list of products in order to evaluate their contents, you may be able to use Object.values(res.body.menu.categories[i].products)[random]. As far as I am aware, this feature is only standard as of ES2017. You may also be able to apply a map function to your availableProducts object to retrieve the corresponding values for each product key.
Ok, thanks for help, now its works as i want. Below my solution, maybe it help someone in the future:
for (i=0;i<res.body.menu.categories.length;i++){
if(res.body.menu.categories[i].name=='NEWS'){ //random simple product from 'news' category
availableProducts = Object.keys(res.body.menu.categories[i].products);
console.log ('avbl prods from news category: ',availableProducts);
while(availableProductIdForDelivery==''){
random=Math.floor(Math.random()*Object.keys(res.body.menu.categories[i].products).length);
if((Object.values(res.body.menu.categories[i].products)[random].productConfig).length==0)
availableProductIdForDelivery = Object.values(res.body.menu.categories[i].products)[random].id;
}
console.log('random simple product: '+availableProductIdForDelivery) ;
}
}
I have a program where I'm requesting weather data from a server, processing the data, and then saving it to an mlab account using mongoose. I'm gathering 10 years of data, but the API that I'm requesting the data from only allows about a year at a time to be requested.
I'm using findOndAndUpdate to create/update the document for each weather station, but am having trouble updating the arrays within the data object. (Probably not the best way to describe it...)
For example, here's the model:
const stnDataSchema = new Schema(
{
station: { type: String, default: null },
elevation: { type: String, default: null },
timeZone: { type: String, default: null },
dates: {},
data: {}
},
{ collection: 'stndata' },
{ runSettersOnQuery: true }
)
where the dates object looks like this:
dates: ["2007-01-01",
"2007-01-02",
"2007-01-03",
"2007-01-04",
"2007-01-05",
"2007-01-06",
"2007-01-07",
"2007-01-08",
"2007-01-09"]
and the data object like this:
"data": [
{
"maxT": [
0,
null,
4.4,
0,
-2.7,
etc.....
what I want to have happen is when I run findOneAndUpdate I want to find the document based on the station, and then append new maxT values and dates to the respective arrays. I have it working for the date array, but am running into trouble with the data array as the elements I'm updated are nested.
I tried this:
const update = {
$set: {'station': station, 'elevation': elevation, 'timeZone': timeZone},
$push: {'dates': datesTest, 'data.0.maxT': testMaxT}};
StnData.findOneAndUpdate( query, update, {upsert: true} ,
function(err, doc) {
if (err) {
console.log("error in updateStation", err)
throw new Error('error in updateStation')
}
else {
console.log('saved')
but got an output into mlab like this:
"data": {
"0": {
"maxT": [
"a",
"b",
the issue is that I get a "0" instead of an array of one element. I tried 'data[0].maxT' but nothing happens when I do that.
The issue is that the first time I run the data for a station, I want to create a new document with data object of the format in my third code block, and then on subsequent runs, once that document already exists, update the maxT array with new values. Any ideas?
You are getting this output:
"data": {
"0": {
"maxT": [
"a",
"b",
because you are upserting the document. Upserting gets a bit complicated when dealing with arrays of documents.
When updating an array, MongoDB knows that data.0 refers to the first element in the array. However, when inserting, MongoDB can't tell if it's meant to be an array or an object. So it assumes it's an object. So rather than inserting ["val"], it inserts {"0": "val"}.
Simplest Solution
Don't use an upsert. Insert a document for each new weather station then use findOndAndUpdate to push values into the arrays in the documents. As long as you insert the arrays correctly the first time, you will be able to push to them without them turning into objects.
Alternative Simple Solution if data just Contains one Object
From your question, it looks like you only have one object in data. If that is the case, you could just make the maxT array top-level, instead of being a property of a single document in an array. Then it would act just like dates.
More Complicated MongoDB 3.6 Solution
If you truly cannot do without upserts, MongoDB 3.6 introduced the filtered positional operator $[<identifier>]. You can use this operator to update specific elements in an array which match a query. Unlike the simple positional operator $, the new $[<identifier>] operator can be used to upsert as long as an exact match is used.
You can read more about this operator here: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
So your data objects will need to have a field which can be matched exactly on (say name). An example query would look something like this:
let query = {
_id: 'idOfDocument',
data: [{name: 'subobjectName'}] // Need this for an exact match
}
let update = {$push: {'data.$[el].maxT': testMaxT}}
let options = {upsert: true, arrayFilters: [{'el.name': 'subobjectName'}]}
StnData.findOneAndUpdate(query, update, options, callbackFn)
As you can see this adds much more complexity. It would be much easier to forget about trying to do upserts. Just do one insert then update.
Moreover mLab currently does not support MongoDB 3.6. So this method won't be viable when using mLab until 3.6 is supported.
My DB instance contains some documents with the following schema:
{
id: <someGuid>
myJsonArray: [
{
subId: SmallUniqueId1,
val: <someValue>
},
{
subId: SmallUniqueId2,
val: <someOtherValue>
}
]
}
I have a Web API that generates new SubIds (you can think of them as randomly generated base64 strings that have a max length of 5 chars).
I'd like to query my DB to verify that the newly minted SubId doesn't already exist somewhere in all of my docs.
I figured the solution probably involves plugging in the new subId value into a WHERE clause (I'm hitting CosmosDB via the REST API). Here's what I don't know:
I don't know how to query over every element of each document's JsonArray (if it was just querying on a single string field, it would be easy)
I'm also not sure about the efficiency of this query and whether this approach for validating new Ids against what's already in the DB is, in general, a bad pattern. I have a one partition CosmosDB instance - the subId validation check should not occur very often.
I don't know how to query over every element of each document's
JsonArray (if it was just querying on a single string field, it would
be easy)
I created two sample documents in my azure cosmosdb as you described.
[
{
"id": "id1",
"myJsonArray": [
{
"subId": "sub1",
"val": "value1"
},
{
"subId": "sub2",
"val": "value2"
}
],
},
{
"id": "id2",
"myJsonArray": [
{
"subId": "sub3",
"val": "value3"
},
{
"subId": "sub4",
"val": "value4"
}
],
}
]
You could use the SQL below to query field in array.
SELECT a.subId as subId FROM c
join a in c.myJsonArray
Result:
Add where clause .
SELECT a.subId as subId FROM c
join a in c.myJsonArray
where a.subId='sub1'
Result:
I'm also not sure about the efficiency of this query and whether this
approach for validating new Ids against what's already in the DB is,
in general, a bad pattern. I have a one partition CosmosDB instance -
the subId validation check should not occur very often.
As you needs,I thought you could use User Defined Function in Azure Cosmos DB.
Invoke UDF when you create document into your DB, check if the field value already exists, return the boolean result.
Please refer to this official doc.
Hope it helps you. Any concern,please feel free to let me know.
How to get access to arrays data by requesting HTTP GET call with headers in
nodeJS. I am getting undefined error when I am trying to call the 3rd party
services. I make HTTP GET call with two headers.
This is response from HTTP GET call.
Here How I can access to "description".
{
"dotdList": [
{
"title": "From ₹ 10,299",
"description": "Haier 181 Single Door",
"url": "https://dl.flipkart.com/dl/home-kitchen/home-appliances/refrigerators/pr?sid=j9e,abm,hzg&offer=nb:mp:07de369c12&affid=fstlistgm",
"imageUrls": [
{
"url": "https://rukminim1.flixcart.com/image/800/800/j2z1fgw0/refrigerator-new/m/j/c/hrd-1813bms-r-3-haier-original-imaeu7gzwfskmxfx.jpeg?q=90",
"resolutionType": "default"
},
{
"url": "https://rukminim1.flixcart.com/image/200/200/j2z1fgw0/refrigerator-new/m/j/c/hrd-1813bms-r-3-haier-original-imaeu7gzwfskmxfx.jpeg?q=90",
"resolutionType": "low"
},
{
"url": "https://rukminim1.flixcart.com/image/400/400/j2z1fgw0/refrigerator-new/m/j/c/hrd-1813bms-r-3-haier-original-imaeu7gzwfskmxfx.jpeg?q=90",
"resolutionType": "mid"
},
{
"url": "https://rukminim1.flixcart.com/image/800/800/j2z1fgw0/refrigerator-new/m/j/c/hrd-1813bms-r-3-haier-original-imaeu7gzwfskmxfx.jpeg?q=90",
"resolutionType": "high"
}
],
"availability": "LIVE"
}
]
}
How to access to description.
Here is my actual code
var request = require('request');
request({
url: 'https://affiliate-api.flipkart.net/affiliate/offers/v1/dotd/json',
method: 'GET',
headers: {
'Fk-Affiliate-Id':'XXXXX',
'Fk-Affiliate-Id': 'XXXXXXXXXXX'
}
}, function(error, response, body) {
if(!error && response.statusCode == 200) {
let dataFs = JSON.parse(body);
let titleN = `${dataFs["dotdList"][0]["title"]}`;
console.log("error in fun");
sendTextMessage(sender, titleN);
}else {
console.error(response.error);
console.log("else error");
}
});
Part 2.This is another Response JSON
How Can I access objects from array. here I am making HTTP GET call to another url to get JSON data. below is the reponse data.
Where I am not able to access to array of data. I need to access offer_name
[
{
"featured": "0",
"exclusive": "0",
"promo_id": "P74192",
"offer_id": "180",
"offer_name": "Dominos.co.in CPS - India",
"coupon_title": "Garlic Bread & Dip free",
"category": "Food & Beverage",
"coupon_description": "TnC: Valid on Saturday",
"coupon_type": "Coupon",
"coupon_code": "NET09",
"ref_id": "src=vcommission&utm_source=Vcommission&utm_medium=Affiliate&utm_term=paid&utm_campaign=OLOHome",
"link": "http://tracking.vcommission.com/aff_c?offer_id=180&aff_id=36792",
"coupon_expiry": "2017-08-12",
"added": "2017-08-01",
"preview_url": "https://pizzaonline.dominos.co.in/",
"store_link": "http://tracking.vcommission.com/aff_c?offer_id=180&aff_id=36792",
"store_image": "http://media.vcommission.com/brand/files/vcm/180/dominos.jpg"
}
]
Answering your questions
Part 1
You already access the data's title via dataFs["dotdList"][0]["title"], and you can access the description the same way: dataFs["dotdList"][0]["description"].
Part 2
This one is quite similar to your old one. You should be able to access the fields you want via responseData[0]["offer_name"].
Explanation of Response objects, arrays, etc.
In your responses you get an array object, which you can tell by having values surrounded by brackets ([ /* data */ ]). To access values within an array, you use the access operator, like so: arrayValue[0], where 0 is the key or index of the object you are trying to fetch.
In both of your questions you are trying to access the first (and only) object within the array, and thus you can short cut that by using the 0 index of the array, i.e. responseData[0].
Within those arrays, though, you have objects. JSON Objects specifically (as is given to you by the HTTP GET request) can have fields with values which can be a string ("string"), number (0), or a boolean values (true or false). You can access the fields of an object by either accessing them using the dot operator (object.field) or by using the access operator, but with specific key (object["field"]). Using the access operator is especially useful if your field's key uses a space or number.
Putting those things together we can figure out your questions quite easily:
dataFS is an object, and we are wanting to access the elements underneath the objects in the array called "dotdList". Thus, we can access the list via the access operator (dataFs["dotdList"]), then access the first (and only) object via using the 0 index (dataFs["dotdList"][0]), and lastly accessing the fields we want by either using the dot or the access operator (dataFs["dotdList"][0]["description"] or `dataFs["dotdList"][0].description). If you need to do this multiple times, it is recommended to store the object in a variable, i.e:
var dotd = dataFs["dotdList"][0];
console.log(dotd.description) // got description!
The second part is also quite easy to figure out; you get back an array, you want the first object, and then you want to access the field: thus, access the first object using the 0 index (responseData[0]) and then access the fields you want (responseData[0].offer_name).
Always check to make sure what you are trying to access is there (i.e.the array itself in #1 and the objects within the array in both). If it isn't, your code will start throwing errors!