MongoDB using only map without reduce in PHP - php

In mongoDB i have two collection users and posts following this structure:
Posts
{
_id: ObjectId(""),
subject: "some post",
content: "here is the content",
user_id: "4351"
}
Users
{
user_id: "4351",
name: "John Marks",
picURL: "http://...",
aboutme: "historian of the future"
}
needing to get the posts in array with name.
db.posts.find().map(function(newPost){
newPost.name = db.users.findOne({user_id: newPost.user_id}).name;
return (newPost);
})
I wrote this code and it's work in mongoshell well returning this result:
{
_id: ObjectId(""),
subject: "some post",
content: "here is the content",
user_id: "4351",
name: "John Marks"
}
but i could not apply in php. You can't just simple get the output of the map function. It requires reduce function and output collection for the returning value.
Edit:
$map = new MongoCode('
function(newPost) {
newPost.username = db.users.findOne({user_id: newPost.user_id}).name;
return newPost;
}
');
post = $app->mongo->command(array(
"mapreduce" => "posts",
"map" => $map,
"reduce" => '',
"out" => array("inline" => 1)
));
var_dump($post);
This code must be work but accessing another collection in map function via 'db' is forbidden after mongo 2.4 release. That's why i changed my approach. Instead of using map/reduce, handled with php. Added posts user_ids to array and get the users information with following code.
$userInf = $app->mongo->selectCollection("users")->find(
array('user_id' => array('$in' => $user_ids)),
array("_id" => 0, "user_id" => 1, "name" => 1, "picURL" => 1)
);

Related

How to get all Objects of an array with value condition

I'm absolutely new to php/laravel world so sorry if my question is simple.
Language: php with laravel.
What : I want to get all objects who contain the name of the users.
//Exemple of my users Array
($users = ["name1", "name2","name4"])
//Forms is an array who contain multiple object, each object have a reference to a user Name.
$Forms = [{_id: 1, title : "title1", userName : "name1" }, {_id: 2, title : "title2", userName : "name2" }, {_id: 3, title : "title3", userName : "name3" }, {_id: 4, title : "title4", userName : "name4" },{_id: 5, title : "title5", userName : "name1" }]
//here i want to get form with name1, name2 and name4
foreach ($users as $user) {
$allForm = Forms::where('userName ', $user)->get();
};
Problematic: I only received 2 objects (objects from the first user of my array).
Exemple: here i want to get every forms who contain "name1", "name2","name4" but i will received only every forms with "name1".
This should work as per your requirements:
$users = ["name1", "name2","name4"];
$Forms = [
[
'id' => 1,
'title' => 'title1',
'userName' => 'name1'
]
];
$collection = collect($Forms);
$results = $collection->whereIn('userName', $users)->toArray();
dd($results);
use toArray() function to convert the collection
Try this
$users = ["name1", "name2","name4"];
$forms = Form::whereIn('userName', $users)->get();

MongoDB (PHP) - MapReduce - Connect Documents in different collections with the same ID

I have a collection named requests with documents following this prototype:
{
"_id": {
"$id": "56295368ef2b8e5458000029"
},
"title": "Hardcoded Name",
"requestorId": "dve34gegrgeefdsfewe"
}
The requestorId of those documents correlates with the id (not the _id Mongo Object ID) of the users collection, whose documents follow this prototype:
{
"_id": {
"$id": "86235288ef2gif5458000027"
},
"id": "dve34gegrgeefdsfewe",
"name": "John Doe"
}
I need to be able to run a find() on the requests collection and obtain the name associated with the requestorId in each request document. I would normally just do the obvious: run a find() on requests, then while looping through each document, do a subsequent query on users where id = [requestorId (really "id" in users collection)], BUT I need to be able to sort the entire requests collection by the name associated with each requestorId, then use skip() & limit(). This should be pretty simple but I can't seem to figure it out... Here's my code so far, inspired by https://www.noppanit.com/merge-documents-two-collections-together-mongodb/
$m = new MongoClient(); // connect
$db = $m->{"database"};
// construct map and reduce functions
$eventRequest_map = new MongoCode("function() {
emit(this.requestorId, {\"requestorId\" : this.requestorId})
}");
$user_map = new MongoCode("function() {
emit(this.id, {\"name\" : this.name})
}");
$reductionFunction = new MongoCode("function(key, values) {
var result = {
\"requestorId\" : \"\",
\"name\" : \"\"
};
return result;
}");
$sales = $db->command(array(
'mapreduce' => "requests",
'map' => $eventRequest_map,
'reduce' => $reductionFunction,
'query' => array(),
'out' => 'joined'));
$sales = $db->command(array(
'mapreduce' => "users",
'map' => $user_map,
'reduce' => $reductionFunction,
'query' => array(),
'out' => 'joined'));
$collection = $db->joined;
$cursor = $collection->find();
$outputJoined = array();
foreach($cursor as $doc){
$outputJoined[] = $doc;
}
echo json_encode($outputJoined);

javascript / jquery Accessing JSON array by both name and index

I am very much hoping you can help me with this as I've spent all too much time on this. First, my JSON formatting is unfortunately not very mutable and I have moved it to a number of different formats to support both some jquery and a php-based search. Each time I move it, the search will work and the rest of the site will break or vice-versa.
Is it possible to access a JSON array by both name and index number? Here is my JSON (stored in PHP file and being retrieved & converted successfully to valid JSON):
<?php
$contents = array(
'Song Name #1 by Artist Name #1 (maininfo)' => array(
'contentid' => '1',
'aname' => 'Artist Name',
'sname' => 'Song Name',
'main' => 'core content #1',
'maininfo' => 'url')
),
'Song Name #2 by Artist Name #2 (maininfo)' => array(
'contentid' => '2',
'aname' => 'Artist Name',
'sname' => 'Song Name',
'main' => 'core content #2',
'maininfo' => 'url')
);
?>
My search works when something in the array title is matched on, otherwise it returns no matches so I must leave the array title as-is.
Another part of my project uses jquery and has the following:
parse(jsonobj[0][1]['sname']) //successfully already returning 'Song Name'
The above will ONLY work when the array title is not provided (e.g. 'Song Name #1 by Artist Name #1 (maininfo)' => array( becomes simply array(.
For those curious, file is being converted to JSON using:
var jsonobj;
$.ajax({
url: 'getjson.php',
dataType: "json",
success: function (doc) {
jsonobj = doc;
}
});
On the PHP side, when getjson.php is called the JSON array (above) is loaded in and converted to valid JSON using:
$final = array($final_contents);
header('Content-type: application/json');
echo json_encode($final);
Note: $final_contents is just $contents with an additional header added. See Searching JSON array for values and accessing surrounding keys/values; output as JSON for the PHP I have running specifically.
Thank you in advance.
JavaScript does not support arrays with named indexes. You should encode it as a JSON object instead.
var $contents = {
"Song Name #1 by Artist Name #1 (maininfo)": {
"contentid": 1,
"aname": "Artist Name",
"sname": "Song Name",
"main": "core content #1",
"maininfo": "url"
},{
"Song Name #2 by Artist Name #2 (maininfo)": {
"contentid": 2,
"aname": "Artist Name",
"sname": "Song Name",
"main": "core content #2",
"maininfo": "url"
}
};
Although it would probably be better to arrange it this way (here's a fiddle to demonstrate:
var songs = [
{
"contentid": 1,
"artist": "Artist Name",
"title": "Song Title 1",
"main": "core content #1",
"maininfo": "url"
},
{
"contentid": 2,
"artist": "Artist Name",
"title": "Song Title 2",
"main": "core content #2",
"maininfo": "url"
}
];
Then you can search through your songs list by id, or iterate through to filter on specific field values. For instance to find all songs whose titles start with "Song Title":
var findAllSongs = function(prop, value){
var result = new Array();
for (var i = 0; i < songs.length; i++) {
var song = songs[i];
if (song[prop] && (song[prop] === value || song[prop].search(value) >= 0)){
result.push(song);
}
}
return result;
};
var song = findAllSongs("title","Song Title 2")[0];
alert(song.contentid);
// Outputs "2"
The php equivalent of my json above is:
$songs = array(
array(
"contentid" => 1,
"artist" => "Artist Name",
"title" => "Song Title 1",
"main" => "core content #1",
"maininfo" => "url",
),
array(
"contentid" => 2,
"artist" => "Artist Name",
"title" => "Song Title 2",
"main" => "core content #2",
"maininfo" => "url",
)
);
If you're using PHP 5.4 or higher, you can use the short syntax:
$songs = [
[
"contentid" => 1,
"artist" => "Artist Name",
"title" => "Song Title 1",
"main" => "core content #1",
"maininfo" => "url",
],[
"contentid" => 2,
"artist" => "Artist Name",
"title" => "Song Title 2",
"main" => "core content #2",
"maininfo" => "url",
]
];
Then you can turn it into JSON by using your current method:
json_encode($songs);
You are having array. convert it to json using following code (json_encode) and echo so that jquery can receive it:
$jsonVar = json_encode($contents);
echo $jsonVar;
Update:
Code to call json using ajax is:
$.ajax({
dataType: "json",
url: url,
data: data,
success: success
});
Alternatively you can use shorthand for it:
$.getJSON( "ajax/test.json", function( data ) {
var items = [];
$.each( data, function( key, val ) {
items.push( "<li id='" + key + "'>" + val + "</li>" );
});
$( "<ul/>", {
"class": "my-new-list",
html: items.join( "" )
}).appendTo( "body" );
});
Of course the file being sent should be in json format, that is:
{
"one": "Singular sensation",
"two": "Beady little eyes",
"three": "Little birds pitch by my doorstep"
}
for that you need to convert the array into json.

laravel mongodb push element to existing array in document_

In my mongodb collection I want to push some elements to an existing array. I use jenssegers/Laravel-MongoDB - Eloquent model and Query builder to work with lavavel and mongodb.
How can I use the $push operator in jenssegers/Laravel-MongoDB?
MongoDB entry which shall be updated (RockMongo representation):
{
"_id": ObjectId("5328bc2627784fdb1a6cd398"),
"comments": {
"0": {
"id": 3,
"score": 8
}
},
"created_at": ISODate("2014-03-18T21:35:34.0Z"),
"file": {
"file_id": NumberLong(1175),
"timestamp": NumberLong(1395178534)
}
}
Hint about array representation in rockmongo and mongo shell
RockMongo and mongo shell array representation of the documents are a little bit different. Have a look at the comments-array. The above RockMongo representation appears in the mongo shell as:
{
"_id" : ObjectId("5328c33a27784f3b096cd39b"),
"comments" : [
{
"id" : 3,
"score" : 8
}
],
"created_at" : ISODate("2014-03-18T22:05:46Z"),
"file" : {
"file_id" : NumberLong(1176),
"timestamp" : NumberLong(1395180346)
}
}
As the documentation states the $push operater to push elements to an array. This works fine in the mongo-shell:
Mongo shell
db.Images.update({'file.file_id': 1175},
{ $push: { comments: { id: 3, score: 8} }
})
But in the query-builder I struggle to incorporate the $push operator. I get the error:
localhost:27017: Modified field name may not start with $
I did not find any documentation or example that showed me how to do it..
My jenssegers/Laravel-MongoDB code, that returns the error
// $file_id = 1175
public static function addComment( $file_id ) {
$image = Images::where( 'file.file_id', '=', floatval( $file_id ) )
->update( array('$push' => array( 'comments' => array( 'id' => 4, 'score' => 9 ) ) ) );
return $image;
}
Assuming everything is okay as it works in the shell then use the provided method to push instead:
Images::where('file.file_id', '=', floatval( $file_id ))
->push('comments', array( 'id' => 4, 'score' => 9 ));

Updating multiple embedded docs in mongoDB

I need to update multiple embedded docs in mongo using PHP. My layout looks like this:
{
_id: id,
visits : {
visitID: 12
categories: [{
catagory_id: 1,
name: somename,
count: 11,
duration: 122
},
{
catagory_id: 1,
name: some other name,
count: 11,
duration: 122
},
{
catagory_id: 2,
name: yet another name,
count: 11,
duration: 122
}]
}
}
The document can have more than one visit too.
Now i want to update 2 categories, one with id=1 and name=somename and the other with id=1 and name=some_new_name. Both of them should inc "count" by 1 and "duration" by 45.
First document exists, but second does not.
Im thinking of having a function like this:
function updateCategory($id, $visitID,$category_name,$category_id) {
$this->profiles->update(
array(
'_id' => $id,
'visits.visitID' => $visitID,
'visits.categories.name' => $category_name,
'visits.categories.id' => $category_id,
),
array(
'$inc' => array(
'visits.categories.$.count' => 1,
'visits.categories.$.duration' =>45,
),
),
array("upsert" => true)
);
}
But with this i need to call the function for each category i will update. Is there any way to do this in one call?
EDIT:
Changed the layout a bit and made "categories" an object instead of array. Then used a combination of "category_id" and "category_name" as property name. Like:
categories: {
1_somename : {
count: 11,
duration: 122
},
1_some other name : {
count: 11,
duration: 122
},
2_yet another name : {
count: 11,
duration: 122
},
}
Then with upsert and something like
$inc: {
"visits.$.categories.1_somename.d": 100,
"visits.$.categories.2_yet another name.c": 1
}
i can update several "objects" at a time..
Mongodb currently not supporting arrays multiple levels deep updating (jira)
So following code will not work:
'$inc' => array(
'visits.categories.$.count' => 1,
'visits.categories.$.duration' => 123,
),
So there is some solutions around this:
1.Load document => update => save (possible concurrency issues)
2.Reorganize your documents structure like this(and update using one positional operator):
{
_id: id,
visits : [{
visitID: 12
}],
categories: [{
catagory_id: 1,
name: somename,
count: 11,
duration: 122,
visitID: 12
}]
}
}
3.Wait for multiple positional operators support (planning in 2.1 version of mongodb).
4.Reorganize your documents structure somehow else, in order to avoid multiple level arrays nesting.

Categories