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
Related
For those who don't want to read the whole question:
I'm looking for the index in the API-Request (Zammad) to set a tag while creating a ticket.
Details:
I'm using PHP to make an API-Request to my server where Zammad is installed. The following shows the data i sent via curl:
json_encode([
"title" => $title,
"group_id" => 2,
"priority_id" => 2,
"category" => 'Some Category',
"state_id" => 1,
"type" => "Some Type",
"customer" => $userID,
"article" => [
"body" => $htmlBody,
"type" => "note",
"content_type" => "text/html",
],
"tag_list" => [ // <-- The question is about this segment
"The tag i want to add",
],
]);
After converting the data to JSON, im sending it via POST to http://<myServerIP>/api/v1/tickets
What I've tried so far:
I tried guessing the index of the tag at which i failed.
The first full example is shown above.
Secondly:
...
"tag_id" => 9, // I've check its the actual ID of the tag i want to add
And Finally:
...
"tag" => "The tag i want to add",
Needless to say that i didn't succeed. Sometimes i get an error id (im assuming its because the index doesn't exist [who would have thought that? :)]), sometimes i get nothing and Zammad just creates the ticket without the tag. What do i mean when i say sometimes? I refer my tries specified above.
What I've also tried:
Searching for some answer on the web. The thing that comes close to what i want is this. But i would rather create the ticket with the tag instead of making another request just to add the tag.
I've looked inside the code, its written in ruby. The index is 'tags' and needs to be sperated by ,.
Basicly:
json_encode([
"title" => $title,
"group_id" => 2,
"priority_id" => 2,
"category" => 'Some Category',
"state_id" => 1,
"type" => "Some Type",
"customer" => $userID,
"article" => [
"body" => $htmlBody,
"type" => "note",
"content_type" => "text/html",
],
"tags" => "tag1,tag2,tag3", // or simply "tags" => "tag1"
]);
It might help someone in the future ...
I am having trouble in returning matched embedded document using sokil PHPMongo ODM library. I am new to ODM concept and following is my document structure of collection Project:
{
"_id" : ObjectId("59f889e46803fa3713454b5d"),
"projectName" : "usecase-updated",
"classes" : [
{
"_id" : ObjectId("59f9d7776803faea30b895dd"),
"className" : "OLA"
},
{
"_id" : ObjectId("59f9d8ad6803fa4012b895df"),
"className" : "HELP"
},
{
"_id" : ObjectId("59f9d9086803fa4112b895de"),
"className" : "DOC"
},
{
"_id" : ObjectId("59f9d9186803fa4212b895de"),
"className" : "INVOC"
}
]
}
So in my query first criteria is to get the Project with a matched ID and 2nd criteria is to to retrieve only the class from the classes array of embedded documents with a specific id.This is how i am building the query:
$collection = $PHPMongoDBInstance->getCollection("Project");
$result = $collection->getDocument(
"59f889e46803fa3713454b5d",
function (\Sokil\Mongo\Cursor $cursor) {
// get embedded documents with matched id
$cursor->whereElemMatch("classes", $cursor->expression()->where("_id", new MongoId("59f9d7776803faea30b895dd")));
}
);
I was expecting it to return only the embedded document of OLA from the usecase-updated document like this:
{
"_id" : ObjectId("59f889e46803fa3713454b5d"),
"projectName" : "usecase-updated",
"classes" : [
{
"_id" : ObjectId("59f9d7776803faea30b895dd"),
"className" : "OLA"
}
]
}
But PHPMongo library is returning the whole Project Document (shown in the start of question) with all the classes. Someone suggested to look into aggregation framework. But problem is there is not good enough documentation on the PHPMongo for using array aggregation functions (like $filter)
I tried it by using native instance of MongoCollection and with that i can use the findOne method to project my final result using this way:
$result = $collection->getMongoCollection("Project")->
findOne(array("_id" => new MongoId("59f889e46803fa3713454b5d")),
array("classes" =>
array('$elemMatch' =>
array("_id" => new MongoId("59f9d7776803faea30b895dd")))));
If i want to achieve the similar projection using the getDocument method of sokil PHPMongo is there some possibility?
UPDATE:
I tried achieving with aggregation framework and following was the query:
$result = $collection->aggregate(array(
array(
'$match' => array(
"_id" => new MongoId("59f889e46803fa3713454b5d")
)
),
array(
'$project' => array(
'classes' => array(
'$filter' => array(
'input' => '$classes',
'as' => 'classItem',
'cond' => array(
'$eq' => array('$$classItem._id' => new MongoId("59f9d7776803faea30b895dd"))
)
)
)
)
)
));
But i get this exception:
Sokil\\Mongo\\Exception\nMessage: Aggregate error: Unrecognized expression '$$classItem._id'\nFile:
So after posting the problem on the PHPMongo GitHub issues i found so far there is no implementation of $elemMatch. Author posted some interesting insights on this issue and now this issue is marked as enhancement for the future.
Furthermore i have tried aggregation framework and i am succesfully able to achieve the desired results i found that there was a mistake in the syntax of following expression (for full query see above in the question):
'$eq' => array('$$classItem._id' => new MongoId("59f9d7776803faea30b895dd"))
I learned that expression of '$eq' shouldn't be a associative array rather each element in the expression should be a separate array item like this:
'$eq' => array('$$class._id', new MongoId("59f9d7776803faea30b895dd"))
So now my final aggregation query was like this:
$collection = $PHPMongoDBInstance->getCollection("Project");
$result = $collection->aggregate(array(
array(
'$match' => array(
"_id" => new MongoId('59f889e46803fa3713454b5d')
)
),
array(
'$project' => array(
'classes' => array(
'$filter' => array(
'input' => '$classes',
'as' => 'class',
'cond' => array(
'$eq' => array('$$class._id', new MongoId("59f9d7776803faea30b895dd"))
)
)
),
'projectName' => 1
)
)
));
Hope this will help people in future if they had similar problem like me as i tried finding solution and it wasn't there over the internet. I will try to update once there is a feature enhancement in PHPMongo of $elemMatch.
I am using jenssegers/laravel-mongodb library in a laravel application however I need to show counts of an embedded document. Using a generic example of comment/posts, while I can solve my problem by just pulling all the posts and looping through to get comments to count them but was just was not sure if I could query them.
I did set up my relationships. In my post class I did:
public function comments()
{
return $this->hasMany('App\Comment');
}
and in my comment class:
public function post()
{
return $this->belongsTo('App\Post');
}
Later in code:
$post->comments()->save($comment);
$comment->post()->associate($post);
my document structure:
"posts" : [
{
"_id" : ObjectId("5805a11e2594ee26543ea041"),
"Post_Num" : "166236001010",
"updated_at" : ISODate("2016-10-18T04:12:14.454Z"),
"created_at" : ISODate("2016-10-18T04:12:14.451Z"),
"comments" : [
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
},
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
}
]
},
{
"_id" : ObjectId("5805a11e2594ee26543ea041"),
"Post_Num" : "166236001010",
"comments" : [
{
"Comment_Num" : "3333333",
"_id" : ObjectId("5805a11e2594ee26543ea042"),
"post_id" : "5805a11e2594ee26543ea041",
}
]
}
]
Now when I try getting the comments like:
$post->comments()->count()
or
$post->comments()->get()->count()
or
$post->comments->get()->count()
I get a 0. The same logic works if it is not an embedded document but just was wondering if it was possible to do an aggregate query ? Perhaps is best to just let the code iterate and add everything?
As you can tell I need some minor hand holding. Thank You
UPDATE: I am trying the following
public function commentCount()
{
$commentsCount = Post::raw(function($collection)
{
return $collection->aggregate(['$project' => ['_id' => 1,
'comments_count' => ['$size' => '$comments']],
['$group' => ['_id' => null, 'count' => ['$sum' => '$comments_count']]]]);
});
return $commentsCount;
}
What I get now is:
$pipeline is not a list (unexpected index: "$project")
Just to be clear, you want a list of your posts with the number of comments on each post?
Aggregation has something to offer for that:
https://docs.mongodb.com/manual/reference/operator/aggregation/size/#exp._S_size
I'm not a php dev but this is my shot at it:
Post::raw()->aggregate(
['$project' => ['_id' => 1,
'Post_Num' => 1,
'comments_count' => ['$size' => '$comments']],
['$group' => ['_id' => null, 'count' => ['$sum' => '$comments_count']]]
]);
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'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) :-)