I have a bunch of data in a hash and I am picking from it. Sometimes there will be data there to pick and sometimes there won't. What is the best way to know when there was something found by the pick operator and when there wasn't so I can react to that in my code?
The pick operator will take a second optional parameter that will make it so that it always returns the results in an array. This means that if something is picked, the length of the array will be greater than 0, otherwise it will be 0. You can then use that to do what you are wanting to do.
Example code/app taken from http://kynetxappaday.wordpress.com/2011/01/04/day-30-detecting-empty-pick/
ruleset a60x526 {
meta {
name "hash-pick-detect"
description <<
hash-pick-detect
>>
author "Mike Grace"
logging on
}
global {
dataHash = {
"one": {
"name": "Mike"
}, // number
"two": {
"random": 8
}, // number
"three": {
"name": "Alex"
} // number
}; // dataHash
} // global
rule detect_the_pick {
select when pageview ".*"
foreach dataHash setting (key, value)
pre {
userName = value.pick("$.name", true);
length = userName.length();
}
if (length > 0) then {
notify("Key: #{key}","Name: #{userName}<br/>Length: #{length}") with sticky = true;
}
notfired {
raise explicit event empty_pick
with pickedKey = key;
}
}
rule empty_pick_found {
select when explicit empty_pick
pre {
pickedKey = event:param("pickedKey");
results =<<
Key: #{pickedKey}<br/>
doesn't have a name associated with it to pick from
>>; //' fixing syntax highlighting
}
{
notify("An empty pick was detected",results) with sticky = true;
}
}
}
Related
The goal is to set a column's text filter to be a range of letters that values start with. For example in a customer name column setting the filter to be "starts with" and a range of a-m. The user will enter the two letters that define the start and end of the range (eg. "a" and "m").
Ag-grid's filter docs state that "in range" filtering is only supported for date and number data types. Looking at ag-grid's multi-filter example, multiple filters are combined with an OR condition in the filter model:
{
athlete: {
filterType: "multi",
filterModels: [
{
filterType: "text",
operator: "OR",
condition1: {
filterType: "text",
type: "startsWith",
filter: "a"
},
condition2: {
filterType: "text",
type: "startsWith",
filter: "b"
}
},
null
]
}
}
It looks like the solution is to programmatically find all letters in the range specified by the user, then include a condition for each letter in the "filterModels" array. Is there a more efficient way to do this?
A custom filter was the best solution for this scenario.
I was looking to support an optional range of letters, along with optional additional text in the filter field. Using a regular expression that matched this pattern inside the doesFilterPass method is working as expected.
Example using Vue and lodash:
doesFilterPass(params) {
// Regex matches "[A-M]", "[n-z]", "[E-p] additionaltext", etc
const nameFilterRegex = /^(?<nameStartRange>\[[a-z]{1}-[a-z]{1}\])?(?:\s+)?(?<additionalText>[a-z]+)?(?:\s+)?$/i;
const regexResult = nameFilterRegex.exec(params.data.name);
if (!isNil(regexResult)) {
const nameRange = regexResult.groups.nameStartRange;
const additionalText = regexResult.groups.additionalText;
if (!isEmpty(nameRange)) {
try {
const lastNameRegex = new RegExp(nameRange, "gi");
const matchedChar = params.data.name[0].match(lastNameRegex);
if (isEmpty(matchedChar)) {
return false;
}
} catch {
return false;
}
}
if (!isEmpty(additionalText)) {
if (!params.data.filterValue.includes(additionalText)) {
return false;
}
}
}
return true;
};
In Mongo how can I find all documents that have a given key and value, regardless of where that key appears in the document's key/value hierarchy?
For example the input key roID and value 5 would match both:
{
roID: '5'
}
and
{
other: {
roID: '5'
}
}
There is no built in way to do this. You might have to scan each matched document recursively to try and locate that attribute. Not recommended. You might want to think about restructuring your data or perhaps manipulating it into a more unified format so that it will be easier (and faster) to query.
If your desired key appears in a fixed number of different locations, you could use the $or operator to scan all the possibilities.
Taking your sample documents as an example, your query would look something like this:
db.data.find( { "$or": [
{ "roID": 5 },
{ "other.roID": 5 },
{ "foo.bar.roID": 5 },
{ any other possbile locations of roID },
...
] } )
If the number of documents in collection is not so large, then it can be done by this:
db.system.js.save({_id:"keyValueExisted", value: function (key, value) {
function findme(obj) {
for (var x in obj) {
var v = obj[x];
if (x == key && v == value) {
return true;
} else if (v instanceof Object) {
if (findme(v)) return true;
}
}
return false;
}
return findme(this);
}});
var param = ['roID', '5'];
db.c.find({$where: "keyValueExisted.apply(this, " + tojsononeline(param) + ");"});
Controller_Something extends Controller_Rest {
public function get_something() {
$query = Model_Something::query()->related('hasMany')->get();
return $this->response($query);
}
}
Returns:
{
stuff: here,
looks: good,
hasMany: {
151251: {
id: 151251,
other: stuff
}
}
}
I want the relations as arrays:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff
}
]
}
This happens because the ORM returns related result arrays with keys corresponding to the record's PKEY, and JSON interprets this as an Object. I want these arrays to go through array_values() or something, so the JSON result will use Array.
Currently I am doing this to "solve" the problem:
$res = Format::forge($result_set)->to_array();
$res['hasMany'] = array_values($res['hasMany']);
return $this->response($res);
But this is only useful to one or two levels, where I know the data will be.
If there are relations that are not guaranteed, I don't what to have to error-check every potential subset of a complex Model.
I just want all the one-to-many arrays to be keyed sequentially instead of by the records PKEY.
$query = Model_Something::find()->related('hasMany');
returns a query object in < 1.6, an exception in 1.6, and null in 1.6.1+. So I assume you do something else that produces that result.
If you want arrays as a result instead of objects, you need to convert the result. You can do that by calling to_array() on a model object, or by using the Format class to convert an array of model objects to an array:
$result = \Format::forge($result)->to_array();
function object_to_array($data){
$new_data2= array();
$keys = array_keys((array)$data);
foreach ($keys as $key)
{
$value = $data[$key];
if(is_numeric($key))
{
$new_data2[] = object_to_array($value);
}elseif(is_string($value) || is_null($value))
{
$new_data2[$key] = $data[$key];
}else
{
$new_data2[$key] = object_to_array($value);
}
}
return $new_data2;
}
$formattedArray = \Format::forge(Model_Product::query()->get())->to_array();
$cleanData=object_to_array($formattedArray);
echo \Format::forge($cleanData)->to_json();
This way checks the array key; if key is number and value is object or array clean key
In short: you can't unless you create a hook in Query:hydrate https://github.com/fuel/orm/blob/1.7/develop/classes/query.php#L1083, or shadowing the Query class with some implementation that returns the very same results except for hydrate.
Programmatically it is possible to be done. Following the model below, but for very deep relationships is not interesting by the complexity of the algorithm.
Model:
class Model_Something extends \Orm\Model
{
...
public function relatedAsArray()
{
$this->relationsAsArray($this->_data_relations);
}
private function relationsAsArray(&$relations)
{
foreach ($relations as $key => $relation) {
foreach ($relation as $fields) {
foreach ($fields as $field) {
if (isset($field->_data_relations)) {
$this->relationsAsArray($field->_data_relations);
}
}
}
if (is_array($relation)) {
$relations[$key] = array_values($relation);
}
}
}
}
Call of method:
$something = Model_Something::find($somethingId, array('related' => array('hasMany', 'hasMany.hasOthers')));
$something->relatedAsArray();
The result was exactly like you wanted.
Result:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff,
hasOthers: [
{
id: 12312,
field: other
}, ...
]
}, ...
]
}
Assume there's a data in Mongo! There are a few projects, and there're managers, to who the problems are assigned. Each problem is assigned to different people as the project has different parts.
How can one compute the number, each assignee has been assigned within a project.
For example:
{
"Project A": {
"Ted":10,
"Matt":5
},
"Project B": {
"Mary":3,
"Hector":30
}
}
The following writes the sum but doesn't group it:
db.test.issues.group({
"key": {
"project": true
"assignee":true
},
"initial": {
"countassignee": 0
},
"reduce": function(obj, prev) {
prev.countassignee++;
}
});
It will return as:
{
{
"project": "Project A",
"countassignee":10,
"assignee":"Ted"
}
....
}
The data sample provide by you is pretty confusing. its Project A and project B supposed to be different document.
I believe the best use of group is to aggegrate within a collections, if you want to count within a doucment you may use javascript.
OK! It doesn't seem like the best answer, but it'll do!
first define a cursor sorting by projects
var cursor = db.test.find().sort({projects:1});
a list for the counter:
var asgn_nums = {};
and a variable holding project name;:
var project = '0';
Here counts for each project:
while (cursor.hasNext()) {
var issue = cursor.next();
var p = issue.project;
if(p != project)
{
if(project != '0'){
print(project+":");
printjson(asgn_nums);
}
project = p;
asgn_nums = {};
}
if(issue.assignee != null)
{
if (asgn_nums[issue.assignee] == null)
asgn_nums[issue.assignee] = 1;
else
asgn_nums[issue.assignee]+= 1;
}
if(!cursor.hasNext())
{
print(project+":");
printjson(asgn_nums);
}
}
I have a hash coming back from an XML datasource that looks like this:
{...,
'records' :{
'record' :[
{'availability' :{'$t' :'available'}, ...},
{'availability' :{'$t' :'available'}, ...}
]
}
};
I'd like to get all the record hashes into an array so I can filter() it and do some other operations. However, when I have this statement in my pre block,
raw_records = raw.pick("$..record");
the array that gets returned is an array of two empty strings:
var raw_records = ['', ''];
The odd thing is that I can pick out just availability with expected results:
availability = raw.pick("$..availability.$t");
producing
var availability = ['available', 'available'];
What's wrong with my first pick()?
EDIT: Here is a more complete version that should help with reproducing the problem. It's slightly different, since I'm using the JSON version of the web service now:
global {
datasource hbll <- "https://svc.lib.byu.edu/services/catalog/v1/search/?field=isbn&format=json&terms=";
}
rule new_rule {
select when pageview "amazon.com/.*/?dp/(.*)/" setting (isbn)
pre {
//This is the array with two empty strings...
raw = datasource:hbll(isbn);
myfilter = function(x) { x.pick("availability") eq "available"; };
records = raw.filter(myfilter);
len = records.length();
availability = records.pick("$..availability");
middleman = len > 1 => availability[0] | availability;
available = middleman eq "available" => true | false;
url_list = records.pick("$..url");
url = len > 1 => url_list[0] | url_list;
msg = <<
<p>This book is available for checkout at the BYU Library.</p>
More information
>>;
}
notify("BYU Harold B. Lee Library", msg) with sticky=true;
}
I'm going to need a more complete example. The test app and results I got are below:
ruleset a8x167 {
meta {
name "Pick - Array of Hashes"
description <<
Testing
>>
author "Sam Curren"
logging on
}
dispatch {}
global {
raw = {
'records' :{
'record' :[
{'availability' :{'$t' :'available'}},
{'availability' :{'$t' :'available'}}
]
}
};
}
rule test {
select when pageview ".*" setting ()
pre {
raw_records = raw.pick("$..record");
availability = raw.pick("$..availability.$t");
}
notify("Hello World", "This is a sample rule.");
}
}
And Results:
var raw_records = [{'availability' :{'$t' :'available'}}, {'availability' :{'$t' :'available'}}];
var availability = ['available', 'available'];