Algorithm for creating nested structure - algorithm

I have following structure:
{
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
I am looking for an algorithm on how to create from that depth a parent-child-like, nested structure.
{
"list": [{
"depth": 0,
"data": "lorem1",
"children": [{
"depth": 1,
"data": "lorem2",
"children": [{
"depth": 2,
"data": "lorem3",
"children": [],
}, {
"depth": 2,
"data": "lorem4",
"children": [],
}]
}]
}, {
"depth": 0,
"data": "lorem5",
"children": [{
"depth": 1,
"data": "lorem6",
"children": [],
}, {
"depth": 1,
"data": "lorem7",
"children": [{
"depth": 2,
"data": "lorem8",
"children": [],
}]
}]
}
]}
The logic is like this:
Assumption: The first item in the list always starts with depth=0
If depth is larger than the last, it must be child of this last one
I can not get this to work. It should be recursive to have infinite nesting/depth levels.
Thank you guys for the help!

You can use a stack to keep track of the current path in the tree. When depth increases from one to the next, then push the new node also on that stack. If not, pop items from the stack until the right depth is reached.
Then you always know in which children collection you need to add the new node.
Here is an runnable implementation in JavaScript:
function algo(list) {
// Create a dummy node to always stay at the bottom of the stack:
let stack = [
{ "depth": -1, "data": "(root)", "children": [] }
];
for (let node of list) {
let newNode = { ...node, children: [] }; // Copy and add children property
if (newNode.depth >= stack.length || newNode.depth < 0) throw "Invalid depth";
while (newNode.depth < stack.length - 1) stack.pop();
stack[stack.length - 1].children.push(newNode);
stack.push(newNode);
}
return stack[0].children;
}
// Demo
let data = {
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
// Create a new structure, and load the transformed list in its list property:
let result = {
"list": algo(data.list)
};
// Show result
console.log(result);
To answer to your request to do this without dummy node:
function algo(list) {
let result = [];
let stack = [];
for (let node of list) {
let newNode = { ...node, children: [] }; // Copy and add children property
if (newNode.depth > stack.length || newNode.depth < 0) throw "Invalid depth";
while (newNode.depth < stack.length) stack.pop();
if (!stack.length) result.push(newNode);
else stack[stack.length - 1].children.push(newNode);
stack.push(newNode);
}
return result;
}
// Demo
let data = {
"list": [
{ "depth": 0, "data": "lorem1" },
{ "depth": 1, "data": "lorem2" },
{ "depth": 2, "data": "lorem3" },
{ "depth": 2, "data": "lorem4" },
{ "depth": 0, "data": "lorem5" },
{ "depth": 1, "data": "lorem6" },
{ "depth": 1, "data": "lorem7" },
{ "depth": 2, "data": "lorem8" }
]
}
// Create a new structure, and load the transformed list in its list property:
let result = {
"list": algo(data.list)
};
// Show result
console.log(result);

Related

Filter documents out of the facet count in enterprise search

We use enterprise search indexes to store items that can be tagged by multiple tenants.
e.g
[
{
"id": 1,
"name": "document 1",
"tags": [
{ "company_id": 1, "tag_id": 1, "tag_name": "bla" },
{ "company_id": 2, "tag_id": 1, "tag_name": "bla" }
]
}
]
I'm looking to find a way to retrieve all documents with only the tags of company 1
This request:
{
"query": "",
"facets": {
"tags": {
"type": "value"
}
},
"sort": {
"created": "desc"
},
"page": {
"size": 20,
"current": 1
}
}
Is coming back with
...
"facets": {
"tags": [
{
"type": "value",
"data": [
{
"value": "{\"company_id\":1,\"tag_id\":1,\"tag_name\":\"bla\"}",
"count": 1
},
{
"value": "{\"company_id\":2,\"tag_id\":1,\"tag_name\":\"bla\"}",
"count": 1
}
]
}
],
}
...
Can I modify the request in a way such that I get no tags by "company_id" = 2 ?
I have a solution that involves modifying the results to strip the extra data after they are retrieved but I'm looking for a better solution.

can I filter an array in elastic?

I had to insert a huge amount of data into elastic and I have done it in the following manner.
I need to query this object but I am unable to filter the "logData" array. Can someone help me out here ? is it even possible to filter an array in elastic?
"_source":{
"FileName": "fileName.log"
"logData": [
{
"LineNumber": 1,
"Data": "data1"
},
{
"LineNumber": 2,
"Data": "Data2"
},
{
"LineNumber": 3,
"Data": "Data3"
},
{
"LineNumber": 4,
"Data": "Data4"
},
{
"LineNumber": 5,
"Data": "Data5"
},
{
"LineNumber": 6,
"Data": "Data6"
}
]}
Is there a way to query such that I get only few items from this array ?
like:
"_source":{
"FileName": "fileName.log"
"logData": [
{
"LineNumber": 1,
"Data": "data1"
},
{
"LineNumber": 2,
"Data": "Data2"
},
{
"LineNumber": 3,
"Data": "Data3"
}
]
}
There's no dedicated array mapping type in ES.
With that being said, when you have an array of objects with shared keys, it's recommended that you use the nested field type to preserve the connections of the individual sub-objects' attributes. If you don't use nested, the objects will be flattened which may lead to seemingly wrong query results.
As to the actual query -- assuming your mapping looks something like this:
PUT logs_index
{
"mappings": {
"properties": {
"logData": {
"type": "nested"
}
}
}
}
you'll need to filter those logData sub-documents of interest, perhaps with a terms_query. Then and only then can you extract only those array objects that've matched this query (lineNumber: 1 or 2 or 3).
The technique for that is called inner_hits:
POST logs/_search
{
"_source": ["FileName", "inner_hits.logData"],
"query": {
"nested": {
"path": "logData",
"query": {
"terms": {
"logData.LineNumber": [
1,
2,
3
]
}
},
"inner_hits": {}
}
}
}
Check this thread for more info.

How to retrieve Json data type data in SQL using a where condition in LARAVEL

Question : How to retrieve Json data type data in SQL using a where condition in LARAVEL?
I want to display all the order that contains order->Product->user->id === 1
{
"currentUserID": 1,
"currentUserName": "Mohamed Naalir",
"order": [
{
"id": 26,
"Product": [
{
"id": 4,
"name": "Araliya Rice",
"desription": "Araliya Rice",
"salePrice": 500,
"category": "Rice",
"user": {
"id": 1,
"name": "Mohamed Naalir",
}
}
],
},
{
"id": 27,
"Product": [
{
"id": 2,
"name": "white sugar",
"desription": "aaa",
"salePrice": 100,
"category": "Sugar",
"user": {
"id": 5,
"name": "Mohamed Sharaf",
}
}
],
}
]
}
json where clauses
$orders = DB::table('orders')
->whereJsonContains('Product', [['user' => ['id' => 1]]])
->get();

Can an aggregation have two keys in elasticsearch

Suppose I have an index with a nested document that looks like this:
{
"mappings": {
"assignment": {
"properties":{
"id": {
"type": "string"
},
"location": {
"type": "string"
},
"typeOfLoss":{
"type": "string"
},
"lineItems": {
"type": "nested",
"properties": {
"categoryCode":{
"type": "string"
},
"selectorCode":{
"type": "string"
},
"roomType": {
"type": "string"
}
}
}
I now want to get a count aggregation of "lineItems" documents that returns the selectorCode and categoryCode where the roomType matches a search query. I am new to elasticsearch and can write my query in SQL
SELECT COUNT(*) as theCount, ln.category_code, ln.selector_code
FROM line_items as ln, assignment
WHERE assignment.location = "84043"
AND assignment.typeOfLoss = "Fire"
AND ln.roomType = "kitchen"
GROUP BY ln.category_code, ln.selector_code
ORDER BY theCount DESC;
I have started on the NEST query but am having some problems and am hoping someone can point me in the right directions.
var typeOfLossQuery = new TermQuery
{
Field = "typeOfLoss",
Value = typeOfLoss
};
var locationQuery = new TermQuery
{
Field = "location",
Value = location
};
var roomTypeQuery = new TermQuery
{
Field = "roomType",
Value = roomType
};
var result = client.Search<LineItem>(s => s
.From(0)
.Size(numberOfItems)
.Query(q => q.HasParent<Assignment>(a => a
.Query(x =>x
.MatchAll() && typeOfLossQuery && locationQuery
)
) && q.MatchAll() && roomTypeQuery
));
You can indeed do this with ElasticSearch but it's not quite as clean as in SQL. We can accomplish this with Nested Aggregations.
Setup
I'm going to setup the data so that you'd get the following equivalent result in SQL:
categoryCode | selectorCode | Count
c1 | s1 | 1
c1 | s2 | 2
PUT test1
PUT test1/_mapping/type1
{
"properties": {
"id": {
"type": "string"
},
"location": {
"type": "string"
},
"typeOfLoss": {
"type": "string"
},
"lineItems": {
"type": "nested",
"properties": {
"categoryCode": {
"type": "string",
"fielddata": true
},
"selectorCode": {
"type": "string",
"fielddata": true
},
"roomType": {
"type": "string"
}
}
}
}
}
POST test1/type1
{
"location":"l1",
"lineItems":
{
"categoryCode": "c1",
"selectorCode": "s1",
"roomType": "r1"
}
}
POST test1/type1
{
"location":"l1",
"lineItems":
{
"categoryCode": "c1",
"selectorCode": "s2",
"roomType": "r1"
}
}
POST test1/type1
{
"location":"l1",
"lineItems":
{
"categoryCode": "c1",
"selectorCode": "s2",
"roomType": "r1"
}
}
Query
GET test1/type1/_search
{
"size": 0,
"query": {
"nested": {
"path": "lineItems",
"query": {
"term": {
"lineItems.roomType": {
"value": "r1"
}
}
}
}
},
"aggs": {
"nestedAgg": {
"nested": {
"path": "lineItems"
},
"aggs": {
"byCategory": {
"terms": {
"field": "lineItems.categoryCode",
"size": 10
},
"aggs": {
"bySelector": {
"terms": {
"field": "lineItems.selectorCode",
"size": 10
}
}
}
}
}
}
}
}
My query is saying the following:
only show me data where roomType = 'r1'
aggregate (group in SQL) by categoryCode
created a "nested" or "sub" aggregation on "selectorCode"
Result
{
"took": 6,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0,
"hits": []
},
"aggregations": {
"nestedAgg": {
"doc_count": 3,
"byCategory": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "c1",
"doc_count": 3,
"bySelector": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "s2",
"doc_count": 2
},
{
"key": "s1",
"doc_count": 1
}
]
}
}
]
}
}
}
}
So the results returns a list of aggregations. Inside an aggregation is a "bucket". Notice that the outer bucket for byCategory shows a doc_count of 3. That's because there are 3 records that match in the DB.
Then, nested inside that is the bySelector bucket showing s2 and s1 with a doc_count of 2 and 1 respectively.
Hopefully that helps, I'll let you turn all this into a NEST query.

jqgrid tree reader not working

My sample Json object is shown below:
{
"o": [
{
"level": 0,
"outlineItemId": 8,
"parentItemId": null,
"parentItem": null,
"order": 0,
"text": "section 1",
"isLeaf": "false",
"expanded": "true"
},
{
"level": 1,
"outlineItemId": 9,
"parentItemId": 8,
"parentItem": {
"level": 0,
"outlineItemId": 8,
"parentItemId": null,
"parentItem": null,
"order": 0,
"text": "section 1",
"isLeaf": "false",
"expanded": "true"
},
"order": 0,
"text": "sub 1",
"isLeaf": "false",
"expanded": "true"
},
{
"level": 2,
"outlineItemId": 10,
"parentItemId": 9,
"parentItem": {
"level": 1,
"outlineItemId": 9,
"parentItemId": 8,
"parentItem": {
"level": 0,
"outlineItemId": 8,
"parentItemId": null,
"parentItem": null,
"order": 0,
"text": "section 1",
"isLeaf": "false",
"expanded": "true"
},
"order": 0,
"text": "sub 1",
"isLeaf": "false",
"negateDevice": null,
"expanded": "true"
},
"order": 0,
"text": "sub sub 1",
"isLeaf": "true",
"expanded": "true"
}
]
}
Earlier when the tree was configured as:
treeReader: {
level_field: "level",
parent_id_field: "parentItemId",
leaf_field: "isLeaf",
expanded_field: "expanded"
},
I was displaying the correct indentation and image icons, however they were not expanded when the json obj always had "expanded":"true" so i tried the below code.
treeReader: {
level_field: "o.level",
parent_id_field: "o.parentItemId",
leaf_field: "o.isLeaf",
expanded_field: "o.expanded"
},
Now I am not getting the Image icons and the tree which was expanded earlier is now flat.
My Json reader just in case i goofed up..
jsonReader: {
root: 'o',
id: 'o.outlineItemId',
parentItemId: 'o.parentItem.outlineItemId',
text: 'o.text',
repeatitems: false,
page: function(obj) { return 1; },
total: function(obj) { return 1; },
records: function(obj) { return obj.o.length; },
},
Any help will be appreciated.
Shah
Got it!
For the reader i had to include cell:'' and remove the o. references. and also loaded:true in the json object.

Resources