I want to use aggregation to get this array only with those tickets, which have start field after 2015-06-16. Can someone help me with the pipeline?
{
"name" : "array",
"tickets" : [
{
"id" : 1,
"sort" : true,
"start" : ISODate("2015-06-15T22:00:00.000Z")
},
{
"id" : 2,
"sort" : true,
"start" : ISODate("2015-06-16T22:00:00.000Z")
},
{
"id" : 3,
"sort" : true,
"start" : ISODate("2015-06-17T22:00:00.000Z")
}
]
}
It's true that the "standard projection" operations available to MongoDB methods such as .find() will only return at most a "single matching element" from the array to that is queried by either the positional $ operator form in the "query" portion or the $elemMatch in the "projection" portion.
In order to do this sort of "ranged" operation, you need the aggregation framework which has greater "manipulation" and "filtering" capabilities on arrays:
collection.aggregate(
array(
# First match the "document" to reduce the pipeline
array(
'$match' => array(
array(
'tickets.start' => array(
'$gte' => new MongoDate(strtotime('2015-06-16 00:00:00'))
)
)
)
),
# Then unwind the array
array( '$unwind' => '$tickets' ),
# Match again on the "unwound" elements to filter
array(
'$match' => array(
array(
'tickets.start' => array(
'$gte' => new MongoDate(strtotime('2015-06-16 00:00:00'))
)
)
)
),
# Group back to original structure per document
array(
'$group' => array(
'_id' => '$_id',
'name' => array( '$first' => '$name' ),
'tickets' => array(
'$push' => '$tickets'
)
)
)
)
)
Or you can possibly use the $redact operator to simplify with MongoDB 2.6 or greater which basically uses the $cond operator syntax as it's input:
collection.aggregate(
array(
# First match the "document" to reduce the pipeline
array(
'$match' => array(
array(
'tickets.start' => array(
'$gte' => new MongoDate(strtotime('2015-06-16 00:00:00'))
)
)
)
),
# Redact entries from the array
array(
'$redact' => array(
'if' => array(
'$gte' => array(
array( '$ifNull' => array(
'$start',
new MongoDate(strtotime('2015-06-16 00:00:00'))
)),
new MongoDate(strtotime('2015-06-16 00:00:00:00'))
)
),
'then' => '$$DESCEND',
'else' => '$$PRUNE'
)
)
)
)
So both examples do the "same thing" in "filtering" the elements from the array that "do not" match the conditions specified and return "more than one" element, which is something basic projection cannot do.
You should use Aggregation to get output.
You should use following query:
db.collection.aggregate({
$match: {
name: "array"
}
}, {
$unwind: "$tickets"
}, {
$match: {
"tickets.start": {
$gt: ISODate("2015-06-16")
}
}
}, {
$group: {
"_id": "name",
"tickets": {
$push: "$tickets"
}
}
})
Related
I have been using Elasticsearch 7.6 and PHP client API for all the operations.
I have created elasticsearch index settings and mappings as follows
$params = [
'index' => 'elasticindex',
'body' => [
'settings' => [
"number_of_shards" => 1,
"number_of_replicas" => 0,
"index.queries.cache.enabled" => false,
"index.soft_deletes.enabled" => false,
"index.requests.cache.enable" => false,
"index.refresh_interval" => -1
],
'mappings' => [
'_source' => [
"enabled" => false
],
'properties' => [
"text" => [
"type" => "text",
"index_options" => "docs"
]
]
]
]
];
I was able to index document using the following code
$params = array();
$params['index'] = 'elasticindex';
for($i = 1; $i <=2; $i++) {
$params['id'] = $i;
$params['body']['text'] = 'apple';
$responses = $client->index($params);
}
But when I use the following search query
$params = [
'index' => 'elasticindex',
'body' => [
'query' => [
'match' => [
"text" => "apple"
]
]
]
];
$results = $client->search($params);
I am getting empty results as follows
Array
(
[took] => 3
[timed_out] =>
[_shards] => Array
(
[total] => 1
[successful] => 1
[skipped] => 0
[failed] => 0
)
[hits] => Array
(
[total] => Array
(
[value] => 0
[relation] => eq
)
[max_score] =>
[hits] => Array
(
)
)
)
Without creating a static index template, if I try to index, elasticsearch dynamic mapping works well and I am getting the results.
The goal is that I want the elasticsearch to index only document id in its inverted index and not position or offset and I want to retrieve only matching document ids as results. Help is much appreciated. Thanks in advance!
Since the document is being returned by the get handler and not the query handler, your index is not being refreshed properly after indexing the document.
As you noted yourself, in your configuration you set:
"index.refresh_interval" => -1
.. which means that the index is not being refreshed automagically. There's seldom a need to change the refresh interval, except in very high throughput situations or where a particular behavior is wanted.
Try to index doc something like this.
$client = Elasticsearch\ClientBuilder::create()
->setHosts($hosts)
->build();
$params = [
'index' => 'elasticindex',
'type' => 'documents',
'id' => '1',
'body' => ['text' => 'apple']
];
$response = $client->index($params);
print_r($response);
Note: _id you can define dynamically if you want, else id will automatically set by elastic.
And try to get doc via a search query.
{
"query": {
"bool": {
"must": {
"term": {
"text": "apple"
}
}
}
}
}
If you want full-text search on this key, set this key property as
"properties": {
"name": {
"type": "text"
}
}
i have collection like this
{
"wl_total" : 380,
"player_id" : 1241,
"username" : "Robin",
"hand_id" : 292656,
"time" : 1429871584
}
{
"wl_total" : -400,
"player_id" : 1243,
"username" : "a",
"hand_id" : 292656,
"time" : 1429871584
}
as both collection have same hand_id i want to aggregate both these collection on the basis of hand_id
i want result as combine of
data=array(
'hand_id'=>292656,
'wl_total'=>
{
0=>380,
1=>-400
},
'username'=>
{
0=>"Robin",
1=>"a"
},
"time"=>1429871584
)
You basically want a $group by the "hand_id" common to all players, and then $push to different arrays in the document and then also do something with "time", I took $max. Nees to be an accumulator of some sort at any rate.
Also not sure what your underlying collection name is, but you can call this in laravel with a construct like this:
$result = DB::collection('collection_name')->raw(function($collection)
{
return $collection->aggregate(array(
array(
'$group' => array(
'_id' => '$hand_id',
'wl_total' => array(
'$push' => '$wl_total'
),
'username' => array(
'$push' => '$username'
),
'time' => array(
'$max' => '$time'
)
)
)
));
});
Which returns output ( shown in json ) like this:
{
"_id" : 292656,
"wl_total" : [
380,
-400
],
"username" : [
"Robin",
"a"
],
"time" : 1429871584
}
Personally I would have gone for a single array with all the infomation in it for the grouped "hand", but I supose you have your reasons why you want it this way.
Continuing on my project, I need to translate some SQL statements to mongoDB
My SQL Statement is:
Delete from 'table' where proc_id = $xxx and (day_id < $day OR day_id > $anotherDay)
Now my condition array is this:
$condicion = array(
'proc_id' => $xxx,
'$or' => array(
'day_id' => array(
'$lt' => $day,
'$gt' => $anotherDay
)
)
);
The function made for delete in mongo collections returns cannot delete...
Some help please?
Each "day_id" would be in it's own $or argument:
$query = array(
'proc_id' = > $xxx,
'$or' => array(
array( 'day_id' => array ( '$lt' => $day ) ),
array( 'day_id' => array ( '$gt' => $anotherDay ) ),
)
)
That is how $or conditions work as a "list" of possible expressions.
The JSON syntax is clearer to visualise:
{
"proc_id": $xxx,
"$or": [
{ "day_id": { "$lt": $day } },
{ "day_id": { "$gt": $anotherDay }}
]
}
Since there is a very clear distinction between a "list" and an "object" definition. $or conditions are "lists" of "objects", and that means you list the full condition just as if it were a query in itself. Since this is not called within an $elemMatch.
And of course the "DELETE" part is the .remove() method:
$collection->remove($query)
There are general examples and resources in the core documentation SQL to MongoDB Mapping Chart, where if the examples there do not immediately help, the linked articles and presentations should.
I have "_id" and "ProductLot". How can I Update Qty "0" to "1 " in the Lot array if ProductLot if present or append a new Lot element if it is not present?
"_id" : ObjectId("5462e44c599e5c6c1300000a"),
"LocationName" : "Putaway",
"Owner" : "",
"Status" : "1",
"Scrap" : "0",
"Lot" : [{
"Qty" :"6",
"ProductLot" : ObjectId("5462dbd9599e5c200e000000"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"Movement" :[ {"OriginId" : "266",Qty:2,Type:"DN"},
{"OriginId" : "267" , Qty:1 , Type:"DN"},
{"OriginId" : "2" , Qty:3 , Type:"IM"},
]
},
{
"Qty" :"0",
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"Movement" :[ {"OriginId" : "266",Qty:2,Type:"DN"},
{"OriginId" : "267" , Qty:1 , Type:"DN"},
{"OriginId" : "2" , Qty:-3 , Type:"IM"},
]
}]
}
EG: I have "ProductLot" : ObjectId("5462dbd9599e5c200e000000") present in array so it should update qty 0 to 1; however, "ProductLot" : ObjectId("5462dbd9599e5c200e00000a") is not available in array so that should append a new element to the array.
PHP code, which is not updating creating appending array at every time:
$inventoryId = new \MongoId($_POST["inventoryid"]);
$productLotId = new \MongoId($invlotid);
$originId = $_POST['id'];
//$lotcontent = [ /* whatever this looks like */ ];
$lotcontent = array(
'Qty' => $invqty,
'ProductLot' => new \MongoId($invlotid),
'Product'=>$invproduct,
'Movement'=> array(
array(
'OriginId' => $_POST['id'],
'Qty' => $invqty,
'Type'=> 'DN',
), )
);
$invcolname = 'Inventory';
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory without the specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
'$not' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
//'OriginId' => $originId,
),
),
),
),
// Append a new element to the Lot array field
array('$push' => array( 'Lot' => $lotcontent ))
);
$movementcontent = array(
'OriginId' => $_POST['id'],
'Qty' => $invqty,
'Type'=> 'DN',
);
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory without the specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
//'$not' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
'Movement'=>array(
'$not' => array(
'$elemMatch' => array(
'OriginId' => $originId,
)
)
)
),
// ),
),
),
// Append a new element to the Lot.Movement array field
array('$push' => array( 'Lot.$.Movement' => $movementcontent ))
);
$result = $mongo->$dbname->$invcolname->update(
// Match an inventory with a specific ProductLot/OriginId element
array(
'_id' => $inventoryId,
'Lot' => array(
'$elemMatch' => array(
'ProductLot' => $productLotId,
//'OriginId' => $originId,
'Movement'=>array(
'$elemMatch' => array(
'OriginId' => $originId,
)
)
),
),
),
// Update the "Qty" field of the first array element matched (if any)
//array( '$set' => array( 'Lot.$.Qty' => 'Updated' )),
array( '$set' => array( 'Lot.$.Movement.$.Qty' => $invqty )),
array('upsert' => true));
Please anyone help me to resolve this?
Using $addToSet is problematic in this case, because the following lot elements would be considered different:
{
"Qty" :0,
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"OriginId" : "266"
}
{
"Qty" :5,
"ProductLot" : ObjectId("5462dbd9599e5c200e000003"),
"Product" : ObjectId("543ca7be4cf59d400c000004"),
"OriginId" : "266"
}
The first element is likely what you would be adding (either with quantity 0 or 1), and the second element would be the same logical lot element, just with an incremented quantity. If the latter element already existed in the array, I imagine you'd like for your application to increment the quantity from 5 to 6 instead of adding the first element, which is essentially a duplicate.
We definitely need two updates here, but I would propose the following:
// Let's assume the following identifiers...
$inventoryId = new MongoId($_POST['inventoryid']);
$productLotId = new MongoId($invlotid);
$originId = new MongoId($_POST['id']);
$lotcontent = [ /* whatever this looks like */ ];
$result = $collection->update(
// Match an inventory without the specific ProductLot/OriginId element
[
'_id' => $inventoryId,
'Lot' => [
'$not' => [
'$elemMatch' => [
'ProductLot' => $productLotId,
'OriginId' => $originId,
],
],
],
],
// Append a new element to the Lot array field
[ '$push' => [ 'Lot' => $lotcontent ] ]
);
MongoCollection::update() will return a result document with an n field indicating the number of affected documents. Since we aren't using the multiple option and are also matching at most one document by _id, we can expect n to be either 0 or 1. If n was 0, we either couldn't find an inventory document with that _id or we found one but it already had a Lot element with the product and origin identifiers (i.e. our $elemMatch criteria matched something, invalidating our negation). If n was 1, that means we found the inventory document, it did not contain a matching Lot element, and we appended it (i.e. our job is done).
Assuming n was 0, we should issue another update and attempt to increment the quantity:
$result = $collection->update(
// Match an inventory with a specific ProductLot/OriginId element
[
'_id' => $inventoryId,
'Lot' => [
'$elemMatch' => [
'ProductLot' => $productLotId,
'OriginId' => $originId,
],
],
],
// Update the "Qty" field of the first array element matched (if any)
[ '$inc' => [ 'Lot.$.Qty' => 1 ] ]
);
Here, I'm using the $ positional update operator to access a specific array element that was matched in the criteria. This allows us to craft an $inc without worrying about the index of the matched element.
Again, we can check $result['n'] here. If it's still 0, then we can assume that no document matches our _id (a completely separate error). But if n is 1 at this point, we successfully incremented the quantity and our job is done.
So my code here:
$featurecollection = ("FeatureCollection");
$test[] = array (
"type" => $featurecollection,
$features[] = array($images)
);
file_put_contents($cache,json_encode($test));
results in the following json:
[
{
"type":"feature",
"0":[
[
{
"title":"some title",
"src":"value",
"lat":"value",
"lon":"value"
},
{
"title":"some title",
...
But I need to nest things differently and I'm perplexed on how the php array should be constructed in order to get a result like:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"coordinates":[
-94.34885,
39.35757
],
"type":"Point"
},
"properties":{
"latitude":39.35757,
"title":"Kearney",
"id":919,
"description":"I REALLY need new #converse, lol. I've had these for three years. So #destroyed ! :( Oh well. Can't wait to get a new pair and put my #rainbow laces through. #gay #gaypride #bi #proud #pride #colors #shoes #allstar #supporting ",
"longitude":-94.34885,
"user":"trena1echo5",
"image":"http://images.instagram.com/media/2011/09/09/ddeb9bb508c94f2b8ff848a2d2cd3ece_7.jpg",
"instagram_id":211443415
}
},
What would the php array look like for that? I'm thrown off by the way everything is nested but still has a key value.
Here's how I'd represent that in PHP:
array(
'type' => 'FeatureCollection',
'features' => array(
array(
'type' => 'Feature',
'geometry' => array(
'coordinates' => array(-94.34885, 39.35757),
'type' => 'Point'
), // geometry
'properties' => array(
// latitude, longitude, id etc.
) // properties
), // end of first feature
array( ... ), // etc.
) // features
)
So to get that structure, each feature has to be an associative array of:
type,
geometry - an associative array of:
coordinates - an indexed array of values,
type
properties - an associative array of values like latitude, longitude, id etc.
It's times like these when I prefer languages that distinguish between lists (array(1, 2, 3)) and dictionaries or maps (array('a' => 1, 'b' => 2)).
With PHP 5.4 and above:
$array = [
'type' => 'FeatureCollection',
'features' => [
[
'type' => 'Feature',
'geometry' => [
'coordinates' => [-94.34885, 39.35757],
'type' => 'Point'
], // geometry
'properties' => [
// latitude, longitude, id etc.
] // properties
], // end of first feature
[] // another feature, and so on
] // end of features
];
For the PHP script below:
<?php
header('Content-type=> application/json');
echo json_encode($array);
This is the JSON output;
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"coordinates": [
-94.34885,
39.35757
],
"type": "Point"
},
"properties": []
},
[]
]
}