elastic, can I use my own global ordinals with a painless terms aggregation script? - elasticsearch

Here's what one of my document might look like
{
"CC":{"colors":["Blue","Green","Yellow"]},
"CN":{"colors":["White","Green","Blue"]},
"WA":{"colors":["Orange","Green","Blue"]},
...
}
I want a terms aggregation, on the intersection of two fields CC.colors and CN.colors. That is, for this document, that field will have ["Green", "Blue"] in the intersection, and I want a term aggregation on this intersection.
As far as I understand, there are two ways to do it.
1) A painless script in terms aggregation, which returns the intersection of these two arrays for each document.
2) A new field created during index time, maybe called CC_CN.colors, which holds intersection for all docs.
I can't go ahead with 2 because my combinations will be too many. I can have any need during search time, like CC_CN, or CC_WA, or WA_CN_CC etc.
For 1), it works, but gets painfully slow. One reason is that 1) cannot use global ordinals.
Is there any trick, that I can ask elastic to build a custom global ordinal for my painless terms aggregation? I know there are just 25 colors in my system, so can give all colors to elastic somewhere, and "assure" them that I'll not return anything else but these colors from my aggregation?
Or, if I encode and store numbers instead of strings in index, would this be faster for elastic? e.g. 0 instead of "Black", 1 instead of "Green" etc.?
Other than intersection, my other use cases involve union etc. as well. Thanks for reading!

To answer it myself, we ended up asking for these arrays in _source and performing union/intersection in Ruby.
It is also possible to do this in painless, and that offers a bit better performance. Elastic uses map to do aggregation, and I couldn't figure out any way to use global ordinals. I don't think its possible.
We wrote code that generates painless code to perform intersection and union between arrays.
For any future wanderer, here's what the generated code looks like:
This is for union:
Stream stream = [].stream();
String[] stream_keys = new String[] {'CC.colors', 'CN.colors'};
for (int i = 0; i < stream_keys.length; ++i) {
if (doc.containsKey(stream_keys[i])) {
stream = Stream.concat(stream, doc[stream_keys[i]].stream());
}
}
stream = stream.distinct();
And this is for intersection (stream, list_0_stream and list_1_stream intersection):
List list_0 = list_0_stream.collect(Collectors.toList());
List list_1 = list_1_stream.collect(Collectors.toList());
return stream.filter(list_0::contains).filter(list_1::contains).toArray();
The performance seems to be acceptable.

There are 2 method offer to you
Cost alot of my time
^ ^
ColorMap={"Blue":0,"Green":1,"Yellow":2,"White":3,"Orange":4};
ReverseColorMap=["Blue","Green","Yellow","White","Orange"];
var All={
"CC":{"colors":["Blue","Green","Yellow"]},
"CN":{"colors":["White","Green","Blue"]},
"WA":{"colors":["Orange","Green","Blue"]}
};
//Cover Encode
function EncodeColor(T1){
var T2 = JSON.parse(JSON.stringify(T1));//Clone Original
for(var i in T2){
for(var j in T2[i]["colors"]){
T2[i]["colors"][j]=ColorMap[T2[i]["colors"][j]];
}
}
return T2;
}
var NewAll=EncodeColor(All);
console.log(All);
console.log(NewAll);
function SortColor(T1){
for(var i in T1){
T1[i]["colors"].sort((a, b) => {
return a-b;
});
}
}
function BuildSameColor(T1){
var CombineNew={};
var Name_Temp=[];
for(var i in T1){
Name_Temp.push(i);
}
for(var i =0;i<Name_Temp.length;i++){
for(var j =i+1;j<Name_Temp.length;j++){//j=i+1 because CC_CC not valid CC_CN is valid etc...
CombineNew[Name_Temp[i]+"_"+Name_Temp[j]]={"colors":T1[Name_Temp[i]]["colors"].concat(T1[Name_Temp[j]]["colors"])};//combine color array
}
}
SortColor(CombineNew);//Sort Result
//Sort will reduce compare time(later) when color is a lot
for(var i in CombineNew){
var NewAr=[];
for(var j=0;j<CombineNew[i]["colors"].length-1;j++){
if(CombineNew[i]["colors"][j]==CombineNew[i]["colors"][j+1]){
NewAr.push(CombineNew[i]["colors"][j]);
}
}
CombineNew[i]["colors"]=NewAr;
}
return CombineNew;
}
var TTT=BuildSameColor(NewAll);
console.log(TTT);
//Then Decode Color
function DecodeColor(T1){
var T2 = JSON.parse(JSON.stringify(T1));//Clone Original
for(var i in T2){
for(var j in T2[i]["colors"]){
T2[i]["colors"][j]=ReverseColorMap[T2[i]["colors"][j]];
}
}
return T2;
}
var TTTQQ=DecodeColor(TTT);
console.log(TTTQQ);
//This Also work any length of color
var Examp={
"CC":{"colors":["Blue","Green","Yellow","Orange"]},
"CN":{"colors":["White","Green","Blue"]},
"WA":{"colors":["Orange","Green","Blue"]}
};
var E_Examp=EncodeColor(Examp);
var Com_E_E_Examp=BuildSameColor(E_Examp);
var D_Examp=DecodeColor(Com_E_E_Examp);
console.log(D_Examp);
Enjoin it!!!!!!!!
ColorMap={"Blue":0,"Green":1,"Yellow":2,"White":3,"Orange":4};
ReverseColorMap=["Blue","Green","Yellow","White","Orange"];
var All={
"CC":{"colors":["Blue","Green","Yellow"]},
"CN":{"colors":["White","Green","Blue"]},
"WA":{"colors":["Orange","Green","Blue"]}
};
//Cover Encode
function EncodeColor(T1){
var T2 = JSON.parse(JSON.stringify(T1));//Clone Original
for(var i in T2){
for(var j in T2[i]["colors"]){
T2[i]["colors"][j]=ColorMap[T2[i]["colors"][j]];
}
}
return T2;
}
var NewAll=EncodeColor(All);
console.log(All);
console.log(NewAll);
function SortColor(T1){
for(var i in T1){
T1[i]["colors"].sort((a, b) => {
return a-b;
});
}
}
function StaticSearch(T1,Name1,Name2){
if(Name1 in T1 && Name2 in T1 ){
var Temp= T1[Name1]["colors"].concat(T1[Name2]["colors"]);
var T2=[];
Temp.sort((a, b) => {
return a-b;
});
for(var i=0;i<Temp.length-1;i++){
if(Temp[i]==Temp[i+1]){
T2.push(Temp[i]);
}
}
var ReturnObj={};
ReturnObj[Name1+"_"+ Name2]={};
ReturnObj[Name1+"_"+ Name2]["colors"]=T2;
return ReturnObj;
}
return null;
}
var SearchResult=StaticSearch(NewAll,"CC","CN");
console.log(SearchResult);
//Then Decode Color/*
function DecodeColor(T1){
var T2 = JSON.parse(JSON.stringify(T1));//Clone Original
for(var i in T2){
for(var j in T2[i]["colors"]){
T2[i]["colors"][j]=ReverseColorMap[T2[i]["colors"][j]];
}
}
return T2;
}
var TTTQQ=DecodeColor(SearchResult);
console.log(TTTQQ);
//This Also work any length of color
var Examp={
"CC":{"colors":["Blue","Green","Yellow","Orange"]},
"CN":{"colors":["White","Green","Blue"]},
"WA":{"colors":["Orange","Green","Blue"]}
};
var E_Examp=EncodeColor(Examp);
var Com_E_E_Examp=StaticSearch(E_Examp,"WA","CC");
var D_Examp=DecodeColor(Com_E_E_Examp);
console.log(D_Examp);
Another Method Static Search

Related

Speed Up Find-and-Replace Google Apps Script Function for sheets

I've written a pretty simple script that successfully takes information from one sheet in a Google Spreadsheet, and replaces information in a column in another sheet in the same spreadsheet pending satisfaction of two criteria: the receiving row has the same "Customer ID" and "Product Type." I say "simple" because it's intuitive, but extremely computationally demanding (taking nearly 30 seconds to run!).
From what I've read online, it's the sequential read and write operations that are causing the slowdown. I'm assuming that if I sort the sheets in question on the two criteria and THEN do a function that writes over subsequent rows, I may be able to speed it up. I'm a little weak on algorithms, so I'm still scratching my head on how to do this elegantly.
Does anyone have any suggestions? Below is my original script, and I've already made sure that the spreadsheet collapses empty rows, so time isn't wasted iterating over nothing.
function replaceRawWithRepChanges(receivedSheet) {
var ss = SpreadsheetApp.openById(receivedSheet);
var repchanges = ss.getSheetByName('repchanges');
var rawSheet = ss.getSheetByName('Sheet1');
var rawTMtoReplace = rawSheet.getRange('P2:P');
var repCustID = repchanges.getRange('A1:A').getValues();
var repTM = repchanges.getRange('F1:F').getValues();
var repCategory = repchanges.getRange('G1:G').getValues();
var rawCustID = rawSheet.getRange('A2:A').getValues();
var rawTM = rawSheet.getRange('P2:P').getValues();
var rawCategory = rawSheet.getRange('U2:U').getValues();
var repInfo = [repCustID, repTM, repCategory];
var rawInfo = [rawCustID, rawTM, rawCategory];
for (var i=0; i < rawInfo[0].length; i++) {
for (var j=0; j < repInfo[0].length; j++) {
// var thisRawCust = rawInfo[0][i];
// var thisRepCust = repInfo[0][j];
if (rawInfo[0][i].toString() == repInfo[0][j].toString()) {
// var thisRawCategory = rawInfo[2][i];
// var thisRepCategory = repInfo[2][j];
if (rawInfo[2][i].toString() == repInfo[2][j].toString()) {
// var repvalue = repInfo[1][j];
rawInfo[1][i] = repInfo[1][j];
// var newRawValue = rawInfo[1][i];
}
}
}
}
return rawInfo[1];
}
Yes, you should sort the data (perhaps using the SORT command, which does work with multiple columns). Then, using two pointers, you only have to go down the columns once, rather than checking the entirety of repInfo for matches for every single row in rawInfo.
Once you've sorted the information, your loop might look like the following:
var i = 0;
var j = 0;
while (i < rawInfo[0].length && j < repInfo[0].length) {
if (rawInfo[0][i].toString() == repInfo[0][j].toString()) {
if (rawInfo[2][i].toString() == repInfo[2][j].toString()) {
rawInfo[1][i]=repInfo[1][j];
i++;
j++;
} else if (rawInfo[2][i].toString() < repInfo[2][j].toString()) {
i++;
} else {
j++;
}
} else if (rawInfo[0][i].toString() < repInfo[0][j].toString()) {
i++;
} else {
j++;
}
}

How to create all possible variations from single string presented in special format?

Let's say, I have following template.
Hello, {I'm|he is} a {notable|famous} person.
Result should be
Hello, I'm a notable person.
Hello, I'm a famous person.
Hello, he is a notable person.
Hello, he is a famous person.
The only possible solution I have in mind - full search, but it is not effective.
May be there is a good algorithm for such kind of job but I do not know what task about. All permutations in array is very close to this but I have no idea how to use it here.
Here is working solution (it's part of object, so here is only relevant part).
generateText() parses string and converts 'Hello, {1|2}, here {3,4}' into ['Hello', ['1', '2'], 'here', ['3', '4']]]
extractText() takes this multidimensional array and creates all possible strings
STATE_TEXT: 'TEXT',
STATE_INSIDE_BRACKETS: 'INSIDE_BRACKETS',
generateText: function(text) {
var result = [];
var state = this.STATE_TEXT;
var length = text.length;
var simpleText = '';
var options = [];
var singleOption = '';
var i = 0;
while (i < length) {
var symbol = text[i];
switch(symbol) {
case '{':
if (state === this.STATE_TEXT) {
simpleText = simpleText.trim();
if (simpleText.length) {
result.push(simpleText);
simpleText = '';
}
state = this.STATE_INSIDE_BRACKETS;
}
break;
case '}':
if (state === this.STATE_INSIDE_BRACKETS) {
singleOption = singleOption.trim();
if (singleOption.length) {
options.push(singleOption);
singleOption = '';
}
if (options.length) {
result.push(options);
options = [];
}
state = this.STATE_TEXT;
}
break;
case '|':
if (state === this.STATE_INSIDE_BRACKETS) {
singleOption = singleOption.trim();
if (singleOption.length) {
options.push(singleOption);
singleOption = '';
}
}
break;
default:
if (state === this.STATE_TEXT) {
simpleText += symbol;
} else if (state === this.STATE_INSIDE_BRACKETS) {
singleOption += symbol;
}
break;
}
i++;
}
return result;
},
extractStrings(generated) {
var lengths = {};
var currents = {};
var permutations = 0;
var length = generated.length;
for (var i = 0; i < length; i++) {
if ($.isArray(generated[i])) {
lengths[i] = generated[i].length;
currents[i] = lengths[i];
permutations += lengths[i];
}
}
var strings = [];
for (var i = 0; i < permutations; i++) {
var string = [];
for (var k = 0; k < length; k++) {
if (typeof lengths[k] === 'undefined') {
string.push(generated[k]);
continue;
}
currents[k] -= 1;
if (currents[k] < 0) {
currents[k] = lengths[k] - 1;
}
string.push(generated[k][currents[k]]);
}
strings.push(string.join(' '));
}
return strings;
},
The only possible solution I have in mind - full search, but it is not effective.
If you must provide full results, you must run full search. There is simply no way around it. You don't need all permutations, though: the number of results is equal to the product of the number of alternatives in each template.
Although there are multiple ways to implement this, recursion is among the most popular approaches. Here is some pseudo-code to get you started:
string[][] templates = {{"I'm", "he is"}, {"notable", "famous", "boring"}}
int[] pos = new int[templates.Length]
string[] fills = new string[templates.Length]
recurse(templates, fills, 0)
...
void recurse(string[][] templates, string[] fills, int pos) {
if (pos == fills.Length) {
formatResult(fills);
} else {
foreach option in templates[pos] {
fills[pos] = option
recurse(templates, fills, pos+1);
}
}
}
It seems like the best solution here is going to be n*m where n=the first array and m= the second array . There are nm required lines of output, which means that as long as you are only doing nm you aren't doing any extra work
The generic running time for this is where there is more than 2 arrays with options, it would be
n1*n2...*nm where each of those is equal to the size of the respective list
A nested loop where you just print out the value for the current index of the outer loop along with the current value for the index of the inner loop should do this properly

firebase sort reverse order

var playersRef = firebase.database().ref("team_mapping/");
playersRef.orderByChild("score").limitToFirst(7).on("child_added", function(data) {
}
Using this query, I can sort it in ascending order. But, I wanted to sort in descending order. Can any body suggest me with some way to do this. Any help will be appreciated.
Thanks in Advance!!
just use reverse() on the array , suppose if you are storing the values to an array items[] then do a this.items.reverse()
ref.subscribe(snapshots => {
this.items = [];
snapshots.forEach(snapshot => {
this.items.push(snapshot);
});
**this.items.reverse();**
},
You can limit to the last 7 which will give you the highest scores since the query is in ascending order. Then all you have to do is reverse the last 7 you get for them to be in descending order.
var playersRef = firebase.database().ref("team_mapping/");
var playersData = [];
var pageCursor;
playersRef.orderByChild("score").limitToLast(7).on("child_added", function(data) {
playersData = [data] + playersData;
}
UPDATE
Here is an example of how you could paginate by score.
const query = firebase.database().ref("team_mappings").orderByChild('score');
var snapshots = [];
function reversedChildren(snapshot) {
var children = [];
snapshot.forEach(function (child) { children.unshift(child); });
return children;
}
function addPlayerToLeaderBoard(snapshot) {
var key = snapshot.key;
var place = snapshots.indexOf(snapshot) + 1;
var score = snapshot.child('score').val();
$('#leaderboard').append(`<li><b>${key}: ${score}</li>`);
}
function parsePage(snapshot) {
var children = reversedChildren(snapshot);
children.forEach(function (child) {
players.push(child);
addPlayerToLeaderBoard(child);
});
pageCursor = children[children.length - 1];
}
function reloadPlayers() {
players = [];
pageCursor = null;
query.limitToLast(5).once('value').then(parsePage);
}
function loadMorePlayers() {
query.endAt(pageCursor).limitToLast(5).once('value').then(parsePage);
}
reloadPlayers();
$('#reload').on('click', reloadPlayers);
$('#load_more').on('click', loadMorePlayers);
Unfortunatelly, there is not a shortcut for this yet. However, if you are using Listview or Recyclerview as UI, you could simply solve this issue by reversing the UI,by reversing Listview or Recyclerview
For Recyclerview you could reverse as follows:
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager)
For Listview:
listView.setStackFromBottom(true);

Meteor sort collection random

I want to get a randomly sorted set from a Meteor collection. What is the best/most efficient way?
Mongo options are controversial.
I am currently using underscore _.shuffle which is quite neat, eg:
Template.userList.helpers({
users: function() {
return _.shuffle(Meteor.users.find().fetch());
}
});
I use Jade, so maybe there's an option at the templating level?
You could use Lodash _.shuffle, like so:
Template.userList.helpers({
users: function() {
return _.shuffle(Meteor.users.find().fetch());
}
});
As hilarious as that might seem, there is a difference under the hood:
Underscore (source)
_.shuffle = function(obj) {
var set = isArrayLike(obj) ? obj : _.values(obj);
var length = set.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = _.random(0, index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = set[index];
}
return shuffled;
};
Lo-Dash (slightly modified for ease of comparison, source)
_.shuffle = function(collection) {
MAX_ARRAY_LENGTH = 4294967295;
return sampleSize(collection, MAX_ARRAY_LENGTH);
}
function sampleSize(collection, n) {
var index = -1,
result = toArray(collection),
length = result.length,
lastIndex = length - 1;
n = clamp(toInteger(n), 0, length);
while (++index < n) {
var rand = baseRandom(index, lastIndex),
value = result[rand];
result[rand] = result[index];
result[index] = value;
}
result.length = n;
return result;
}
You can take a look at this SO discussion for a more in-depth look at comparing the two libraries.
Both, Underscore and Lo-Dash, use the Fisher-Yates shuffle which you'd have a hard time doing "better" than.

Sorting an array collection based on value of array in flex

I've been looking at sorting array collections. So far I've seen things like sorting numerically in ascending and descending order. What I'm looking for is to discover away to sort an arraycollection based on the order of values in another array.
For Example:
I have an array containing 10 numerical values. I also have an arraycollection. One of the properties of each arracycollection entry corresponds to one of the values in the former array.
I want to be able to sort the arraycollection based on the order of the values in the array.
Whats the best way to this?
I was thinking of looping through the first array, getting the first value, finding the entry in the arraycollection with that value and then adding it to a new arraycollection but it seems kinda long winded and I was hoping there might be a clever way to do it.
EDIT
This is what I've pieced together so far. Seems a bit too round about though
private function parseXML(xml:XML):void
{
var s:String=xml.toXMLString()
artistinfo=convertXmlToArrayCollection(s)
sort()
dispatchEvent(new XMLEvent(XMLEvent.XML_PARSED))
//artistinfo.sort()
}
private function clone(source:Object):*
{
var myBA:ByteArray=new ByteArray();
myBA.writeObject(source);
myBA.position=0;
return (myBA.readObject());
}
private function sort():void
{
var myAC:ArrayCollection=new ArrayCollection(clone(artistinfo.source));
//artistinfo=new ArrayCollection();
var amt:int=trackids.length;
var value:Number=0
var arr:*
var index:Number
for (var i:int=0; i < amt; i++)
{
value=trackids[i];
index=getItemIndexByProperty(myAC, "id", new String(value))
artistinfo[i]=myAC.getItemAt(index)
}
}
public function getItemIndexByProperty(array:ArrayCollection, property:String, value:String):Number
{
for (var i:Number=0; i < array.length; i++)
{
var obj:Object=Object(array[i])
if (obj[property].value == value)
return i;
}
return -1;
}
I'm going to add in my edit as the answer for now.
It may be useful for somebody who lands here from a search
If anybody can offer a more concise or better way please post!
private function parseXML(xml:XML):void
{
var s:String=xml.toXMLString()
artistinfo=convertXmlToArrayCollection(s)
sort()
dispatchEvent(new XMLEvent(XMLEvent.XML_PARSED))
//artistinfo.sort()
}
private function clone(source:Object):*
{
var myBA:ByteArray=new ByteArray();
myBA.writeObject(source);
myBA.position=0;
return (myBA.readObject());
}
private function sort():void
{
var myAC:ArrayCollection=new ArrayCollection(clone(artistinfo.source));
//artistinfo=new ArrayCollection();
var amt:int=trackids.length;
var value:Number=0
var arr:*
var index:Number
for (var i:int=0; i < amt; i++)
{
value=trackids[i];
index=getItemIndexByProperty(myAC, "id", new String(value))
artistinfo[i]=myAC.getItemAt(index)
}
}
public function getItemIndexByProperty(array:ArrayCollection, property:String, value:String):Number
{
for (var i:Number=0; i < array.length; i++)
{
var obj:Object=Object(array[i])
if (obj[property].value == value)
return i;
}
return -1;
}

Resources