Retrieve Partial Array Properties - json

We are storing documents like this:
{
"FullName": "Jim",
"Children": [
{
"Name": "Sue",
"Hobbies": [
{
"Title": "Stamps",
"EnthusiasmLevel": 1
},
{
"Title": "Baseball",
"EnthusiasmLevel": 5
}
]
},
{
"Name": "Frank",
"Hobbies": [
{
"Title": "Dance",
"EnthusiasmLevel": 3
},
{
"Title": "Juggling",
"EnthusiasmLevel": 2
}
]
}
]
}
Usually when we are retrieving this "Jim" record, we'd want the full details on him and his children, but in certain circumstances we are going to want his name and just each child's name and the title of each of their hobbies.
Is there a straight-forward (or not) way of going about retrieving just parts of these documents while retaining (or rebuilding on the fly) their structure?
If I try something like:
SELECT p.FullName, [{"Name": child.Name}] AS Children
FROM People AS p
JOIN child in p.Children
I can construct an array, but I (obviously, per the join) get a record per child instead of one. If I instead remove the join and try to access these properties via the parent collection, I can't get at them.
What I want to get back is:
{
"FullName": "Jim",
"Children": [
{
"Name": "Sue",
"Hobbies": [
{"Title": "Stamps"},
{"Title": "Baseball"}
]
},
{
"Name": "Frank",
"Hobbies": [
{"Title": "Dance"},
{"Title": "Juggling"}
]
}
]
}
Even if I had to lose the structure, I'd still want to get back a single record representing "Jim" that contains his children's names and hobbies, but right now I'm just retrieving everything and doing the filtering on the client side, which is less than ideal.
Is what I'm after possible?

Based on your situation, I suggest you using Stored Procedure to process your data on the server side. I test sample code for you and it matches your requirements.
Sample Code:
function sample() {
var collection = getContext().getCollection();
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT p.FullName, p.Children FROM People AS p',
function (err, feed, options) {
if (err) throw err;
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var returnResult = [];
for(var i = 0;i<feed.length;i++){
var peopleObj = feed[i];
ChildrenArray = [];
for(var j = 0;j<peopleObj.Children.length;j++){
console.log(j)
var childObj = peopleObj.Children[j];
HobbiesArray = [];
for(var k = 0; k < childObj.Hobbies.length;k++){
var hobbyObj = childObj.Hobbies[k];
map ={};
map["Title"] = hobbyObj.Title;
HobbiesArray.push(map);
}
childObj.Hobbies = HobbiesArray;
}
ChildrenArray.push(childObj);
}
returnResult.push(peopleObj);
getContext().getResponse().setBody(returnResult);
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
Output :
[
{
"FullName": "Jim",
"Children": [
{
"Name": "Sue",
"Hobbies": [
{
"Title": "Stamps"
},
{
"Title": "Baseball"
}
]
},
{
"Name": "Frank",
"Hobbies": [
{
"Title": "Dance"
},
{
"Title": "Juggling"
}
]
}
]
}
]
Any concern , please feel free to let me know.

I would suggest a simpler query-only solution. A la:
select jim.FullName,
ARRAY(
select child.Name,
ARRAY(
select hobby.Title from hobby in child.Hobbies
) as "Hobbies"
from jim join child in jim.Children
) as "Children"
from jim
While SP does have its uses, in this case I would consider this solution preferred over the SP because:
it is a lot simpler
it is more maintainable as it would keep your query logic in single place, query could be ran on any DB, without requiring any special permissions or preparation on server
It is better to keep application logic out of data layer. I.e. having the logic in app makes it possible to concurrently use multiple versions of this query.
haven't tested, but I'm pretty sure it is cheaper by RU.

Related

Find specific value in a large json file

I've a simple json array similar to the below. I'd like to find the record that contain a matching value, say for example name == jack.
{
"data": [
{
"Record": {
"attributes": {
"name": "Jack",
"age": "38",
"description": "1234",
}
}
}
]
}
Below python code works but it is very slow. Is there any way to get the results quicker?
with open('records.json') as f:
input_dict = json.load(f)
input_var = input_dict["data"]
for i in input_var:
if i["Record"]["attributes"]["name"] == "Jack":
print(i["Record"]["attributes"])
break

Get to deeply nested object by array path of ids

I have this JSON data which has questions with options and questions nested in them. So the user picks an answer and the next question is chosen based on that answer and so on.... I have an array that is essentially a path (of ids) to the specific question object I'm looking for. How can I get to that object using my array of paths in my react component?
path = ["525289", "128886", "123456", "7547", "35735"]
{"question": {
"id":"525289",
"options": [
{
"id":"128886",
"optionName":{"alt":"123"},
"question": {
"id":"123456",
"title": "soidjfs",
"options": [
{
"id":"7547",
"optionName":{"alt":"new"},
"question": {
"id":"35735",
"title": "soidjfs",
"options": [
]
}
},
{
"id":"1234",
"optionName":{"alt":"new"},
"question": {
"id":"25825",
"title": "soidjfs",
"options": [
]
}
}
]
}
},
{
"id":"1234569999",
"optionName":{"alt":"123"},
"question": {
"id":"3457",
"title": "soidjfs",
"options": [
{
"id":"999998",
"optionName":{"alt":"new"},
"question": {
"id":"2134678",
"title": "soidjfs",
"options": [
]
}
},
{
"id":"55555",
"optionName":{"alt":"new"},
"question": {
"id":"123456159",
"title": "soidjfs",
"options": [
]
}
}
]
}
}
]
}
}
This could be done with a path similar (although not too similar) to what you provide, using the reduce function although there are some inconsistencies in your examples. For example, question seems to have a 1:1 relationship with option, so it doesn't seem like it needs filtering on traversal?
Alternatively as Mitya stated, you can use a JSON querying library. These are much more general purpose and so will be more verbose than a bespoke solution with contextual knowledge of your data structures.
I've used a much more simple, recursive parent-children model here:
let data = [
{
id: "123",
children: [
{
id: "456A",
title: "xyz",
children: [
{
id: "789AA",
message: "Foo"
},
{
id: "789AB",
message: "Bar"
}
]
},
{
id: "456B",
title: "abc",
children: [
{
id: "789BA",
message: "Hello"
},
{
id: "789BB",
message: "World!"
}
]
}
]
}
];
let path1 = ["123", "456A", "789AA"];
let path2 = ["123", "456C", "789CA"]; // Invalid as 456C isn't a valid item at this point in the tree
let path3 = ["123", "456B", "789BB"];
function select(path, data) {
return path.reduce((acc, v) => acc && acc.children.find(c => c.id === v), {children: data});
}
console.log(select(path1, data));
console.log(select(path2, data));
console.log(select(path3, data));
Output:
{ id: '789AA', message: 'Foo' }
undefined
{ id: '789BB', message: 'World!' }
If you're open to using a (small) library, you could use something like very own J-Path, which allows you to query and traverse an object (first argument) using XPath (second), which is normally used to query XML.
let item = jpath(object, 'question[id="525289"]/options/item[id="128886"]/question[id="123456"]/options/item[id="7547"]/question[id="35735"]');
Or, more simply, we could go straight to the target using the final ID:
let item = jpath(object, '//question[id="35735"]');
If you don't want a library, you'll need to step through the IDs manually and use a while loop until you find the data item you need.
I think your approach to this question is wrong. I think a better way would be to define each question separately as their own object and then have arrays of nested objects.
var questionId1 = {}
var questionId2 = {}
var questionId3 = {}
var questionId4 = {}
var questionId5 = {}
questionId1.options = [questionId2, questionId3]
questionId2.options = [questionId4]
questionId4.options = [questionId5]
Traversing things that way can make the idea of the path obsolete. You'd just need the name of the current question to display the options. When the next question is selected, the parent is no longer relevant, only its children matter.
Still, if you're stuck using this method for one reason or another, you can use the . notation to access nested elements inside of objects. In this case, it sounds like you're looking for each element's id and wanting the element's options object. Assuming you want the option that traverses the user's path and returns the options at the end of the path, you're going to need a lot of nested ifs to make sure that certain properties are there and that they're not empty and loops to go deeper into arrays.
Here's a fiddle that shows that method.

Insert into existing map a map structure in DynamoDB using Nodejs

Structure of an item in database is as shown below:
{
"cars": {
"x": [
{
"time": 1485700907669,
"value": 23
}
]
},
"date": 1483214400000,
"id":"1"
}
I have to add a new item "z" of type list to cars like
{
"cars": {
"x": [
{
"time": 1485700907669,
"value": 23
}
],
"z": [
{
"time": 1485700907669,
"value": 23
}
]
},
"date": 1483214400000,
"id": "1"
}
What would the update expression in Node.js look like if I want to achieve somethings like this?
So far this is what I came up with:
set #car.#model= list_append(if_not_exists(#car.#model, :empty_list), :value)
However, if the item does not exist at the time of creation it throws error. Any idea how to do this?
This is the updated parameter I am using, still doesn't work
var params = {
TableName:table,
Key:{
"id": id,
"date": time.getTime()
},
ReturnValues: 'ALL_NEW',
UpdateExpression: 'SET #car.#model = if_not_exists(#car.#model,
:empty_list)',
ExpressionAttributeNames: {
'#car': 'cars',
'#model':"z"
},
ExpressionAttributeValues: {
':empty_list': [],
}
};
The solution is to update operation in two steps, first create a empty map for the parent since it does not exist in the first place.
So, in my case
SET #car= :empty_map
where :empty_map = {}
after doing this run the other update expression
SET #car.#model = list_append(if_not_exists(#car.#model, :empty_list), :value)
where :empty_list=[] and :value= {
"time": 1485700907669,
"value": 23
}
Break your update expression apart into two separate expressions:
SET #car.#model = if_not_exists(#car.#model, :empty_list) SET #car.#model = list_append(#car.#model, :value)

couchbase sort issue in java code

Below is the document on which I am working:
{
"id": "idgwSRWDUJjQH",
"rev": "15-13d1d4545cd601560000000000000000",
"expiration": 0,
"flags": 0
}
{
"geminiType": "storegroup",
"_class": "web.model.StoreGroup",
"order": "10",
"childId": [
],
"name": "aaa",
"parent": "root",
"userGroupId": [
],
"type": "Folder",
"storeId": [
]
}
I am trying to sort based on name as below
function (doc, meta) {
if(doc.geminiType == "storegroup") {
emit(doc.name, [ meta.id,doc.name, doc.parent, doc.type]);
}
}
This I have created permanent view in couchbase console. I am fetching the document from my jave code using couchbase client as below:
View storeGrpView = cc.getView(RRConsts.STORE_GROUP_VIEW_DESIGN_DOC, RRConsts.STORE_GROUP_VIEW);
Query getAllstoreGrpQuery = new Query();
getAllstoreGrpQuery.setIncludeDocs(true);
getAllstoreGrpQuery.setStale(Stale.FALSE);
ViewResponse result = cc.query(storeGrpView, getAllstoreGrpQuery);
logger.info("getAllstoreGrpQuery resultset: " + result.toString());
for(ViewRow row : result) {
logger.info("store group :"+row.getDocument().toString());
}
}
Here I am getting the result in order of meta.id of the doc but i was expecting the result set to be in order of doc.name. Please let me know where I am doing wrong.
I got the solution to above issue. Actually if I used below two get method instead of row.getDocument() to fetch data, I am getting data in sorted order as expected.
row.getkey()
row.getvalue()

Builtin Query Capabilities in JSON using Javascript or JQuery

I am looking query the JSON data based on some where conditions for ex. list the person names whose cell no is 777-777-7777.
pl let me know what are query capabilities in JSON.
var json = [{
"name": "senthil",
"Phoneno": [{
"Home": "111-111-1111"
},
{
"Cell": "222-222-2222"
},
{
"Office": "333-333-3333"
}
],
"City": "Hartford"
},
{
"name": "kumar",
"Phoneno": [{
"Home": "444-555-6666"
},
{
"Cell": "777-777-7777"
},
{
"Office": "888-888-8888"
}
],
"City": "Austin"
},
];
var people = json.filter(function(el)
{
return el.Phoneno.some(function(number)
{
return number.Cell == "777-777-7777";
});
});
This uses the Array.filter and Array.some functions from ECMAScript 5. filter returns an array of the elements that pass a test. some returns true if any element in an array passes the test.
For browsers that don't support it, you can use the code at MDC.
This will almost certainly not be faster than the obvious for-loop approach.
As a note, if every person can have up to one phone number per type, a simpler representation would be:
"Phoneno": { "Home": "111-111-1111",
"Cell": "222-222-2222",
"Office": "333-333-3333"
}
Also, JSON technically refers to the text representation, not actual JavaScript objects.