I'm trying to use Codeigniter with codeigniter-mongodb-library/tree/v2 in order to connect with mongo and Update documents with this structure:
{
"_id" : ObjectId("50fd8bd460e958aa38000002"),
"created_at" : 1358793684,
"updated_at" : 1358793684,
"is_active" : 0,
"memberships" : [
{
}
],
"first_models" : [
[ ]
],
"second_models" : [
[ ]
],
"pages" : [
[ ]
]
}
All I want is Update some document by it's given _id. Particularly I need to add elements inside "first_models" array. From my Codeigniter model I'm calling this:
$result = $this->mongo_db
->push(array('_id' => '50fd8bd460e958aa38000002'),array('first_models' => array('Some text here')))
->update($this->collections['sites']);
I'm getting this error when I try to Update one specific document:
Mod on _id not allowed
Seems there is some conflict with the given _id... I'm missing something and I don't know what :(
I'm using Mongodb 2.2.2 with Codeigniter 2.1.3, PHP5.4 and Apache2.
I was going in wrong direction with that. Finally #Rajan points me to right solution:
$updated = $this->mongo_db->where('_id', new MongoId($id))
->set($data)
->update('mycollection');
You can see, using set instead push, and referencing object id with new MongoId($id) :-)
Related
I'm using this package https://github.com/jenssegers/laravel-mongodb#mongodb-specific-operations to deal with a mongo on the project I'm working on.
I have collection with such a structure (here is a example of one document)
{
"_id" : ObjectId("5fda3a602279e5262a3ddec6"),
"type" : "type",
"tags" : [
"tag1",
"tag2",
],
"filters" : [
{
"key" : "1",
"label" : "some key1"
},
{
"key" : "2",
"label" : "some key2"
}
],
"updated_at" : ISODate("2020-12-16T16:48:32.000+0000"),
"created_at" : ISODate("2020-12-16T16:48:32.000+0000")
},
what I need to do is check if collection has a document with a same type and exact same tags as provided, if yes I need to update that document if not create it.
Here is a code I wrote:
$this->collection->where('type', $type)
->where('tags', 'all', $tags)
->update(
['type' => $type, 'tags' => $tags, 'filters' => $filters,],
['upsert' => true,]
);
here I'm checking if we have a document with a type equal to $type and tags same as $tags, if such document exists I update it (replace all data with a new one) if not it should create a new document, thanks to ['upsert' => true,]
But when I'm running it I'm getting error cannot infer query fields to set, path 'tags' is matched twice
FYI: when I'm removing ->where('tags', 'all', $tags) query is working, not sure if correct, but it's not failing
Also I tried to do it like this: ->where('tags', ['$all' => $tags]) and issue is the same as above
Updated it's happening only when it trying to insert new field, updates work correctly
I am using the Moloquent model with Laravel 5.6. here is my collection record given below:-
{
"_id" : ObjectId("5afe619dbe0b8d0e5876b16b"),
"name" : "Central Park",
"categories" : [
"4d4b7105d754a06376d81259",
"4bf58dd8d48988d116941735",
"4bf58dd8d48988d119941735",
"4d4b7105d754a06376d81259",
"4bf58dd8d48988d116941735",
"4bf58dd8d48988d119941735",
"4d4b7105d754a06374d81259",
"4bf58dd8d48988d16d941735"
],
"loc" : {
"type" : "Point",
"coordinates" : [
88.4166612820784,
22.5835157504658
]
}
}
I am running this query from the Laravel controller.
$users = MongoTest::where('loc', 'near', [
'$geometry' => [
'type' => 'Point',
'coordinates' => [
$longitude,
$latitude,
],
],
'$maxDistance' => 10,
])->get();
print_r($users);
I am getting this error:-
error processing query: ns=tkit.testTree: GEONEAR field=loc maxdist=10 isNearSphere=0
Sort: {}
Proj: {}
planner returned error: unable to find index for $geoNear query
How can I solve this?
Create an index from the mongo shell. A 2sphere index is expected by the query using the $near or $nearSphere operator or geoNear command, or even the $geoNear aggregation pipeline stage.
The createIndex() method will do this for you, so just name your collection within the correct database:
db.collection.createIndex({ "loc": "2dsphere" })
Note that any API actually using the geoNear command really needs to update to one of the other mentioned query operators as this command is "deprecated" from MongoDB 4.0 onwards and will be removed.
Operations looking for "nearest" should use $near or $nearSphere, and those which want to return the "distance" should use the $geoNear pipelie stage instead.
I have created query in mongoDB. In MongoChef this query produces more than 10 thousand records in less than 2 seconds. Now I want to execute this query in PHP.
So i don't know how to write query in php as I read various documents on internet but confused how to implement it.
db.PMS.aggregate(
[
{$project:
{EventTS:1,MainsPower:1,PanelID:1}
},
{$unwind:
{path:"$MainsPower",includeArrayIndex:"arrayIndex",preserveNullAndEmptyArrays:true}
},
{ $match: { "MainsPower":{$ne:null}}},
{ $match: { "EventTS":{$gt:new Date("2016-01-01")}}},
{$project:
{MainsPower:1,
PanelID:1,
timestamp:{"$add":
[{'$subtract' : ["$EventTS",new Date("1970-01-01")]},
{"$multiply":[60000,"$arrayIndex"]}
]}
}
}
]
);
You can use some resources available on the php official documentation. A mapping of sql queries in php to mongoDB queries in php can be found here.
Also I have a demo login and registration script at my github. You can view those in this repo.
If you use MongoDB PHP Library you should be able to do something similar to this:
$mongo = new MongoClient();
$database = $mongo->examples;
$collection = $database->PMS;
$pipeline = [
[
'$project' => [
'EventTS' => 1,
'MainsPower' => 1,
'PanelID' => 1,
]
],
[
'$unwind' => [
'path' => '$MainsPower',
'includeArrayIndex' => 'arrayIndex',
'preserveNullAndEmptyArrays' => true
]
],
...
];
$cursor = $collection->aggregate($pipeline);
I want to be able to store a json object in my ES index. Here's an example of what I'm trying to store (this a serialized model, a request body that is sent to ES):
"{"id":218,"name":"Test2","category_id":1,"address":"Pushkin street","phone":null,"site":null,"location":{"lat":64,"lon":70},"city":"Heaven","description":"Super company","tags":["#test1","#test2"]}"
When I try to store it (via the extension, of course), here's the error that ES returns:
"{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"failed to parse [location]"}],"type":"mapper_parsing_exception","reason":"failed to parse [location]","caused_by":{"type":"illegal_argument_exception","reason":"unknown property [lat]"}},"status":400}"
It seems that I am unable to do so without having a specific type mapping, like in the docs:
https://www.elastic.co/guide/en/elasticsearch/reference/1.4/mapping-object-type.html
However, I don't seem to find a way to provide that mapping inside the model. The extension's documentation doesn't really say anything about it.
So, my question is: do I need it at all, and if I do, how?
Appreciate all feedback.
I'll assume your model is \yii\elasticsearch\ActiveRecord. You'll need to describe its attributes:
public function attributes()
{
return [
'name',
'category_id',
'address',
'phone',
'site',
'location',
'city',
'description',
'tags'
];
}
Don't forget to configure index() and type(). In the following example type is my_address.
Then you'll need to create an index with proper field mapping. Here's what your mapping should look like:
"mappings" : {
"my_address" : {
"properties" : {
"name" : { "type" : "string"},
"category_id" : { "type" : "integer"},
"address" : { "type" : "string"},
"phone" : { "type" : "string"},
"site" : { "type" : "string"},
"location" : { "type" : "geo_point"},
"city" : { "type" : "string"},
"description" : { "type" : "string"},
"tags" : { "type" : "string"}
}
}
}
Note three things:
Location is of type geo_point.
Tags are declared as string. This will also allow them to be arrays of strings.
I didn't include the id field. If it's unique, I suggest you just set your yii model's id to the necessary value ($model->primaryKey = '123'). Otherwise your ES model will have its internal id set to something like AVDXmfJ3Ou7LzqD1DDMj and also have an id field which is not very convenient.
I encourage you to take a closer look at the mappings - they are very important when it comes to configuring how exactly the strings are being analyzed.
UPDATE: You don't really describe the mapping anywhere in your model. Do it in a migration - similar to creating tables in SQL.
In case you using ElasticSearch ActiveRecord , you could define a method for setupMapping
Class BookIndex extends yii\elasticsearch\ActiveRecord
{
/**
* sets up the index for this record
*
*/
public static function setUpMapping()
{
$db = static::getDb();
//in case you are not using elasticsearch ActiveRecord so current class extends database ActiveRecord yii/db/activeRecord
// $db = yii\elasticsearch\ActiveRecord::getDb();
$command = $db->createCommand();
/*
* you can delete the current mapping for fresh mapping but this not recommended and can be dangrous.
*/
// $command->deleteMapping(static::index(), static::type());
$command->setMapping(static::index(), static::type(), [
static::type() => [
// "_id" => ["path" => "id", "store" => "yes"],
"properties" => [
'name' => ["type" => "string"],
'author_name' => ["type" => "string"],
'publisher_name' => ["type" => "string"],
'created_at' => ["type" => "long"],
'updated_at' => ["type" => "long"],
'status' => ["type" => "long"],
],
],
]);
}
}
Later on you just need to call this method any time you want to apply the new mapping.
I have an array object like this:
[
'things'=>[
't1'=>[ 'prop1'=>3 ]
]
]
and I want to merge it into the following collection entry:
[
'id'=>123,
'things'=>[
't1'=>[ 'prop1'=>1, 'prop2'=>2 ],
't2'=>[ 'prop1'=>1, 'prop2'=>2 ]
]
]
If I try to use the object as is, the following code deletes things.t1.prop2 and things.t2:
$db_collection->update([ 'id'=>123 ], [ '$set'=>[ 'things'=>[ 't1'=>[ 'prop1'=>3 ] ] ] ]);
Probably I have to convert the object into the dot notation format like this:
$db_collection->update([ 'id'=>123 ], [ '$set'=>[ 'things.t1.prop1'=>3 ] ]);
I can also read the whole entry first, merge data with a specific function inside my script, and write the updated entry back into the database.
But these ways don't seem to be very efficient. Is there a special command in Mongo to incorporate a raw object into nested data without overwriting the whole entry?
P.S.: I'm using a PHP MongoDB driver.