Given this object, i wan't to merge all possible start and endpoints with a distance of x, thus creating three paths given below objects. The function only seems to see half of the connections.
The model below would output three elements in maker js using chaining, i wan't to recreate that.
How could i improve this function?
let mergedPaths = [
{
"id": 2,
"path": "M 19.8279000000 0.0000000000L -19.8300000000 0.0000000000",
"startPoint": {
"x": "19.8279000000",
"y": "0.0000000000",
},
"endPoint": {
"x": "-19.8300000000",
"y": "0.0000000000"
}
},
{
"id": 3,
"path": "M 24.8250000000 -5.1691000000L 24.8230000000 -5.1691000000",
"startPoint": {
"x": "24.8250000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
}
},
{
"id": 4,
"path": "M -17.9684000000 -65.0000000000L 17.9664000000 -65.0000000000",
"startPoint": {
"x": "-17.9684000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "17.9664000000",
"y": "-65.0000000000",
}
},
{
"id": 5,
"path": "M -24.8271000000 -5.1691000000L -22.9656000000 -60.1691000000",
"startPoint": {
"x": "-24.8271000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "-22.9656000000",
"y": "-60.1691000000",
}
},
{
"id": 6,
"path": "M 22.9615000000 -60.1691000000L 24.8230000000 -5.1691000000",
"startPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
}
},
{
"id": 7,
"path": "M 22.9615000000 -60.1691000000L 22.9635000000 -60.1691000000L 22.9635000000 -60.1691000000L 24.8250000000 -5.1691000000L 24.8250000000 -5.1691000000A 5.0000000000 5.0000000000 0 0 1 19.8279000000 0.0000000000",
"startPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "19.8279000000",
"y": "0.0000000000",
}
},
{
"id": 8,
"path": "M 24.8230000000 -5.1691000000A 5.0000000000 5.0000000000 0 0 1 19.8259000000 0.0000000000",
"startPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "19.8259000000",
"y": "0.0000000000",
}
},
{
"id": 9,
"path": "M 17.9643000000 -65.0000000000A 5.0000000000 5.0000000000 0 0 1 22.9615000000 -60.1691000000",
"startPoint": {
"x": "17.9643000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
}
},
{
"id": 10,
"path": "M -22.9656000000 -60.1691000000A 5.0000000000 5.0000000000 0 0 1 -17.9684000000 -65.0000000000",
"startPoint": {
"x": "-22.9656000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "-17.9684000000",
"y": "-65.0000000000",
}
},
{
"id": 11,
"path": "M -19.8300000000 0.0000000000A 5.0000000000 5.0000000000 0 0 1 -24.8271000000 -5.1691000000",
"startPoint": {
"x": "-19.8300000000",
"y": "0.0000000000",
},
"endPoint": {
"x": "-24.8271000000",
"y": "-5.1691000000",
}
},
{
"id": 12,
"path": "M 17.9664000000 -65.0000000000A 5.0000000000 5.0000000000 0 0 1 22.9635000000 -60.1691000000",
"startPoint": {
"x": "17.9664000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "22.9635000000",
"y": "-60.1691000000",
}
},
{
"id": 13,
"path": "M 0.0000000000 -48.7500000000A 1.2500000000 1.2500000000 0 0 1 0.0000000000 -51.2500000000",
"startPoint": {
"x": "0.0000000000",
"y": "-48.7500000000",
},
"endPoint": {
"x": "0.0000000000",
"y": "-51.2500000000",
}
}
]
let pointMatchingDistance = 1;
getDistance = (x1: number, x2: number, y1: number, y2: number) => {
let y = x2 - x1;
let x = y2 - y1;
return Math.sqrt(x * x + y * y);
}
for (let mergedPath of mergedPaths) {
for (let mergedPathCheck of mergedPaths) {
if (!mergedPathCheck.mergedWith && !mergedPath.mergedWith) {
let distanceStartToEndCheck = this.getDistance(
mergedPathCheck.endPoint?.x,
mergedPath.startPoint?.x,
mergedPathCheck.endPoint?.y,
mergedPath.startPoint?.y,
);
if (distanceStartToEndCheck <= pointMatchingDistance) {
mergedPaths = mergedPaths.map((mp) => {
return mp.id == mergedPath.id ? {
...mp,
path: mergedPathCheck.path + mergedPath.path.replace("M", "L"),
startPoint: mergedPathCheck.startPoint,
mergedWith: mergedPathCheck.id
} : mp
}
)
}
}
}
}
I'll do something that is not very helpfull most of the time - show you a completely different code solving the same problem. But it's incidentally something that I have recently written, and that is relatively thoroughly tested by now.
It takes into account some special cases that might or might not be applicable to your data:
No prior knowledge about the order of the paths in the collection. A path to be "stitched" to another one may be earlier or later in the array.
No prior knowledge whether successfull stitching will involve matching path1.endPoint to path2.startPoint or path2.endPoint to path1.startPoint.
Stitching might even only be possible if you match path1.startPoint to path2.startPoint or path1.endPoint to path2.endPoint, which means you have to reverse the direction of one of the paths.
A path may close in on itself, when path1.endPoint and path1.startPoint are within matching distance (before or after stitching multiple paths together).
As you can see, there are a lot of cases how the paths might fit together, and probably your code missed some of them.
Another difference is that your code tests the condition
if (!mergedPathCheck.mergedWith && !mergedPath.mergedWith)
Since you are changing existing paths, this means that no more than two paths can ever be stitched together - a resulting path made up of three or more parts can never happen. My code works like this:
splice an original path off of the source array (called heap), and add it to a target array (chains) as a new, unconnected path
run through the heap searching for paths matching the last path in chains. If you find one, stitch it onto the path. and run 2. again.
If you find none, go back to 1., until heap is empty.
One method is missing: Chainer.prototype.reverse(path). My original code had the path data as collections of point objects, not as strings. Therefore I can't provide you with code for reversing a path string.
class Chainer {
chains = [];
pointMatchingDistance = 1;
constructor (heap) {
while (heap.length) {
const { id, path, startPoint, endPoint } = heap.shift();
// start a new ordered array of matching paths
const ordered = {
paths: [{d: path, startPoint, id}],
closed: this.matchPoints(first, startPoint, endPoint),
endPoint
};
// recursively add matching paths to the ordered array
this.findNext(ordered, heap, startPoint, endPoint);
// after no more matching paths are found,
// add the ordered array to chains
this.chains.push(ordered);
}
}
matchPoints(p1, p2) {
const y = p2.x - p1.x;
const x = p2.y - p1.y;
return Math.hypot(x, y) <= this.pointMatchingDistance;
}
reverse(path) {
// TBD
}
// splice a path off heap and reverse its direction if needed
getNext(heap, idx, reverse) {
const add = heap.splice(idx, 1)[0];
if (reverse) {
return {
...add,
path: this.reverse(add.path),
startPoint: add.endPoint,
endPoint: add.startPoint
};
} else {
return {...add};
}
}
// add the path at the end of ordered
addNext(ordered, nextWay) {
const { id, path, startPoint, endPoint } = nextWay;
ordered.paths.push({d: path, first, id});
ordered.endPoint = endPoint;
return endPoint;
}
// add the path at the start of ordered
addPrev(ordered, prevWay) {
const { id, path, startPoint, endPoint } = prevWay;
ordered.paths.unshift({d: path, startPoint, id});
return startPoint;
}
// search for a matching path in the heap and identify the direction
findNext(ordered, heap, startPoint, endPoint) {
let next, prev, reverseNext, reversePrev;
for (const [idx, way] of heap.entries()) {
if(this.matchPoints(way.startPoint, endPoint)) {
next = idx;
break;
}
if (this.matchPoints(way.endPoint, startPoint)) prev = idx;
if (this.matchPoints(way.endPoint, endPoint)) reverseNext = idx;
if (this.matchPoints(way.startPoint, startPoint)) reversePrev = idx;
}
if (next !== undefined) {
endPoint = this.addNext(ordered, this.getNext(heap, next, false));
} else if (prev !== undefined) {
startPoint = this.addPrev(ordered, this.getNext(heap, prev, false));
} else if (reverseNext !== undefined) {
endPoint = this.addNext(ordered, this.getNext(heap, reverseNext, true));
} else if (reversePrev !== undefined) {
startPoint = this.addPrev(ordered, this.getNext(heap,reversePrev, true));
// if none of the matching criteria has been met, return
} else return;
// paths closing in on themselves can have no more matching paths
if (this.matchPoints(startPoint, endPoint)) {
ordered.closed = true;
return;
}
// try to find more matching paths
this.findNext(ordered, heap, startPoint, endPoint);
}
// take the ordered arrays in chains
// and join them to one path data string each
writePaths() {
return this.chains.map(ordered => {
let mergedPath;
ordered.paths.forEach(({d, id}) => {
if (mergedPath) {
mergedPath.d += d.replace("M", "L");
} else {
mergedPath = { d, id };
}
});
if (ordered.closed) mergedPath += "Z";
return mergedPath;
});
}
}
const chainer = new Chainer(paths);
const mergedPaths = chainer.writePaths();
Related
I'm following this video by Coding Train. I'm new to coding in general and unable to understand what's missing in the code.
This is the
error message that's being displayed.
sketch.js code
let spritesheet;
let spritedata;
let animation = [];
function preload(){
spritedata = loadJSON('spritedata.json');
spritesheet = loadImage('spritesheet.png');
}
function setup() {
createCanvas(640,480);
let frames = spritedata.frames;
for (let i=0; i<frames.length; i++){
let pos= frames[i].position;
let img = spritesheet.get(pos.x, pos.y, pos.w, pos.h);
animation.push(img);
}
console.log(animation);
}
function draw() {
background (255,255,0);
image(animation[frameCount%animation.length],0,0);
}
spritedata.json code
{
"frames": [
{
"name" : "RunRight01.png",
"position": {
"x": 1,
"y": 1,
"w": 128,
"h": 128
}
},
{
"name" : "RunRight02.png",
"position": {
"x": 131,
"y": 1,
"w": 128,
"h": 128
}
}
]
}
Thank you for your time :)
I have searched for similar questions and have not found a question that allows me to group the count of an attribute with another attribute.
I have a SQL Query in this format:
SELECT weight, COUNT(age)
FROM patient_table
GROUP BY weight
ORDER BY COUNT(age) DESC;
In an elastic search database I have this information:
{"height": 170, "weight": 200, "age": 18}, {"height": 180, "weight": 250, "age": 25},...
I want to translate this SQL query to a string query in elastic search. Therefore, I want the count of the age grouped by the weight and then to return the top value.
I do not know how to pass a select statement to the elastic search query, but I have figured out how to use terms aggregation to group by weight. I assume that I can just grab the top value after it has been ordered and this will be the answer I need.
This has been my attempt thus far:
GET /patient_table/_search
{
"aggs": {
"patient": {
"terms": {"field": "weight.keyword"},
"order": {"_count": "desc"}
}
}
}
EDIT: YD9's solution works, but is there anyway to create a max sub-aggregation to obtain the max value for the previous aggregations? I ask because when I try to create a sub-aggregation after the count, I get an incorrect value of null for the max_value. This is my attempt:
{
"size": 0,
"aggs": {
"weigth": {
"terms": {
"field": "weight.keyword",
"size": 10,
"order": {
"age_count_by_weight": "desc"
}
},
"aggs": {
"age_count_by_weight": {
"value_count": {"field": "age"}
}
}
}
},
"aggs": {
"max_age": {
"max": {"field": "age"}
}
}
}
Any help would be appreciated. Thank you in advance.
If you want to group by weight and order by the total number of ages for each weight, following query should work.
{
"size": 0,
"aggs": {
"weight": {
"terms": {
"field": "weight.keyword",
"size": 10,
"order": {
"age_count_by_weight": "desc"
}
},
"aggs": {
"age_count_by_weight": {
"cardinality": {
"field": "age"
}
}
}
}
}
}
Edit: cardinalty aggregations counts each age once. If you want to count total number of documents, this query should work:
"size": 0,
"aggs": {
"weigth": {
"terms": {
"field": "weight.keyword",
"size": 10,
"order": {
"age_count_by_weight": "desc"
}
},
"aggs": {
"age_count_by_weight": {
"value_count": {
"field": "age"
}
}
}
}
}
}
Edit 2: To get max of age, you can use max_buckets aggregation. This is the sample query
{
"size": 0,
"aggs": {
"weigth": {
"terms": {
"field": "weight.keyword",
"size": 10,
"order": {
"age_count_by_weight": "desc"
}
},
"aggs": {
"age_count_by_weight": {
"value_count": {
"field": "age"
}
}
}
},
"max_age": {
"max_bucket": {
"buckets_path": "weigth>age_count_by_weight"
}
}
}
}
I have the following ElasticSearch data structure for products in a webshop. It's a main product with children that have a special price and a regular price. I have omitted some extraneous info in the children to make it more clear.
{
"_index": "vue_storefront_catalog_1_product_1617378559",
"_type": "_doc",
"_source": {
"configurable_children": [
{
"price": 49.99,
"special_price": 34.99
}
{
"price": 49.99,
"special_price": null
}
]
}
Using the following mapping in Elasticsearh:
{
"vue_storefront_catalog_1_product_1614928276" : {
"mappings" : {
"properties" : {
"configurable_children" : {
"properties" : {
"price" : {
"type" : "double"
}
"special_price" : {
"type" : "double"
}
}
}
}
}
}
}
I have created a loop in a painless script to go through the children of the configurable_children. I need to do this to determine if the main product is on sale, based on the children configurable_children
boolean hasSale = false;
for(item in doc['configurable_children']) {
hasSale = true;
if (1 - (item['special_price'].value / item['price'].value) > 0.5) {
hasSale = true;
}
}
return hasSale
When I look at the results I see the following error:
"failed_shards": [{
"shard": 0,
"index": "vue_storefront_catalog_1_product_1617512844",
"node": "2EQcMMqlQgiuT5GAFPo90w",
"reason": {
"type": "script_exception",
"reason": "runtime error",
"script_stack": ["org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:90)", "org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:41)", "for(item in doc['configurable_children']) { ", " ^---- HERE"],
"script": " boolean hasSale = false; for(item in doc['configurable_children']) { hasSale = true; if (1 - (item['special_price'].value / item['price'].value) > 0.5) { hasSale = true; } } return hasSale ",
"lang": "painless",
"position": {
"offset": 42,
"start": 26,
"end": 70
},
"caused_by": {
"type": "illegal_argument_exception",
"reason": "No field found for [configurable_children] in mapping with types []"
}
}
}]
No field found for [configurable_children] in mapping with types []
Anyone knows what I'm doing wrong? It looks like the for in loop needs a different kind of data? How do I look through all of the products to determine the sale price?
I have some raw data
{
{
"id":1,
"message":"intercept_log,UDP,0.0.0.0,68,255.255.255.255,67"
},
{
"id":2,
"message":"intercept_log,TCP,172.22.96.4,52085,239.255.255.250,3702,1:"
},
{
"id":3,
"message":"intercept_log,UDP,1.0.0.0,68,255.255.255.255,67"
},
{
"id":4,
"message":"intercept_log,TCP,173.22.96.4,52085,239.255.255.250,3702,1:"
}
}
Demand
I want to group this data by the value of the message part of the message.
Output value like that
{
{
"GroupValue":"TCP",
"DocCount":"2"
},
{
"GroupValue":"UDP",
"DocCount":"2"
}
}
Try
I have tried with these codes but failed
GET systemevent*/_search
{
"size": 0,
"aggs": {
"tags": {
"terms": {
"field": "message.keyword",
"include": " intercept_log[,,](.*?)[,,].*?"
}
}
},
"track_total_hits": true
}
Now I try to use pipelines to meet this need.
"aggs" seems to only group fields.
Does anyone have a better idea?
Link
Terms aggregation
Update
My scene is a little special. I collect logs from many different servers, and then import the logs into es. Therefore, there is a big difference between message fields. If you directly use script statements for grouping statistics, it will result in group failure or inaccurate grouping. I try to filter out some data according to the conditions, and then use script to group the operation code (comment code 1), but this code can't group the correct results.
This is my scene to add:
Our team uses es to analyze the server log, uses rsyslog to forward the data to the server center, and then uses logstash to filter and extract the data to es. At this time, there is a field called message in ES, and the value of message is the detailed log information. At this time, we need to count the data containing some values in the message.
comment code 1
POST systemevent*/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"message": {
"query": "intercept_log"
}
}
}
]
}
},
"aggs": {
"protocol": {
"terms": {
"script": "def values = /,/.split(doc['message.keyword'].value); return values.length > 1 ? values[1] : 'N/A'",
"size": 10
}
}
},
"track_total_hits": true
}
comment code 2
POST test2/_search
{
"size": 0,
"aggs": {
"protocol": {
"terms": {
"script": "def values = /.*,.*/.matcher( doc['host.keyword'].value ); if( name.matches() ) {return values.group(1) } else { return 'N/A' }",
"size": 10
}
}
}
}
The easiest way to solve this is by leveraging scripts in the terms aggregation. The script would simply split on commas and take the second value.
POST systemevent*/_search
{
"size": 0,
"aggs": {
"protocol": {
"terms": {
"script": "def values = /,/.split(doc['message.keyword'].value); return values.length > 1 ? values[1] : 'N/A';",
"size": 10
}
}
}
}
Use Regex
POST test2/_search
{
"size": 0,
"aggs": {
"protocol": {
"terms": {
"script": "def m = /.*proto='(.*?)'./.matcher(doc['message.keyword'].value ); if( m.matches() ) { return m.group(1) } else { return 'N/A' }"
}
}
}
}
The results would look like
"buckets" : [
{
"key" : "TCP",
"doc_count" : 2
},
{
"key" : "UDP",
"doc_count" : 2
}
]
A better and more efficient way would be to split the message field into new fields using an ingest pipeline or Logstash.
I'm running into a problem. The bar series is not coming right under it's label. I can't figure out what is going wrong.
Here is the code for it
const FlexibleXYPlot = makeWidthFlexible(XYPlot);
<FlexibleXYPlot height={graphContainer.height} margin={graphContainer.margin} xDistance={0} xType="ordinal">
<HorizontalGridLines />
<XAxis/>
<YAxis orientation="right" style={yAxisStyles} />
{ this.state.data.map((lineData, i) => (
<VerticalBarSeries
key={i}
data={lineData.timeline}
/>
)); }
</FlexibleXYPlot>
and the data is this
[
{
"timeline": [
{
"x": "dataA",
"y": 12.21
}
]
},
{
"timeline": [
{
"x": "dataB",
"y": 21.09
}
]
},
{
"timeline": [
{
"x": "dataC",
"y": 16.66
}
]
}
I figured it out.
For each bar I was creating a separate series. I just had to pass the whole data to a single series. Now if somebody wants each bar to have a different color in a single series they can pass the color with the data like given below
[
{
"x": "dataA",
"y": 12.21,
"color": <something>
},
{
"x": "dataB",
"y": 21.09,
"color": <something>
},
{
"x": "dataC",
"y": 16.66,
"color": <something>
}
]
and we need to pass colorType="literal" to the series itself.