MongoDB Aggregation: Value in Array - mongodb

We have the following Testsnippet in Ruby
def self.course_overview(course_member=nil)
course_member = CourseMember.last if course_member == nil
group_global = {"$group" =>
{"_id" => { "course_id" => "$course_id",
"title" => "$title",
"place" => "$place",
"description" => "$description",
"choosen_id" => "$choosen_id",
"year" => {"$year" => "$created_at"},
"course_member_ids" => "$course_member_ids"}}
}
match_global = {"$match" => {"_id.course_member_ids" => {"$in" => "#{course_member.id}"} }}
test = CoursePlan.collection.aggregate([group_global, match_global])
return test
end
The problem is the "match_global" statement. We would like to match all Documents where the course_member ID is appearing in the course_member_ids array.
The above statement fails with the error: "...must be an array". This make sense to me but according to other comments on the web this should be possible this way.
Any advice? How is it possible to return the docs where the course_member id is in the array of the course_member ids?
Sample CoursePlan Object:
{
"_id" : ObjectId("5371e70651a53ed5ad000055"),
"course_id" : ObjectId("5371e2e051a53ed5ad000039"),
"course_member_ids" : [
ObjectId("5371e2a751a53ed5ad00002d"),
ObjectId("5371e2b251a53ed5ad000030"),
ObjectId("5371e2bb51a53ed5ad000033")
],
"created_at" : ISODate("2014-05-13T09:33:58.042Z"),
"current_user" : "51b473bf6986aee9c0000002",
"description" : "Schulung 1 / Elektro",
"fill_out" : ISODate("2014-04-30T22:00:00.000Z"),
"place" : "TEST",
"title" : "Schulung 1",
"updated_at" : ISODate("2014-05-13T09:33:58.811Z"),
"user_ids" : [
ObjectId("51b473bf6986aee9c0000002"),
ObjectId("521d7f606986ae4826000002"),
ObjectId("521d8b3f6986aed678000007")
]
}

Since course_member_ids is an array of course members you should test for equality. In shell syntax:
{$match:{"_id.course_member_ids":<valueYouWantToTest>}}
You don't need $in as this query is analogous to a find when you want to select documents that have a particular single value you are looking for.

Related

MongoDB + Laravel + jenssegers/laravel-mongodb + update nested child elements

Hellow folks, I am new to MongoDB and looking for some answer
Is there any way to update nested array without looping it.
foreach ($post->comments as $key => $comment) {
if ($comment['posted_by'] == $authUser['id']) {
$data = $post->update([
"comments.$key.description" => $dataArray['description'],
"comments.$key.updated_at" => $dataArray['updated_at'],
]);
}}
I want to to do something like below.
$post = Post::where('_id', $id)->where('comments.*.id', $commentId)->update(array('description' => $desc));
Or I have to write raw MongoDB query for that.
I have 1 level nested comment also under main comments so if I want to update nested comment than I have to loop comment array than the nested comment array.
if ($subCommentId) {
foreach ($comment as $nestedkey => $nestedComments) {
if ($nestedComments['id'] === $subCommentId && $nestedComments['posted_by'] == $authUser['id']) {
$data = $post->update([
"comments.$key.$nestedkey.description" => $dataArray['description'],
"comments.$key.$nestedkey.updated_at" => $dataArray['updated_at'],
]);
}
}
}
Something like this :
$post = Post::where('_id', $id)->where('comments.*.id', $commentId)->where('comments.*.*.id', $subCommentId)->update(array('description' => $desc));
Is it good to store comment in the same collection as an array or should I create a new collection for that as maximum BSON document size is 16 megabytes and how much comments it can store like 10K or more?
Below is my sample comment array format under one Collection.
"comments" : [
{
"description" : "description some",
"channel" : "swachhata-citizen-android",
"user_role" : "Citizen",
"id" : "5b4dc367d282f",
"user_role_id" : ObjectId("5accd7f8309a203be03b6441"),
"created_at" : "2018-07-17 15:52:31",
"updated_at" : "2018-07-17 15:52:31",
"ip_address" : "127.0.0.1",
"user_agent" : "PostmanRuntime/6.4.1",
"deleted" : false,
"channel_id" : "5acccfe4309a2038347a5c47",
"posted_by" : NumberInt(1),
"comments" : [
{
"description" : "some description nested",
"channel" : "swachhata-citizen-android",
"user_role" : "Citizen",
"id" : "5b4dcfc7022db",
"user_role_id" : ObjectId("5accd7f8309a203be03b6441"),
"created_at" : "2018-07-17 16:45:19",
"updated_at" : "2018-07-17 16:45:19",
"ip_address" : "127.0.0.1",
"user_agent" : "PostmanRuntime/6.4.1",
"deleted" : false,
"channel_id" : "5acccfe4309a2038347a5c47",
"posted_by" : NumberInt(1)
}
]
}
]
Thanks. :)
To update nested document, you should use arrayFilters:
Post::raw()->updateMany(
[],
[ '$set' => ["comments.$[i].comments.$[j].description" => $desc] ],
[ '$arrayFilters' => [
[
[ "i.id" => "5b4dc367d282f" ],
[ "j.id" => "5b4dcfc7022db" ]
]
]
]
)
Hope it helps :)

Laravel MongoDB library 'jenssegers/laravel-mongodb' hasMany relationship is not working

I am using MongoDB library https://github.com/jenssegers/laravel-mongodb version 3.1.0-alpha in Laravel 5.3.28 I have two collections in MongoDB and I want to make a hasMany relation b/w them. Means each Employee performs many tasks. I have used reference and added employee_ids in the task collection.
Below are my code:
MongoDB:
1st Collection: Employee
{
"_id" : ObjectId("586ca8c71a72cb07a681566d"),
"employee_name" : "John",
"employee_description" : "test description",
"employee_email" : "john#email.com",
"updated_at" : "2017-01-04 11:45:20",
"created_at" : "2017-01-04 11:45:20"
},
{
"_id" : ObjectId("586ca8d31a72cb07a6815671"),
"employee_name" : "Carlos",
"employee_description" : "test description",
"employee_email" : "carlos#email.com",
"updated_at" : "2017-01-04 11:45:20",
"created_at" : "2017-01-04 11:45:20"
}
2nd Collection: Task
{
"_id" : ObjectId("586ccbcf1a72cb07a6815b04"),
"task_name" : "New Task",
"task_description" : "test description",
"task_status" : 1,
"task_start" : "2017-04-01 12:00:00",
"task_end" : "2017-04-01 02:00:00",
"task_created_at" : "2017-04-01 02:17:00",
"task_updated_at" : "2017-04-01 02:17:00",
"employee_id" : [
ObjectId("586ca8c71a72cb07a681566d"),
ObjectId("586ca8d31a72cb07a6815671")
]
},
{
"_id" : ObjectId("586cd3261a72cb07a6815c69"),
"task_name" : "2nd Task",
"task_description" : "test description",
"task_status" : 1,
"task_start" : "2017-04-01 12:00:00",
"task_end" : "2017-04-01 02:00:00",
"task_created_at" : "2017-04-01 02:17:00",
"task_updated_at" : "2017-04-01 02:17:00",
"employee_id" : ObjectId("586ca8c71a72cb07a681566d")
}
Laravel:
Model:
Employee:
<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class Employee extends Eloquent {
protected $collection = 'employee';
protected $primaryKey = '_id';
public function tasks()
{
return $this->hasMany('App\Models\Task');
}
}
Laravel:
Model:
Task:
<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class Task extends Eloquent {
protected $collection = 'task';
protected $primaryKey = '_id';
public function employees()
{
return $this->belongsTo('App\Models\Employee');
}
}
I want to get tasks assigned to the specific employee.
Controller:
public function EmployeeData($data)
{
$employees = Employee::with('tasks')->where('_id', new \MongoDB\BSON\ObjectID('586ca8d31a72cb07a6815671'))->get();
echo "<pre>";
print_r($employees);exit;
}
Output:
Illuminate\Database\Eloquent\Collection Object
(
[items:protected] => Array
(
[0] => App\Models\Employee Object
(
[connection:protected] => mongodb
[collection:protected] => lt_employees
[primaryKey:protected] => _id
[employee_id:App\Models\Employee:private] =>
[employee_name:App\Models\Employee:private] =>
[employee_description:App\Models\Employee:private] =>
[employee_email:App\Models\Employee:private] =>
[employee_created_at:App\Models\Employee:private] =>
[employee_updated_at:App\Models\Employee:private] =>
[parentRelation:protected] =>
[table:protected] =>
[keyType:protected] => int
[perPage:protected] => 15
[incrementing] => 1
[timestamps] => 1
[attributes:protected] => Array
(
[_id] => MongoDB\BSON\ObjectID Object
(
[oid] => 586ca8d31a72cb07a6815671
)
[employee_name] => Carlos
[employee_description] => test description
[employee_email] => carlos#email.com
[updated_at] => 2017-01-04 11:45:20
[created_at] => 2017-01-04 11:45:20
)
[original:protected] => Array
(
[_id] => MongoDB\BSON\ObjectID Object
(
[oid] => 586ca8d31a72cb07a6815671
)
[employee_name] => Carlos
[employee_description] => test description
[employee_email] => carlos#email.com
[updated_at] => 2017-01-04 11:45:20
[created_at] => 2017-01-04 11:45:20
)
[relations:protected] => Array
(
[tasks] => Illuminate\Database\Eloquent\Collection Object
(
[items:protected] => Array
(
)
)
)
[hidden:protected] => Array
(
)
[visible:protected] => Array
(
)
[appends:protected] => Array
(
)
[fillable:protected] => Array
(
)
[guarded:protected] => Array
(
[0] => *
)
[dates:protected] => Array
(
)
[dateFormat:protected] =>
[casts:protected] => Array
(
)
[touches:protected] => Array
(
)
[observables:protected] => Array
(
)
[with:protected] => Array
(
)
[exists] => 1
[wasRecentlyCreated] =>
)
)
)
In the output, relation tasks items are empty.
Can anyone suggest me that the relation b/w collections are correct?
Update
I have used belongsToManyin the relation. Now my models are:
In the Employee Model:
public function tasks()
{
return $this->belongsToMany('App\Models\Task');
}
In the Task Model:
public function employees()
{
return $this->belongsToMany('App\Models\Employee');
}
These are the documents:
Employee collection
{
"_id" : ObjectId("586ca8c71a72cb07a681566d"),
"employee_name" : "Carlos",
"employee_description" : "test description",
"employee_email" : "carlos#email.com",
"updated_at" : "2017-01-04 11:45:20",
"created_at" : "2017-01-04 11:45:20",
"task_ids" : [
ObjectId("586ccbcf1a72cb07a6815b04"),
ObjectId("586cd3261a72cb07a6815c69")
]
},
{
"_id" : ObjectId("586ca8d31a72cb07a6815671"),
"employee_name" : "John",
"employee_description" : "test description",
"employee_email" : "john#email.com",
"updated_at" : "2017-01-04 11:45:20",
"created_at" : "2017-01-04 11:45:20"
}
Task collection
{
"_id" : ObjectId("586ccbcf1a72cb07a6815b04"),
"task_name" : "New Task",
"task_description" : "test description",
"task_status" : 1,
"task_start" : "2017-04-01 12:00:00",
"task_end" : "2017-04-01 02:00:00",
"task_created_at" : "2017-04-01 02:17:00",
"task_updated_at" : "2017-04-01 02:17:00",
"employee_ids" : [
ObjectId("586ca8c71a72cb07a681566d"),
ObjectId("586ca8d31a72cb07a6815671")
]
},
{
"_id" : ObjectId("586cd3261a72cb07a6815c69"),
"task_name" : "2nd Task",
"task_description" : "test description",
"task_status" : 1,
"task_start" : "2017-04-01 12:00:00",
"task_end" : "2017-04-01 02:00:00",
"task_created_at" : "2017-04-01 02:17:00",
"task_updated_at" : "2017-04-01 02:17:00",
"employee_ids" : ObjectId("586ca8c71a72cb07a681566d")
}
I get the first employee with these documents:
$employee = Employee::with('tasks')->first();
dd($employee);
And I gotthe output with empty relation:
Employee {#176
#connection: "mongodb"
#collection: "employee"
#primaryKey: "_id"
-employee_id: null
-employee_name: null
-employee_description: null
-employee_email: null
-employee_created_at: null
-employee_updated_at: null
#parentRelation: null
#table: null
#keyType: "int"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:10 [
"_id" => ObjectID {#170}
"employee_name" => "Carlos"
"employee_description" => "test description"
"employee_email" => "carlos#email.com"
"updated_at" => "2017-01-04 11:45:20"
"created_at" => "2017-01-04 11:45:20"
"task_ids" => array:2 [
0 => ObjectID {#174}
1 => ObjectID {#175}
]
]
#original: array:10 [
"_id" => ObjectID {#170}
"employee_name" => "Carlos"
"employee_description" => "test description"
"employee_email" => "carlos#email.com"
"updated_at" => "2017-01-04 11:45:20"
"created_at" => "2017-01-04 11:45:20"
"task_ids" => array:2 [
0 => ObjectID {#174}
1 => ObjectID {#175}
]
]
#relations: array:1 [
"tasks" => Collection {#173
#items: []
}
]
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
+exists: true
+wasRecentlyCreated: false
}
I understood by your other question, that a task can belong to many employees, right? So you should be using belongsToMany relationship in your Task model. Also your example "task" collection shows that in one document employee_id is an array and in the other document it is an ObjectId, when both should be arrays.
Anyway, I've had a hard time trying to figure this out, but I've seen that you can't use hasMany as the inverse of belongsToMany, because belongsToMany creates an array of ids, and hasMany doesn't work well with arrays. I would say that we would need something like hasManyInArray, but when I associate a belongsToMany relationship, the "parent" document gets created an array of ids, which leads me to think that the parent should also use belongsToMany even though it doesn't "belong to" but actually "has". So when you would associate an employee to a task like this:
$task->employees()->save($employee);
The "employee" document will end up having a "task_ids" attribute with the only task id it should have. So that seems to be the way to go with Jenssegers: to use belongsToMany in both models:
Laravel: Model: Employee:
<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class Employee extends Eloquent
{
protected $collection = 'employee';
public function tasks()
{
return $this->belongsToMany(Task::class);
}
}
Laravel: Model: Task:
<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class Task extends Eloquent
{
protected $collection = 'task';
public function employees()
{
return $this->belongsToMany(Employee::class);
}
}
And you would use this like:
// Give a task a new employee
$task->employees()->save($employee);
// Or give an employee a new task
$employee->tasks()->save($task);
The only thing about this is that when you look at the database, you will see that your employee documents have an array called "task_ids", and inside it, the id of the only task each employee have. I hope this helped.
Just some side notes, you know that you don't have to define the name of the primary key on each model, right? You don't need this:
protected $primaryKey = '_id';
Also you don't have to define the name of the collection (i.e. protected $collection = 'employee';), unless you really want them to be in singular (by default they are in plural).
I got up in the middle of the night (it's 3:52AM here) and checked something on the computer and then checked SO an saw your question, I hope this time I answered soon enough for you, we seem to be in different timezones.
Update
These are the documents I created for testing:
employee collection
{
"_id" : ObjectId("5870ba1973b55b03d913ba54"),
"name" : "Jon",
"updated_at" : ISODate("2017-01-07T09:51:21.316Z"),
"created_at" : ISODate("2017-01-07T09:51:21.316Z"),
"task_ids" : [
"5870ba1973b55b03d913ba56"
]
},
{
"_id" : ObjectId("5870ba1973b55b03d913ba55"),
"name" : "Doe",
"updated_at" : ISODate("2017-01-07T09:51:21.317Z"),
"created_at" : ISODate("2017-01-07T09:51:21.317Z"),
"task_ids" : [
"5870ba1973b55b03d913ba56"
]
}
task collection
{
"_id" : ObjectId("5870ba1973b55b03d913ba56"),
"name" : "New Task",
"updated_at" : ISODate("2017-01-07T09:51:21.317Z"),
"created_at" : ISODate("2017-01-07T09:51:21.317Z"),
"employee_ids" : [
"5870ba1973b55b03d913ba54",
"5870ba1973b55b03d913ba55"
]
}
With these documents I get the first employee like this:
$employee = Employee::with('tasks')->first();
dd($employee);
And in the output we can see the relations attribute is an array:
Employee {#186 ▼
#collection: "employee"
#primaryKey: "_id"
// Etc.....
#relations: array:1 [▼
"tasks" => Collection {#199 ▼
#items: array:1 [▼
0 => Task {#198 ▼
#collection: "task"
#primaryKey: "_id"
// Etc....
#attributes: array:5 [▼
"_id" => ObjectID {#193}
"name" => "New Task"
"updated_at" => UTCDateTime {#195}
"created_at" => UTCDateTime {#197}
"employee_ids" => array:2 [▶]
]
}
]
}
]
}
Update 2
The belongsToMany method isn't in the file you mention because that class (i.e. Jenssegers\Mongodb\Eloquent\Model) extends Laravel's Eloquent Model class, and that's where the belongsToMany method is.
Ok so that must be why it's not working for you, because the arrays have to be strings instead of ObjectIds. Why is this? Because that's how the Jenssegers library work, it saves the Ids as strings. I've also found this behaviour strange, but that's how it works. Remember that you are supposed to relate objects using the Jenssegers library, not by creating the data manually in the database.
How can you index the ids? Just create a normal index in MongoDB, like tasks.createIndex({task_ids: 1}). Here's the documentation on how to create indexes: https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/. You can also create indexes on migrations, here are the docs on migrations, make sure to read Jenssegers notes on migrations too.
You can access the tasks realtion like this: $employee->tasks;. You access relations by getting a property with the same name of the method you declared your relation with, so if you have:
class Post
{
public function owner()
{
return $this->belongsTo(User::class);
}
}
You get the relation as $post->owner;. Here's the documentation on relations: https://laravel.com/docs/5.3/eloquent-relationships

how to check two fields having same values in same table in laravel 5 with mongodb

I have to check the two fields having same values in same table in laravel 5. I am using Mongodb.
{
"id": "565d23ef5c2a4c9454355679",
"title": "Event1",
"summary": "test",
"total": NumberInt(87),
"remaining": NumberInt(87),
"status": "1"
}
I need to check "total" and "remaining" fields are same. How to write query in laravel 5.1. Please help.
One approach you could take would be using the aggregation framework methods from the raw MongoDB collection object provided from the underlying driver. In the mongo shell, you would essentially run the following aggregation pipeline operation to compare the two fields and return the documents which satisfy that criteria:
db.collection.aggregate([
{
"$project": {
"isMatch": { "$eq" : ["$total", "$remaining"] }, // similar to "valueof(total) == valueof(remaining)"
"id" : 1,
"title" : 1,
"summary" : 1,
"total" : 1,
"remaining" : 1,
"status" : 1
}
},
{
"$match": { "isMatch": true } // filter to get documents that only satisfy "valueof(total) == valueof(remaining)"
}
]);
Or using the $where operator in the find() query:
db.collection.find({ "$where" : "this.total == this.remaining" })
Thus in laravel, you can get the documents using raw expressions as follows
$result = DB::collection("collectionName") -> raw(function ($collection)
{
return $collection->aggregate(array(
array(
"$project" => array(
"id" => 1,
"title" => 1,
"summary" => 1,
"total" => 1,
"remaining" => 1,
"status" => 1,
"isMatch" => array(
"$eq" => array( "$total", "$remaining" )
)
)
),
array(
"$match" => array(
"isMatch" => true
)
)
));
});
In the case of $where, you can inject the expressions directly into the query:
Model::whereRaw(array("$where" => "this.total == this.remaining"))->get();
Or using the raw expression on the internal MongoCollection object executed on the query builder. Note that using the raw() method requires using a cursor because it is a low-level call:
$result = Model::raw()->find(array("$where" => "this.total == this.remaining"));
Collectionname::whereRaw(array('$where' => "this.filed1 > this.field2"))

mongoDB : multi value field search ignoring null fields

I am trying to make a search on several field where some of them can be left blank.
Given the following Json document in MongoDB (only three fields here, but N fields in reality, with N > 10) :
{
'first_name' : 'value_X',
'last_name' : 'value_Y',
'mail_adress' : 'value_Z'
}
Then let's suppose a form where the user can enter the value of the first name, last name, mail address and phone number.
If all the field are filled the query in MongoDB looks like :
db.collection.find( {
'first_name' : 'value_A',
'last_name' : 'value_B',
'mail_adress' : 'value_C'
}
)
The problem I have is that I might have some fields left blank by the user :
It means that my query can also become :
db.collection.find({ 'first_name' : 'value_A' })
or
db.collection.find( {'last_name' : 'value_B'})
or
db.collection.find( {'mail_adress' : 'value_C'})
or
db.collection.find( {'first_name' : 'value_A','last_name' : 'value_B'})
or
db.collection.find( {'first_name' : 'value_A','last_name' : 'value_C'})
or
db.collection.find( {'last_name' : 'value_B','mail_adress' : 'value_C'})
I would like to know if I need to write a query for each particular instance ?
I also thought to generate query dynamically depending on the field entered by a user, it would be easy as query are Json doucment.
I am also wondering if MongoDB could ignore field if they are null (but I don't want to lose performance in my query)
Anyone can help me ? Thanks
EDIT on 20140404 at 6PM : part of answer
Here is the Python code with Aditya suggestion.
I also added the python code about generating the query in front end as svjn suggested:
'''
Created on 4 Apr 2014
#author: scoulombel
'''
import pymongo
from pymongo import MongoClient
import random
import string
# fill the database
def fillCollection(collection):
doc = {
'first_name' : 'value_X',
'last_name' : 'value_Y',
'mail_adress' : 'value_Z'
}
docID = collection.insert(doc)
for _ in range(0,10):
doc = {
'first_name' : ''.join(random.choice(string.ascii_uppercase) for _ in range(4)) + ''.join(random.choice(string.digits) for _ in range(4)),
'last_name' : ''.join(random.choice(string.ascii_uppercase) for _ in range(4)) + ''.join(random.choice(string.digits) for _ in range(4)),
'mail_adress' : ''.join(random.choice(string.ascii_uppercase) for _ in range(4)) + ''.join(random.choice(string.digits) for _ in range(4))
}
docID = collection.insert(doc)
# read data in a cursor
def readCursor(cursor):
object_list = []
count = 0
for doc in cursor :
del doc["_id"]
count += 1
doc['resOrder'] = count
object_list.append(doc)
print object_list
if __name__ == '__main__':
#client = MongoClient()
client = MongoClient('ip', 'port')
db = client.test_OR_AND_DB
collection = db.collection
fillCollection(collection)
# SEARCH VALUE which can be null or not
value_A = 'value_X'
value_B = 'null' #'value_Y'
value_C = 'null' #'value_Z'
# APPROACH 1 : ADITYA answer for search
cursor1 = db.collection.find(
{
'$or' : [
{ '$and' : [ {'first_name' : { '$ne': 'null' }}, {'first_name' : value_A } ]},
{ '$and' : [ {'last_name' : { '$ne': 'null' }}, {'last_name' : value_B } ]},
{ '$and' : [ {'mail_adress' :{ '$ne' :'null' }}, {'mail_adress':value_C } ]}
]
}
)
readCursor(cursor1)
# APPROACH 2 ; create a JSON representing the query dynamically
path_value_dict = {'first_name':value_A, 'last_name':value_B, 'mail_address':value_C} # if embedded it would be for instance name.first_name
query= {}
for key in path_value_dict.keys():
if path_value_dict[key] != 'null':
query[key] = path_value_dict[key]
print query
cursor2 = db.collection.find({'first_name': 'value_X', 'mail_adress': 'value_Z'})
readCursor(cursor2)
Use Mongodb $or operator for your query.
The $or operator performs a logical OR operation on an array of two or more <expressions> and selects the documents that satisfy at least one of the <expressions>.
Use :
db.collection.find({
$or : [{ $and : [ {'first_name' : { $ne:null }, {'first_name' :'value_A' }]},
{ $and : [ {'last_name' : { $ne:null }, {'last_name' :'value_B' }]},
{ $and : [ {'mail_addres' :{ $ne:null }, {'mail_addres':'value_C'}]}
]}
);

Is it possible to query MongoDB, using ONLY Array([x][y[x][z]]) Approach? NOT knowing Elements' Content?

This is the first of 7 test/example documents, in collection "SoManySins."
{
"_id" : ObjectId("51671bb6a6a02d7812000018"),
"Treats" : "Sin1 = Gluttony",
"Sin1" : "Gluttony",
"Favourited" : "YES",
"RecentActivity" : "YES",
"GoAgain?" : "YeaSure."
}
I would like to be able to query to retrieve any info in any position,
just by referring to the position. The following document,
{
"_id" : ObjectId("51671bb6a6a02d7812000018"),
"Sin1" : "Gluttony",
"?????????" : "??????",
"RecentActivity" : "YES",
"GoAgain?" : "YeaSure."
}
One could retrieve whatever might be in the 3rd key~value
pair. Why should one have to know ahead of time what the
data is, in the key? If one has the same structure for the
collection, who needs to know? This way, you can get
double the efficiency? Like having a whole lot of mailboxes,
and your app's users supply the key and the value; your app
just queries the dbs' documents' arrays' positions.
Clara? finally? I hope?
The sample document you've provided is not saved as an array in BSON:
{
"_id" : ObjectId("51671bb6a6a02d7812000018"),
"Sin1" : "Gluttony",
"?????????" : "??????",
"RecentActivity" : "YES",
"GoAgain?" : "YeaSure."
}
Depending on the MongoDB driver you are using, the fields here are typically represented in your application code as an associative array or hash. These data structures are not order-preserving so you cannot assume that the 3rd field in a given document will correspond to the same field in another document (or even that the same field ordering will be consistent on multiple fetches). You need to reference the field by name.
If you instead use an array for your fields, you can refer by position or select a subset of the array using the $slice projection.
Example document with an array of fields:
{
"_id" : ObjectId("51671bb6a6a02d7812000018"),
"fields": [
{ "Sin1" : "Gluttony" },
{ "?????????" : "??????" },
{ "RecentActivity" : "YES" },
{ "GoAgain?" : "YeaSure." }
]
}
.. and query to find the second element of the fields array (a $slice with skip 1, limit 1):
db.SoManySins.find({}, { fields: { $slice: [1,1]} })
{
"_id" : ObjectId("51671bb6a6a02d7812000018"),
"fields" : [
{
"?????????" : "??????"
}
]
}
This is one way to Query and get back data when you may not
know what the data is, but you know the structure of the data:
examples in Mongo Shell, and in PHP
// the basics, setup:
$dbhost = 'localhost'; $dbname = 'test';
$m = new Mongo("mongodb://$dbhost");
$db = $m->$dbname;
$CursorFerWrites = $db->NEWthang;
// defining a set of data, creating a document with PHP:
$TheFieldGenerator = array( 'FieldxExp' => array(
array('Doc1 K1'=>'Val A1','Doc1 K2'=>'ValA2','Doc1 K3'=>'Val A3'),
array('Doc2 K1'=>'V1','Doc2 K2'=>'V2','Doc2 K3'=>'V3' ) ) ) ;
// then write it to MongoDB:
$CursorFerWrites->save($TheFieldGenerator);
NOTE : In the Shell : This produces the same Document:
> db.NEWthang.insert({"FieldxExp" : [
{"Doc1 K1":"Val A1","Doc1 K2":"Val A2","Doc1 K3":"Val A3"},
{"Doc2 K1":"V1", "Doc2 K2":"V2","Doc2 K3":"V3"}
]
})
#
Now, some mongodb Shell syntax:
> db.NEWthang.find().pretty()
{
"_id" : ObjectId("516c4053baa133464d36e836"),
"FieldxExp" : [
{
"Doc1 K1" : "Val A1",
"Doc1 K2" : "Val A2",
"Doc1 K3" : "Val A3"
},
{
"Doc2 K1" : "V1",
"Doc2 K2" : "V2",
"Doc2 K3" : "V3"
}
]
}
> db.NEWthang.find({}, { "FieldxExp" : { $slice: [1,1]} } ).pretty()
{
"_id" : ObjectId("516c4053baa133464d36e836"),
"FieldxExp" : [
{
"Doc2 K1" : "V1",
"Doc2 K2" : "V2",
"Doc2 K3" : "V3"
}
]
}
> db.NEWthang.find({}, { "FieldxExp" : { $slice: [0,1]} } ).pretty()
{
"_id" : ObjectId("516c4053baa133464d36e836"),
"FieldxExp" : [
{
"Doc1 K1" : "Val A1",
"Doc1 K2" : "Val A2",
"Doc1 K3" : "Val A3"
}
]
}
Finally, how about write the Query in some PHP ::
// these will be for building the MongoCursor:
$myEmptyArray = array();
$TheProjectionCriteria = array('FieldxExp'=> array('$slice' => array(1,1)));
// which gets set up here:
$CursorNEWthang1 = new MongoCollection($db, 'NEWthang');
// and now ready to make the Query/read:
$ReadomgomgPls=$CursorNEWthang1->find($myEmptyArray,$TheProjectionCriteria);
and the second document will be printed out:
foreach ($ReadomgomgPls as $somekey=>$AxMongoDBxDocFromCollection) {
var_dump($AxMongoDBxDocFromCollection);echo '<br />';
}
Hope this is helpful for a few folks.