pythonmongodbeve

Eve: how to use different endpoints to access the same collection with different filters


I have an Eve app publishing a simple read-only (GET) interface. It is interfacing a MongoDB collection called centroids, which has documents like:

[
{
  "name":"kachina chasmata",
  "location":{
    "type":"Point",
    "coordinates":[-116.65,-32.6]
  },
  "body":"ariel"
},
{
  "name":"hokusai",
  "location":{
    "type":"Point",
    "coordinates":[16.65,57.84]
  },
  "body":"mercury"
},
{
  "name":"cañas",
  "location":{
    "type":"Point",
    "coordinates":[89.86,-31.188]
  },
  "body":"mars"
},
{
  "name":"anseris cavus",
  "location":{
    "type":"Point",
    "coordinates":[95.5,-29.708]
  },
  "body":"mars"
}
]

Currently, (Eve) settings declare a DOMAIN as follows:

crater = {
    'hateoas': False,
    'item_title': 'crater centroid',
    'url': 'centroid/<regex("[\w]+"):body>/<regex("[\w ]+"):name>',
    'datasource': {
        'projection': {'name': 1, 'body': 1, 'location.coordinates': 1}
    }
}

DOMAIN = {
    'centroids': crater,
}

Which will successfully answer to requests of the form http://hostname/centroid/<body>/<name>. Inside MongoDB this represents a query like: db.centroids.find({body:<body>, name:<name>}).

What I would like to do also is to offer an endpoint for all the documents of a given body. I.e., a request to http://hostname/centroids/<body> would answer the list of all documents with body==<body>: db.centroids.find({body:<body>}).

How do I do that?

I gave a shot by including a list of rules to the DOMAIN key centroids (the name of the database collection) like below,

crater = {
...
}

body = {
    'item_title': 'body craters',
    'url': 'centroids/<regex("[\w]+"):body>'
}

DOMAIN = {
    'centroids': [crater, body],
}

but didn't work...

AttributeError: 'list' object has no attribute 'setdefault'

Solution

  • Got it!

    I was assuming the keys in the DOMAIN structure was directly related to the collection Eve was querying. That is true for the default settings, but it can be adjusted inside the resources datasource.

    I figured that out while handling an analogous situation as that of the question: I wanted to have an endpoint hostname/bodies listing all the (unique) values for body in the centroids collection. To that, I needed to set an aggregation to it.

    The following settings give me exactly that ;)

    centroids = {
        'item_title': 'centroid',
        'url': 'centroid/<regex("[\w]+"):body>/<regex("[\w ]+"):name>',
        'datasource': {
            'source': 'centroids',
            'projection': {'name': 1, 'body': 1, 'location.coordinates': 1}
        }
    }
    
    bodies = {
        'datasource': {
            'source': 'centroids',
            'aggregation': {
                'pipeline': [
                    {"$group": {"_id": "$body"}},
                ]
            },
        }
    }
    
    DOMAIN = {
        'centroids': centroids,
        'bodies': bodies
    }
    

    Beautiful. Thumbs up to Eve!