Question

I've searched the docs and web throughly but cannot find any solution for this seemingly trivial problem. Let's say we have a collection called "states". The states collection has a document that looks like this:

{
    'name': 'New York',
    'towns': [
        {
            'name': 'Town A',
            'buildings':[
                {
                    'name': 'Town Hall',
                    'open_days': [False, True, True, True, True, True, False],
                },
                {
                    'name': 'Fire Department',
                    'open_days': [True, True, True, True, True, True, True],
                },
                {
                    'name': 'Car Dealership',
                    'open_days': [False, True, True, True, True, True, True],
                }
            ]
        },
        {
            'name': 'Town B',
            'buildings':[
                {
                    'name': 'Town Hall',
                    'open_days': [False, True, True, True, True, True, False],
                },
                {
                    'name': 'Police Department',
                    'open_days': [True, True, True, True, True, True, True],
                },
                {
                    'name': 'Karate Dojo',
                    'open_days': [False, True, False, True, False, True, False],
                }
            ]
        }
    ]
}

In this data, open_days represents whether a building is open or closed on a particular day of the week (index 0 being Sunday for example).

I just want to query the data to pull up all the buildings that are open on Sunday (index 0).

I've tried several odd syntaxes to no avail, is there an easy solution to this? If it matters, I am querying using PyMongo.

Was it helpful?

Solution

It actually depends. First of all there are a few ways to restructure your data a little bit to make it more easy to query and to extract. You don't have to restructure it at all, but if you can I think it would be beneficial.

Some refactoring suggestions:

  • First of all you might want to split this single large document into multiple documents. Just take out documents from array towns and make each one a standalone document in your collection. Add state name to each. Embedding docs in Mongo is a good idea, but it seems to me that this is a bit over too much. I would even go further extracting each building into a top level document. The reasons for this are: you might not be able to fit all buildings in a large city into a single document (size limit 16Mb). Even if you are able to fit all buildings into a single document your queries will be quite inefficient if you want to select only small part of that document.
  • Change your array to hold integers corresponding to indexes of weekdays, i.e. Sunday == 0, etc. This will make it simpler and more efficient to query.

So your data would look this way:

{
  "stateName": "New York",
  "townName": "Town A",
  "buildingName": "Town Hall",
  "open_days": [1, 2, 3, 4, 5]
}

Now you can find a building in a city that's open on Monday (1):

db.buildings.find({"stateName": "New York", "townName": "Town A", "open_days": 1});

If you don't want to restructure your data take a look at $elemMatch here which projects matched element of an array. You can also take a look at $ here. However, both these operators do not solve your problem because they return first matched element in the array.

You can also use aggregation framework, but you'll be limited to number of results that fit in a single doc (16Mb), won't be able to have a cursor to results, it will be slower and it's over complicated for what you want to do.

OTHER TIPS

What you could use here is aggregation framework. You will find lots of nice examples on aggregation framework in MongoDB docs - Aggregation What you could be interested in in particular would be $unwind pipeline operator which peels off the elements of an array individually, and returns a stream of documents. $unwind returns one document for every member of the unwound array within every source document.

Here is an example, I hope it provides what you expect it to do:

> db.collection.aggregate(
... { $match : { "towns.buildings.open_days.0" : true }},
... { $unwind : "$towns"},
... { $unwind : "$towns.buildings"},
... { $match : { "towns.buildings.open_days.0" : true }}
... )
{
    "result" : [
        {
            "_id" : ObjectId("52e5ba3beb849fd797d10839"),
            "name" : "New York",
            "towns" : {
                "name" : "Town A",
                "buildings" : {
                    "name" : "Fire Department",
                    "open_days" : [
                        true,
                        true,
                        true,
                        true,
                        true,
                        true,
                        true
                    ]
                }
            }
        },
        {
            "_id" : ObjectId("52e5ba3beb849fd797d10839"),
            "name" : "New York",
            "towns" : {
                "name" : "Town B",
                "buildings" : {
                    "name" : "Police Department",
                    "open_days" : [
                        true,
                        true,
                        true,
                        true,
                        true,
                        true,
                        true
                    ]
                }
            }
        }
    ],
    "ok" : 1
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top