Jon M
Jon M

Reputation: 664

Selective find within a Mongo embedded document

I'm trying to find documents that have elements matching a search key. In the example below, I have 6 documents with major and minor categories.

The format of the document is:

{
    docID: <some proxy ID>,
    classCategories: {
        [
           "<major classification>" : [ "<array of sub classifications>" ]
        ]
    }
} 

I have 2 queries below:

  1. Find all documents where the major classification is "2", and it includes a sub classification of "2".
  2. Find all documents where the major classification is "1", and it includes a sub classification of "3".

The test code is here:

db.test.delete_many({})
db.test.insert_one({"docID": 1, "classCategories": {"1": [1], "2": [2]}})
db.test.insert_one({"docID": 2, "classCategories": {"1": [1, 3], "2": [2, 4]}})
db.test.insert_one({"docID": 3, "classCategories": {"1": [1], "2": [2, 6]}})
db.test.insert_one({"docID": 4, "classCategories": {"1": [1], "2": [2]}})
db.test.insert_one({"docID": 5, "classCategories": {"1": [1]}})
db.test.insert_one({"docID": 6, "classCategories": {"2": [2]}})

results=db.test.find()
print("All Data")
for r in results:
    print("{} -> {}".format(r["docID"], r["classCategories"]))

print("Restricted Data-1")
results=db.test.find({"classCategories": {"2" : [2]}})
for r in results:
    print("{} -> {}".format(r["docID"], r["classCategories"]))
print("Restricted Data-2")
results=db.test.find({"classCategories": {"1" : [3]}})
for r in results:
    print("{} -> {}".format(r["docID"], r["classCategories"]))

The results are here:

All Data
1 -> {'1': [1], '2': [2]}
2 -> {'1': [1, 3], '2': [2, 4]}
3 -> {'1': [1], '2': [2, 6]}
4 -> {'1': [1], '2': [2]}
5 -> {'1': [1]}
6 -> {'2': [2]}
Restricted Data-1
6 -> {'2': [2]}
Restricted Data-2

What I would have expected to see from the first query is documents 1-4, and 6. They have a major classification of "2" and all the arrays contain a sub classification of 2.

In the second query, I would have expected document number 2 as it has a major classification of "1", and the sub classification array contains a 3.

How should I be querying this, and/or should I consider a different document structure?

Upvotes: 0

Views: 32

Answers (1)

Jon M
Jon M

Reputation: 664

I've managed to resolve this through a couple of avenues:

  1. The document format needs to be slightly different. classCategories, needs to be an array of sub-documents, not a document of arrays.
  2. The search will use $elemMatch.

Here is the solution:

db.test.delete_many({})
db.test.insert_one({"docID": 1, "classCategories": [{"1": [1]}, {"2": [2]}]})
db.test.insert_one({"docID": 2, "classCategories": [{"1": [1, 3]}, {"2": [2, 4]}]})
db.test.insert_one({"docID": 3, "classCategories": [{"1": [1], "2": [2, 6]}]})
db.test.insert_one({"docID": 4, "classCategories": [{"1": [1], "2": [2]}]})
db.test.insert_one({"docID": 5, "classCategories": [{"1": [1]}]})
db.test.insert_one({"docID": 6, "classCategories": [{"2": [2]}]})

results=db.test.find() print("All Data") for r in results:
    print("{} -> {}".format(r["docID"], r["classCategories"]))

print("Restricted Data-1") results=db.test.find({"classCategories": { "$elemMatch": {"2" : 2} } }) for r in results:
    print("{} -> {}".format(r["docID"], r["classCategories"]))

print("Restricted Data-2") results=db.test.find({"classCategories": { "$elemMatch": {"1" : 3} } }) for r in results:
        print("{} -> {}".format(r["docID"], r["classCategories"]))

And the output as expected is:

All Data
1 -> [{'1': [1]}, {'2': [2]}]
2 -> [{'1': [1, 3]}, {'2': [2, 4]}]
3 -> [{'1': [1], '2': [2, 6]}]
4 -> [{'1': [1], '2': [2]}]
5 -> [{'1': [1]}]
6 -> [{'2': [2]}]
Restricted Data-1
1 -> [{'1': [1]}, {'2': [2]}]
2 -> [{'1': [1, 3]}, {'2': [2, 4]}]
3 -> [{'1': [1], '2': [2, 6]}]
4 -> [{'1': [1], '2': [2]}]
6 -> [{'2': [2]}]
Restricted Data-2
2 -> [{'1': [1, 3]}, {'2': [2, 4]}]

Upvotes: 1

Related Questions