Harrison
Harrison

Reputation: 319

Mongo DB query of complex json structure

Say I have a json structure like so:

{
    "A":{
        "name":"dog",
        "foo":"bar",
        "array":[
            {"name":"one"},
            {"name":"two"}
        ]
    },
    "B":{
        "name":"cat",
        "foo":"bar",
        "array":[
            {"name":"one"},
            {"name":"three"}
        ]
    }
}

I want to be able to do two things.

1: Query for any "name":* within "A.array".

2: Query for any "name":"one" within "*.array".

That is, any object within a specific document's array, and any specific object within any document's array.

I hope I have used proper terminology here, I am just starting to familiarize myself with a lot of these concepts. I have tried searching for an answer but am having trouble finding something like my case.

Thanks.

EDIT: Since I still haven't really made progress towards this, I'll just explain what I'm trying to do: I want to use the "AllSets" dataset (after I trim it down below 16mb) available on mtgjson.com. I am having problems getting mongo to play nicely though.

In an effort to try and learn what's going on, I have downloaded one set: http://mtgjson.com/json/OGW.json.

Here is a photo of its structure laid out:json structure

I am unable to even get mongo to return an object from within the cards array using: "find({cards: {$elemMatch: {name:"Deceiver of Form"}}})" "find({"cards.name":"Deceiver of Form"})"

When I run either of the commands above it just returns the entire document to me.

Upvotes: 2

Views: 2485

Answers (1)

Wan B.
Wan B.

Reputation: 18835

You could use the positional projection $ operator to limit the contents of an array. For example, if you have a single document like below:

{
  "block": "Battle for Zendikar",
  "booster": "...",
  "translations": "...",
  "cards": [
    {
      "name": "Deceiver of Form",
      "power": "8"
    },
    {
      "name": "Eldrazi Mimic",
      "power": "2"
    },
    {
      "name": "Kozilek, the Great Distortion",
      "power": "12"
    }
  ]
}

You can query for a card name matching "Deceiver of Form", and limit fields to return only the matching array card element(s) using:

> db.collection.find({"cards.name":"Deceiver of Form"}, {"cards.$":1})

{
  "_id": ObjectId("..."),
  "cards": [
    {
      "name": "Deceiver of Form",
      "power": "8"
    }
  ]
}

Having said the above, I think you should re-consider your data model. MongoDB is a document-oriented database. A record in MongoDB is a document, so having a single record in a database does not bring out the potential of the database i.e. similar to storing all data in a single row in a table.

You should try storing the 'cards' into a collection instead. Where each document is a single card, (depending on your use case) you could add a reference to another collection containing the deck information. i.e: block, type, releaseDate, etc. For example:

// a document in cards collection:
    {
      "name": "Deceiver of Form",
      "power": "8",
      "deck_id": 1
    }
// a document in decks collection:
    {
      "deck_id": 1,
      "releaseDate": "2016-01-22",
      "type": "expansion"
    }

For different types of data model designs and examples, please see Data Model Design.

Upvotes: 2

Related Questions