Replace multiple joins in SQL with CouchDB views

一笑奈何 提交于 2019-12-21 21:33:19

问题


I am implementing a filter feature for my application and having trouble with writing the view on CouchDB. While in SQL, this would be a statement with multiple join. How to replace multiple join in CouchDB. This article covers single join: http://www.cmlenz.net/archives/2007/10/couchdb-joins. However, it's not obvious to me how to extend this approach to multiple join.

Imagine my object has a dozen properties and each property can have its individual filter. For simplicity, let's assume it has only two properties. If we can solve that, we can probably extend it to any number of properties.

My application needs to support a bunch of AND filters. E.g. find all sessions where location in ("Seattle", "New York") AND start-epoch >= 1336608413. And I also need to persist filter settings.

In SQL, I have two tables: Session and Filter. Then I have one join per filter condition between Session and Filter table to get all filtered sessions.

Session Table
location    start-epoch    
Seattle     1336608413     

Filter Table
Name        Value
location    Seattle
location    New York
start-epoch 1336608413     


Query:
select * 
from Session s 
where exists (select 1 from Filter f where f.name = 'location' and f.value = s.location)
     and exists (select 1 from Filter f on f.name = 'start-epoch' and s.start-epoch >= f2.value)

In CouchDB:

{"type":"session", "location":"Seattle", "start-epoch":1336608413}

Then how to model filter and write the view?

Filter could be something like this:

{"type":"filter", "location":["Seattle", "New York"], "start-epoch":1336608413}

Then how to write the view?

I want to achieve this inside CouchDB. I am hoping to create a view called FilteredSessions and retrieve the filtered session in one http call to Couch. The reason is because I have 5 different clients (iOS, Android, Silverlight, Ruby on Rails and Windows 8) and I don't want to rewrite the logic 5 times. If I use sqlite, I can create a view called "FilteredView" to achieve that. I hope to write the equivalent in map reduce.


回答1:


Hmmm...I'm not sure exactly what you need, but here's what I understood. You have one collection of data which you're calling sessions, and then you have another collection of data which you're calling filters. And then you want to assemble another collection of data from the sessions but using the filter data to select specific data from the sessions. If that's right, there's a few ways to do it, heres' one:

Let's say you have 30 or so session docs. They have two properties each: location, and start-epoch. For example:

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "location"    : "Seattle",
 "start-epoch" : "1336608413",
 "type"        : "session"
}

Or...

{
 "_id"         : "f8fsdnio345235aodnia9dgs8n9",
 "location"    : "New York",
 "start-epoch" : "1236630174",
 "type"        : "session"
}

Then you have some filters, like:

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "location"    : "New York",
 "type"        : "filter"
}

Or...

{
 "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
 "start-epoch" : "1336608413",
 "type"        : "filter"
}

You can pull all the sessions into one view:

"sesssions": {
  "map": "function (doc) {
    if (doc.type === "session") {
      emit(doc.start-epoch, doc);
    }
}"

Then, pull all the filters into one view (and you can update this on the fly):

"filters": {
  "map": "function (doc) {
    if (doc.type === "filter") {
      emit(doc.type, doc);
    }
}"

So then I would assemble my final "view" in nodejs. I would do a request to the filters view of my db or creating a new filter if need be, and store that in a variable, like filters or filter. Then I'd do another call to the sessions view of my db, and pull that data into a variable, like sessions.

A little side note here. You would need to call the view (let's say you stored the json object which couch returned into a var view) and then store the view.rows into a sessions variable, which would give you an array of sessions like this:

[
 {
  "_id"         : "f80hqwfe8nfaodnia9dgs8n9",
  "location"    : "Seattle",
  "start-epoch" : "1336608413",
  "type"        : "session"
 },
 {
  "_id"         : "f8fsdnio345235aodnia9dgs8n9",
  "location"    : "New York",
  "start-epoch" : "1236630174",
  "type"        : "session"
 }
]

Then just do

var myFinalView = sessions.forEach(function (session) {
  if (filter.location !== session.location) {
    // session.splice or delete session
  }
  return;
})

And then myFinalView will be the JSON object you need. I hope that makes sense. Please ask me to elaborate if need be.




回答2:


var sessions = [];
var filters = [];

function(doc) {
  if (doc.type === 'session') {
    sessions.push(doc);
  }
  if (doc.type === 'filter') {
    filters.push(doc);
  }
  emit(filters.length, filters);
  emit(sessions.length, sessions);
}

I tried to pull those emits out below the end of the function but that seemed to upset CouchDB. This 'compiles' the filters and sessions into collections. If there were a good way to know when that 'compilation' is complete, the next step would be to loop through the filters and then forEach through the sessions applying the filters.

Thanks for sharing this challenge! This one really pushes the envelope on CouchDB. I hope we can figure this out, it should be possible.




回答3:


Here is the idea I have. In map function, emit both session and filter documents. Then use the list function to enumerate the map output to read the persisted filters and sessions to do the filtering.



来源:https://stackoverflow.com/questions/10526006/replace-multiple-joins-in-sql-with-couchdb-views

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!