Mongo Nested queries - php

I need to query based on key's that all equal the same thing, let's say the following is an order, and that order contains three products. I need to query orders, that have products, that have a specific status all equal to true. Each product is stored, with a mongo id as its key, so I don't actually know it's key name, Example: (obviously I've shortened the keys)
{
"_id" : "foo",
"products": {
"123": {
"status": {
"a": false,
"b": true,
"c": true,
},
},
"213": {
"status": {
"a": true,
"b": true,
"c": true,
},
},
"321": {
"status": {
"a": false,
"b": false,
"c": true,
},
}
},
}
Here's what I've tried:
$this->database->$collection->find(
array('_id' => 'foo', 'products.$.status.c' => true)
);
I'd expect the above, to return the complete order in the example, as the status 'c' inside each product is true, if I were to perform the same query, but with 'a' or 'b' as the status query, it wouldn't return it.
I'm not really sure how to do this, obviously the above didn't work, so my question is, how can I match on multiple sub keys of an array of objects that I do not know the key name?

You want an array of documents, not an object with the id as its key. The $ positional operator doesn't work on objects.
{
"_id" : "foo",
"products": [
{
id: "123"
"status": {
"a": false,
"b": true,
"c": true,
},
},
{
id: "213"
"status": {
"a": true,
"b": true,
"c": true,
},
},
{
id: "321"
"status": {
"a": false,
"b": false,
"c": true,
},
}
]
}
Your query needs to look like this:
$this->database->$collection->find(
array('_id' => 'foo', 'products.status.c' => true)
);
This returns the cursor at the first 'product' that matches your query
and on udpate.
$this->database->$collection->find(
array('_id' => 'foo', 'products.status.c' => true),
array('products.$.hello' => 'world')
);
This updates only that sub document. The only time you should use objects is if you know what the keys will be. Like status, or address... but if key is a variable as well, it gets really tricky, really quickly.

Related

Recursive method to calculate the Boolean value of an array

I'm working on a Laravel/php app and I have this array where I want to collect the final result:
[
{
"nodeType": "or",
"0": {
"nodeType": "and",
"0": {
"nodeType": "and",
"1": true,
"2": false
},
"3": true
},
"2": {
"nodeType": "or",
"4": false,
"5": true
}
}
]
I would like to be able to collect the final value which is either True or False. The array itself could contain any number of children, for example:
[
{
"nodeType": "or",
"0": {
"nodeType": "and",
"0": {
"nodeType": "or",
"0": {
"nodeType": "and",
"1": true,
"2": false
},
"3": true
},
"3": true
},
"2": {
"nodeType": "or",
"4": false,
"5": true
}
}
]
What would be the best way to go around this? I think loops don't work since the depth of the array is not fixed.
Edit 1:
to answer some of the questions in the comments. The keys have no importance. the nodes has always two values. But a value could have its two values as in the second example.
The outer level is an array but this array has only one entry. So it can simply be used as array[0] to get to the json object.
Edit 2:
The result for the following array should be false but the first answer returns true.
{
"nodeType": "and",
"0": {
"nodeType": "and",
"0": {
"nodeType": "and",
"1": true,
"2": false
},
"3": true
},
"2": {
"nodeType": "or",
"4": false,
"5": true
}
}
The nodetype can be either and or or. It is not always as written in the examples above.
You could use a recursive function, like this:
function deduct($expr) {
if (is_bool($expr)) return $expr; // Base case
// If OR, we can stop when we find true (and return true).
// If AND, we can stop when we find false (and return false).
$search = $expr["nodeType"] == "or";
foreach ($expr as $key => $value) {
if ($key !== "nodeType" && deduct($value) === $search) return $search;
}
// If that never happened, return the opposite (false for OR, true for AND)
return !$search;
}
Example call using your second JSON as input:
$json = '[{"nodeType": "or","0": {"nodeType": "and", "0": {"nodeType": "or", "0": {"nodeType": "and","1": true,"2": false},"3": true},"3": true},"2": {"nodeType": "or","4": false,"5": true}}]';
$obj = json_decode($json, true)[0]; // The outer level is an array: unwrap it.
var_dump(deduct($obj));
Output:
bool(true)

Mongodb how to update more than one array documents

Can somebody tell me please how to update more than one element in array?
I have object like:
{
_id: 1,
name: 'x',
someArray: [
{'a': 1},
{'a': 1, 'b': 2},
{'a': 2}
]
}
I would like to update all elements in someArray where 'a' == 1.
I tried to do it via command
db.collection.update(
{_id: 1, 'somaArray.a': 1},
{$set: {'someArray.$.c': 3}},
{multi: true}
)
but this command updated only one element in someArray. The second one is not updated. Result seems like:
{
_id: 1,
name: 'x',
someArray: [
{'a': 1, 'c': 3},
{'a': 1, 'b': 2},
{'a': 2}
]
}
How to achieve update of all elements which match the condition?
Thank you.
Try as below: (Read)
db.collection.update({_id:1},
{
$set: {
'someArray.$[elem].c': 3
}
},
{ arrayFilters: [{ "elem.a": 1 }], multi: true, "upsert": true }
)
Result response will be as below:
{
"_id" : 1,
"name" : "x",
"someArray" : [
{
"a" : 1,
"c" : 3
},
{
"a" : 1,
"b" : 2,
"c" : 3
},
{
"a" : 2
}
]
}
i think this question is similar to yours
visit (How to Update Multiple Array Elements in mongodb)! and check it out

Updating a MongoDB array subelement field

I can't seem to figure out how to update a single element within a subarray. I'd like to update images > 59db1c3654819952005897 > sort to be 5
"_id" : 34,
"images": [
{
"59db1c3654819952005897": {
"name": "1024x1024.png",
"size": "19421",
"sort": 2
}
},
{
"59db1c3652cda581935479": {
"name": "200x200.png",
"size": "52100",
"sort": 3
}
}
]
Here's what I've tried but neither work:
updateOne(['_id' => 34], ['$set' => ["images.59db1c3654819952005897.sort" => 5]])
updateOne(['_id' => 34], ['$set' => ["images.$.59db1c3654819952005897.sort" => 5]])
When using the positional $ operator and the dot notation to update the embedded documents field, you need to include the array in the query otherwise it won't work. In the above case, the revised update operation would be
db.collection.updateOne(
{
"_id": 34,
"images.59db1c3654819952005897": { "$exists": true } // <-- include array in query
},
{
"$set": {
"images.$.59db1c3654819952005897.sort": 5
}
}
)

Mongodb nested document where clause returns many subdocuments

I have a mongodb document that looks similar to this:
{
"id": 1,
"title": "This is the title",
"body" : "This is the body",
"comments": [
{
"email_address": "mirko.benedetti#somemail.com",
"name": "Mirko",
"surname": "Benedetti",
"language": "it",
"text": "This is a message",
"published": "Y",
"on": "2014-03-22 15:04:04"
},
{
"email_address": "marc.surname#somemail.com",
"name": "Marc",
"surname": "Surname",
"language": "it",
"text": "Another Message",
"published": "N",
"on": "2014-03-23 15:04:05"
}
]
}
And I have a query like this:
$this->db->collection->find(array('id' => $id, 'language' => $lang, 'comments.published' => 'Y'),
array('comments.name' => 1, 'comments.surname' => 1, 'comments.text' => 1, 'comments.on' => 1, '_id' => 0));
My problem is that running that query, mongodb returns both comments, which I don't want, I want only the message with "published": "Y".
I tried for example to run 'comments.published' => 'something' and none comment is selected, which is correct, but if at least one of the comments has
the flag "published" set to 'Y', both comments are showed.
Any help will be welcome.
Look at $elemMatch documentation
db.schools.find( { zipcode: "63109" },
{ students: { $elemMatch: { school: 102 } } } )
You need to be careful while using the elemMatch operator. First thing it has two variants. $elemMatch(projection) & $elemMatch(query)
The elemMatch(projection) variant appears to working because the filter criteria you have only matches to one value in comments array.
The below query will work fine.
find({'_id' : ObjectId("582f2abf9b549b5a765ab380"), comments: { $elemMatch: { language: "it", published : "Y" }}})
Now consider when you have more than 1 matching values (two values with 'Y' published status) in comments arrays, then the above query will not work and will only return the first matching value.
In this scenario, you will need to use $filter, which will filter the comments array based on the filter crtieria passed.
aggregate([{
$match: {
'_id': ObjectId("582f2abf9b549b5a765ab380")
}
}, {
"$project": {
"comments": {
"$filter": {
"input": "$comments",
"as": "result",
"cond": {
$and: [{
$eq: ["$$result.language", "it"]
}, {
$eq: ["$$result.published", "Y"]
}]
}
}
}
}
}, {
$project: {
"comments": {
name: 1,
surname: 1,
text: 1,
on: 1
}
}
}])

Pass Json data in ajax request

I want to send whole json array on a link so i have to send whole json array on right move ? because in it's format they give us a json_array format.
Below is the json array of rightmove,
Json Array ,
{
"network":{
"network_id": 5
},
"branch":{
"branch_id": 1566,
"channel": 1,
"overseas": false
},
"property":{
"agent_ref": "02072013_0406",
"published": true,
"property_type": 2,
"status": 1,
"new_home": false,
"student_property": false,
"create_date": "02-07-2013 00:00:00",
"update_date": "02-07-2013 00:00:00",
"date_available": "02-07-2013 00:00:00",
"contract_months": 12,
"minimum_term": 12,
"let_type": 1,
"address":{
"house_name_number": "33",
"address_2": "Rightmove",
"address_3": "4th Floor",
"address_4": "Soho Square",
"town": "London",
"postcode_1": "W1D",
"postcode_2": "3QU",
"display_address": "Soho Square",
"latitude": 51.514899,
"longitude": -0.132587,
"pov_latitude": 51.51482,
"pov_longitude": -0.13249,
"pov_pitch": -16.78,
"pov_heading": 235.75,
"pov_zoom": 0
},
"price_information":{
"price": 1500,
"price_qualifier": 0,
"deposit": 1000,
"administration_fee": "100",
"rent_frequency": 1,
"tenure_type": 1,
"auction": false,
"tenure_unexpired_years": 999,
"price_per_unit_area": 10
},
"details":{
"summary": "Rightmove Test Property",
"description": "Testing full property schema with all the fields in the call for JSON",
"features": [
"Has own Drive",
"Garage included",
"Double Glazed"
],
"bedrooms": 2,
"bathrooms": 2,
"reception_rooms": 1,
"parking": [13],
"outside_space": [29],
"year_built": 999,
"internal_area": 100,
"internal_area_unit": 1,
"land_area": 100,
"land_area_unit": 1,
"floors": 5,
"entrance_floor": 1,
"condition": 1,
"accessibility": [42],
"heating": [1],
"furnished_type": 0,
"pets_allowed": true,
"smokers_considered": true,
"housing_benefit_considered": true,
"sharers_considered": true,
"burglar_alarm": true,
"washing_machine": true,
"dishwasher": true,
"all_bills_inc": true,
"water_bill_inc": true,
"gas_bill_inc": true,
"electricity_bill_inc": true,
"tv_licence_inc": true,
"sat_cable_tv_bill_inc": true,
"internet_bill_inc": true,
"business_for_sale": true,
"comm_use_class":[1,4],
"rooms": [ {
"room_name": "room1",
"room_description": "room1" ,
"room_length": 10.10,
"room_width": 20.20,
"room_dimension_unit": 5,
"room_photo_urls": ["http://www.rightmove.com/image1.JPG"]
} ]
},
"media": [ {
"media_type":1,
"media_url":"www.rightmove.com/image1.JPG",
"caption":"This is an image",
"sort_order":1,
"media_update_date": "02-07-2013 12:12:12"
} ],
"principal": {
"principal_email_address": "principal#rightmove.co.uk",
"auto_email_when_live": true,
"auto_email_updates": true
}
}
}
I got every thing each and every parameter of it.
Now, rightmove api use post method and url is,
https://adfapi.rightmove.co.uk/v1/property/sendpropertydetails
So i'm trying to send json data to rightmove but it's showing me error. I have stored whole json Array in to $json_array.
jQuery.ajax({
url: 'https://adfapi.rightmove.co.uk/v1/property/sendpropertydetails/',
type : "POST",
crossDomain: true,
dataType: "jsonp",
data: {data : <?php echo $json_array; ?>},
success: function(data) {
alert(data);
}
});
});
I have also added in header but it shows me 403 error.
EDIT with error : it showing failure in response but in localhost i got response in local file so i think ajax call is working properly.
The problem is that the "print_r" funktion isnt creating a JSON-Objekt
As explained on php.net
<?php
$a = array ('a' => 'apple', 'b' => 'banana', 'c' => array ('x','y', 'z'));
print_r ($a);
?>
Would create
Array
(
[a] => apple
[b] => banana
[c] => Array
(
[0] => x
[1] => y
[2] => z
)
)
The solution is to use json_encode instead of print_r
so you only have to change
data : {data :<?php print_r($json_array); ?> }
to
data : {data :<?php json_encode($json_array); ?> },
I am assuming your javascript is generated each time in your PHP script as you dynamically build the webpage!
If your $json_array variable is already a JSON String, like the JOSNString you show above this is wrong
data : {data :<?php print_r($json_array); ?> },
and should be
data : {data :<?php echo $json_array; ?> },
If the data is held in a php array i.e. not already a JSONString then you should do
data : {data :<?php json_encode($json_array); ?> },

Categories