converting mongo map reduce code to php - mongodb

I have the following mongo shell code that I'm trying to use in my php application.
It's basically taking 800 evenly distributed values over a time frame, for use in a graph.
var map = function () {
if (endTime < 0) {
var start = new ISODate("2013-09-01T00:00:00.000Z");
var end = new ISODate("2013-11-01T00:00:00.000Z");
var startMilli = start.getTime();
var endMilli = end.getTime();
var interval = endMilli - startMilli;
delta = interval / 800;
endTime = startMilli + delta;
}
if (endTime < this.date.getTime()) {
resArray = {};
while (endTime < this.date.getTime()) {
endTime += delta;
}
}
var id = this.homeId + this.sensor;
if (typeof resArray[id] == 'undefined') {
resArray[id] = 1;
emit({
homeId: this.homeId,
sensor: this.sensor,
date: this.date,
val: this.val
}, {
x: 1
});
}
};
var reduce = function (key, values) {
return values[0];
};
db.passiv.mapReduce(
map,
reduce,
{ query:
{ homeId: 35600,
sensor : {
$in :[ "z1t","ts1"]
},
date : {
$gte : new ISODate("2013-09-01T00:00:00.000Z"),
$lte : new ISODate("2013-11-01T00:00:00.000Z")
} },
scope : {
resArray : {},
delta : -1,
endTime : -1
},
out: 'TEST1',
sort : {
date:1
}
});
It execute fine in the shell but my attempt to convert it to php is not giving me any results:
$from = '2013-09-01T00:00:00.000Z';
$to = '2013-11-01T00:00:00.000Z';
$map = new MongoCode("
var map = function () {
if (endTime < 0) {
var start = new ISODate(".$from.");
var end = new ISODate(".$to.");
var startMilli = start.getTime();
var endMilli = end.getTime();
var interval = endMilli - startMilli;
delta = interval / 800;
endTime = startMilli + delta;
}
if (endTime < this.date.getTime()) {
resArray = {};
while (endTime < this.date.getTime()) {
endTime += delta;
}
}
var id = this.homeId + this.sensor;
if (typeof resArray[id] == 'undefined') {
resArray[id] = 1;
emit({
homeId: this.homeId,
sensor: this.sensor,
date: this.date,
val: this.val},
{ x: 1 });
}
};");
$reduce = new MongoCode('
var reduce = function (key, values) {
return values[0];
};');
$constraint = array('homeId' => 32168);
$date = array('$gte' => new MongoDate(1377986400), '$lt' => new MongoDate(1383260400));
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => array('z1t', 'ts1'))));
$rs = $passivCollection->command(
array(
"mapreduce" => "passiv",
"query" => $condition,
"map" => $map,
"reduce" => $reduce,
"scope" => array(
"resArray" => array(),
"delta" => -1,
"endTime" => -1
),
"out" => "TEST1",
"sort" => array("date" => 1)
));
Somehow I think it's the scope in the command at the end that's not working properly and I cant seem to find anything useful on the scope aspect anywhere. Any help is greatly appreciated.

A good example of doing Map Reduce in PHP is located here: http://php.net/manual/en/mongodb.command.php at example #3.
Basically one of the problems you have is that the MongoCode objects do not represent anon functions anymore but instead a variable that cannot be returned.
Instead you want to define your functions like:
$reduce = new MongoCode('
function (key, values) {
return values[0];
};
');
That should fix it I think.
edit
There is a problem with your PHP in the your edit:
$start = new MongoDate(strtotime("2013-09-01 00:00:00"));
$end = new MongoDate(strtotime("2013-11-01 00:00:00"));
$constraint = array('homeId' => '32168');
$date = array('$gte' => $start, '$lt' => $end);
$sensors = array('z1t');
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => $sensors)));
var_dump($condition);
$tt = $passivCollection->selectCollection('passiv')->count($date);
var_dump($tt);
You are using:
$date = array('$gte' => $start, '$lt' => $end);
Without defining a field it should search on. Try:
$start = new MongoDate(strtotime("2013-09-01 00:00:00"));
$end = new MongoDate(strtotime("2013-11-01 00:00:00"));
$constraint = array('homeId' => '32168');
$date = array('date' => array('$gte' => $start, '$lt' => $end));
$sensors = array('z1t');
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => $sensors)));
var_dump($condition);
$tt = $passivCollection->selectCollection('passiv')->count($date);
var_dump($tt);
Where the date string in:
$date = array('date' => array('$gte' => $start, '$lt' => $end));
is replaced by your field name.

Related

Laravel-MongoDB, how order result by relevant

I use jenssegers/laravel-mongodb. I make scope
public function scopeWhereFullText($query, $search)
{
return $query->whereRaw(['$text' => ['$search' => $search]],['score'=>['$meta'=>'textScore']]);
}
How I can order By score field like in MongoDB js example:
db.products.find({$text:{$search:"SomeText"}},{score:{$meta:'textScore'}}).sort({score:{$meta:'textScore'}})
What is the solution without crutch:
public function scopeWhereFullText($query, $search)
{
$query->getQuery()->projections = ['score'=>['$meta'=>'textScore']];
return $query->whereRaw(['$text' => ['$search' => $search]]);
}
and in result
$products = Product::whereFullText($request->get('q',''))
->orderBy('score',['$meta'=>'textScore'])->get();
$max = $products->max('score');
$min = $products->min('score');
$products = $products->filter(function($item) use($max,$min){
return $item->score > ($max+$min)/2;
});
Use
$results = DB::connection()->collection('_User')->whereRaw(['$text' => ['$search' => 'SEARCH QUERY']])->project(['score'=>['$meta'=>'textScore']])->orderBy('score', ['$meta' => "textScore"])->limit(10)->get();

GroupBy SQL to EF Lambda syntax

I'm currently defeated in my attempts to map the following sql to an EF lambda style query:
sql:
SELECT ResellerId, Name, Month, SUM(MonthTotal) AS MonthTotal
FROM dbo.View_ResellerYearMonthBase
WHERE (Year = 2015) AND (EventId > 0) AND (ValidationResponse IS NOT NULL)
GROUP BY ResellerId, Month, Name
I've tried
public JsonResult GetResellerAnnualReportData(int year, bool includeUnValidated, bool includeUnbooked)
{
var qry = _reportsDal.View_ResellerYearMonthBase.AsQueryable();
qry = qry.Where(x => x.Year == year);
if (!includeUnValidated) { qry = qry.Where(x => x.ValidationResponse.Length > 0); }
if (!includeUnbooked) { qry = qry.Where(x => x.EventId > 0); }
qry = qry.GroupBy(x => new { x.ResellerId, x.Month, x.Name }).Select(y => new ResellerAnnualReportDto
{
ResellerId = y.Key.ResellerId,
Month = y.Key.Month.Value,
Name = y.Key.Name,
SumMonthTotal = y.Sum(z => z.MonthTotal.Value)
});
throw new NotImplementedException();//keep the compiler happy for now
}
How should I go about achieving the SQL Query with the function parameters (year, includeUnValidated etc)
.GroupBy(key => new { key.ResellerId, key.Month, key.Name},
el => el.MonthTotal,
(key, el) => new ResellerAnnualReportDto
{
ResellerId = key.ResellerId,
Month = key.Month,
Name = key.Name,
MonthTotal = el.Sum(s => s.MonthTotal)
}).ToList();
This uses the overload with keyselector, elementselector and resultselector. This way you avoid making the IGrouping<key,value> and get the results you want immediately. Couldn't test though.
Here is how to do this:
var result = qry.GroupBy(x => new { x.ResellerId, x.Month, x.Name }).
Select(y => new {
y.Key.ResellerId,
y.Key.Month,
y.Key.Name,
SumMonthTotal = y.Sum(z => z.MonthTotal)
}).ToList();

How to add or inc to an array in a update query?

If I have a doc which has an array which contains a items which represents counts for a day, perhaps like :-
{
data : [ {'20141102' : 2 },{'20141103' : 4 } ]
}
when I do an update, and I have a string '20141103' and then later a '20141104' I want to either inc the array entry or add a new array entry. Is this possible with an update?
Yes, it's feasible. I tried like this:
(run on mongo shell; both client and server are V2.6.4)
function tryAndFine(coll, key, value) {
var entry = {};
entry[key] = value;
var parent = 'data';
var prefix = parent + '.';
function incrementOnly() {
var criteria = {};
criteria[prefix + key] = {$exists : true};
var update = {};
update[prefix + "$." + key] = value;
var result = coll.update(criteria, {$inc : update});
// if increment fails, try to add a new one
if (result.nModified == 0) {
addNewElement();
}
}
function addNewElement() {
var criteria = {};
criteria[prefix + key] = {$exists : false};
var update = {};
update[parent] = entry;
var result = coll.update(criteria, {$push : update}, {upsert : true});
// if exists, try to increment the count
if (result.upserted == 0 && result.nModified == 0) {
incrementOnly();
}
}
// run entry
incrementOnly();
}
// test
var c = db.c;
c.drop();
tryAndFine(c, '20141103', 1);
tryAndFine(c, '20141103', 1);
tryAndFine(c, '20141104', 1);
tryAndFine(c, '20141105', 1);
tryAndFine(c, '20141104', 1);
// output
{
"_id" : ObjectId("54577e1a3502852bd4ad2395"),
"data" : [ {
"20141103" : 2
}, {
"20141104" : 2
}, {
"20141105" : 1
} ]
}

Get just inserted id

I have here many-to-many table, i need to get just inserted id - BlogId. lastInsertId() is NULL result. Any ideas?
$table = new Blog_Model_Blog_Table();
$relTable = new Blog_Model_Relation_Table();
$Object = $table->createRow();
$form = new Blog_Form_Blog_Add($Object);
if ($this->getRequest()->isPost() and $form->isValid($_POST)) {
Blog_Model_Blog_Manager::add($Object);
$blogId = $table->getAdapter()->lastInsertId();
foreach ($_POST['category_id'] as $value) {
$relTable->insert(array('id' => $blogId, 'category_id' => $value));
}
Blog_Model_Blog_Manager:
class Blog_Model_Blog_Manager
{
static function add(Blog_Model_Blog_Item &$Object)
{
$data = array(
'time_add' => time(),
'time_edit' => time(),
'url_keyword' => Ap_Filter_Translit::asURLSegment($Object->name)
);
$Object->setFromArray($data)
->save();
}
I make it with mySql function LAST_INSERT_ID():
$blogId = $table->fetchRow($table->select()->from($table)->columns('LAST_INSERT_ID() as idi'));

Moled Dataset using Pex and Moles

I want to test below method.I am returning dataset and checking table collection.
public string FetchAllDetails(string useCase, string strMainFlow)
{
var strError = new StringBuilder();
DataSet ds = _dal.Fetchdetails(useCase, strMainFlow, useCaseId, useCaseName);
DataTable dt = new DataTable();
dt = ds.Tables[0];
if (dt.Rows.Count > 0)
{
BindUsecaseId(strError, useCaseName);
foreach (var row in ds.Tables[0].AsEnumerable())
BindError(strError, GetOriginalFieldName(row[0].ToString()));
}
return strError.ToString();
}
I wrote below unit test using Pex.
[PexMethod]
public string FetchAllDetailsTest(string useCase, string strMainFlow)
{
SIUploadtoolDAL dal = new SIUploadtoolDAL();
UploadtoolBL target = new UploadtoolBL(dal);
MDataTableCollection dt2 = new MDataTableCollection();
dt2.Add = () =>
{
MDataTable dt1 = new MDataTable();
dt1.ColumnsGet = () =>
{
MDataColumnCollection dcCollection = new MDataColumnCollection();
dcCollection.Add = () =>
{
MDataColumn dc = new MDataColumn();
dc.ColumnNameGet = () =>
{
return "string";
};
dc.DataTypeGet = () =>
{
return typeof(string);
};
return dc;
};
return dcCollection;
};
return dt1;
};
dal.FetchdetailsStringStringInt32StringSqlTransaction = (useCaselocal, strMainFlowlocal, useCaseIdlocal, useCaseNamelocal) =>
{
MDataSet ds = new MDataSet();
ds.TablesGet = () =>
{
MDataTableCollection dt = new MDataTableCollection();
dt.Add = () =>
{
MDataTable dt1 = new MDataTable();
dt1.ColumnsGet = () =>
{
MDataColumnCollection dcCollection = new MDataColumnCollection();
dcCollection.Add = () =>
{
MDataColumn dc = new MDataColumn();
dc.ColumnNameGet = () =>
{
return "string";
};
dc.DataTypeGet = () =>
{
return typeof(string);
};
return dc;
};
return dcCollection;
};
return dt1;
};
return dt;
};
return ds;
};
return target.FetchAllDetails(useCase, strMainFlow, useCaseId, useCaseName);
}
Now I am getting MoledNotImplemented for DataTablecollection error.
How I can moled dataset tabele collection?