Create a view to get multiple documents in CouchDb - couchdb

CouchDb newbie here.
I have several documents in CouchDb with the same structure:
{
"_id": "1170140286",
"_rev": "1-79ffad4d4cbe24effc72f9ec519373ca",
"data": [
{
"photo": "link_of_photo1",
"userid": "34623",
"username": "guest-user1"
},
{
"photo": "link_of_photo2",
"userid": "34623",
"username": "guest-user1"
},
{
"photo": "link_of_photo3",
"userid": "34623",
"username": "guest-user1"
}
]
}
and
{
"_id": "43573458",
"_rev": "1-0ca5aa68590fcb58399fe059aa8fb881",
"data": [
{
"photo": "link_of_photo1",
"userid": "6334",
"username": "guest-user2"
},
{
"photo": "link_of_photo2",
"userid": "6334",
"username": "guest-user2"
},
{
"photo": "link_of_photo3",
"userid": "6334",
"username": "guest-user2"
}
]
}
I don't know whether what I want to do is possible, but i am trying to create a view that will combine the data elements of these documents into one single document:
[
{
"photo": "link_of_photo1",
"userid": "34623",
"username": "guest-user1"
},
{
"photo": "link_of_photo2",
"userid": "34623",
"username": "guest-user1"
},
{
"photo": "link_of_photo3",
"userid": "34623",
"username": "guest-user1"
},
{
"photo": "link_of_photo1",
"userid": "6334",
"username": "guest-user2"
},
{
"photo": "link_of_photo2",
"userid": "6334",
"username": "guest-user2"
},
{
"photo": "link_of_photo3",
"userid": "6334",
"username": "guest-user2"
}
]
I am pretty sure I haven't understood the logic of couchdb correctly, so any help is highly aprpeciated.

What you can get is a view result with all links included. It will be an array but will look a bit different to your mocked result structure. A view logic can look like (the key of the rows is not used in the example - maybe the user name or id is an useful value to put in):
function (doc) {
var data = doc.data
if (!data) return
for (var i = 0, link; link = data[i++];)
emit(null, link)
}
For the sake of completeness should be mentioned that there is a way to "merge all docs" server-side. The view result can be manipulated by a CouchDB list before the data will finally send back to the requester. But.it.is.not.recommended! Please take that seriously and don't try that out - its a massive performance issue and was never the goal of CouchDB to provide such uses-cases.

Please refer these links.
http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
CouchDB "Join" two documents
combine multiple documents in a couchdb view
Sample Code:
function(doc){
if(doc.items)
doc.items.forEach(function(item){
emit(doc._id,{_id:item});
})
}
I hope these will solve your problem.

Related

Access deep nested object from array

I am having a challenge getting just the username in a response below. How do I access the username. i can access the other data but this
[
{
"_id": "5f44d450aaa72313549d519f",
"imageTitle": "uuuuu",
"imageDesc": "uuuuu",
"imageControl": "public",
"imageUrl": "http://localhost:3000/uploads/image-1598346320523.jpg",
"userId": {
"local": {
"username": "uuuuu"
},
"_id": "5f44d3e9aaa72313549d519e"
},
"uploadedOn": "2020-08-25T09:05:20.840Z",
"__v": 0
},
{ ....}
]
You can do it like so
var arr = JSON.parse(`
[ { "_id": "5f44d450aaa72313549d519f", "imageTitle": "uuuuu", "imageDesc": "uuuuu", "imageControl": "public", "imageUrl": "http://localhost:3000/uploads/image-1598346320523.jpg", "userId": { "local": { "username": "uuuuu" }, "_id": "5f44d3e9aaa72313549d519e" }, "uploadedOn": "2020-08-25T09:05:20.840Z", "__v": 0 },
{}
]`);
console.log(arr[0].userId.local.username);
If you are on a newer version of node (14.0.0, and up), then I recommend using the optional chaining operator
arr[0].userId?.local?.username

Map/reduce in CouchDB take a field if id contains a subString

I'll explain: I have this function
function (doc) {
if(doc.MovieId == "1721")
emit(doc.Rating, 1);
}
but it return me some document that are not relevant (for example they haven't the Rating field). My document _id is composed of partitionName:id, so I thought to do if(doc.MovieId == "1721" && doc._id.contains("ratings"){...} but it doesn't work.
Is there a way to do this?
-----EDIT 1-----
The docs in the circle are not relevant.
Do you need the schema of the JSON document?
-----EDIT 2-----
the following documents are NOT RELEVANT
1.
{
"_id": "movies : 1721",
"_rev": "1-d7e0e3c8152d6978073d280e0aef7457",
"MovieId": "1721",
"Title": "Titanic (1997)",
"Genres": [
"Drama",
"Romance"
]
}
2.
{
"_id": "tags : 1490",
"_rev": "1-14c20c9cfb3ee1964a298777f80333d5",
"MovieId": "1721",
"UserId": "474",
"Tag": "shipwreck",
"Timestamp": "1138031879"
}
3.
{
"_id": "tags : 2791",
"_rev": "1-e4d6c9573fcdae726a69d5fc6255de27",
"MovieId": "1721",
"UserId": "537",
"Tag": "romance",
"Timestamp": "1424141922"
}
documets like this are RELEVANT:
{
"_id": "ratings : 31662",
"_rev": "1-446665286337faaf51e23e40b527ec2d",
"MovieId": "1721",
"UserId": "219",
"Rating": "0.5",
"Timestamp": "1214043346"
}
Following view should just emit documents whose _id starts with "ratings :":
function (doc) {
var id_prefix = "ratings :";
if(doc._id.substr(0, id_prefix.length) === id_prefix && doc.MovieId == "1721")
emit(doc.Rating, 1);
}

How to query all documents where array field have at least one object-element with property equal 'X'?

I have some MongoDB documents like that:
{
"group": "P32666",
"order": [{
"_id": {
"$oid": "5e8e9b40e7999f6b90fd88bf"
},
"name": "Dmitriy A",
"login": "example",
"password": "example",
"email": "example",
"level": "user",
"uuid": "b6a19744-bb20-4d39-9e1e-0ca5b464f890"
}, {
"_id": {
"$oid": "5e8ea03f5a21c26b90983de4"
},
"name": "Dmitriy B",
"login": "example",
"password": "example",
"email": "example",
"level": "user",
"uuid": "556924c3-605c-44cc-8a26-d32f58222e89"
}, {
"_id": {
"$oid": "5e8ea0645a21c26b90983de5"
},
"name": "Dmitriy C",
"login": "example",
"password": "example",
"email": "example",
"level": "user",
"uuid": "aef00707-ef00-4ce9-918b-5cef17e7280b"
}]}
I'm working with Mongo in Mongoose and can't understand how to query all documents(like above) where field(array) "order" has at least one object within where field, for example, "login" is queal to "example". How can I do this?
I tried something like this:
export async function getQueues(request: Request, response: Response) {
const returningQueues = await queues.find({order: [login: request.params.login]});
response.json(returningQueues);
But TypeScript error (56 missing properties(mostly internal Mongoose's) of type "User", which I store within an array) says that's I am wrong in my thoughts.
If you just have one condition on the array field, you can simply do:
queues.find({"order.login": request.params.login})
But if you have multiple conditions, you can use $elemMatch:
queues.find({
order: { $elemMatch: { login: request.params.login, name: request.params.name } }
})

CouchDB indexes to connect the dots between documents

I have the following documents:
{ _id: "123", type: "project", worksite_id: "worksite_1" }
{ _id: "456", type: "document", project_id: "123" }
{ _id: "789", type: "signature", document_id: "456" }
My goal is to run a query and to inevitably do a filtered replication of all documents that have a connection with worksite_id: worksite_1.
Example:
Because this project has the worksite I am looking for
document has that project
signature has that document
I should be able to retrieve all of these documents if I want everything from that worksite.
Normally I would just add a worksite_id to my type:document and type:signature. However, worksite's can change in a project for various reasons.
I was wondering if there is a way to create an index or do something I am not thinking about to show these resemblances.
This feels like it is on the right path but the explanation puts documents inside other documents where I just want them to be separate.
A map function only considers one document at a time, so unless that document knows about other documents, you can't link them together. Your structure implies a three-table join in SQL terms.
With your structure, the best you can hope for is a two-request solution. You can create a view that shows signed documents only:
function (doc) {
if (doc && doc.type && doc.type === "signature" && doc.document_id) {
emit(doc.document_id, {_id: doc.document_id})
}
}
and using the same technique, link projects to documents -- but you can't get all three linked.
I think I have what you are looking for.
Here's some data:
{
"docs": [
{
"_id": "123",
"type": "project",
"code": "p001"
},
{
"_id": "1234",
"type": "worksitelog",
"documents": [
{
"timestamp": "20180921091501",
"project_id": "123",
"document_id": "457",
"signature_id": "789"
},
{
"timestamp": "20180921091502",
"project_id": "123",
"document_id": "457",
"signature_id": "791"
},
{
"timestamp": "20180921091502",
"project_id": "123",
"document_id": "458",
"signature_id": "791"
},
{
"timestamp": "20180921091502",
"project_id": "123",
"document_id": "456",
"signature_id": "790"
}
],
"worksite_id": "worksite_2"
},
{
"_id": "1235",
"type": "worksitelog",
"documents": [
{
"timestamp": "20180913101502",
"project_id": "125",
"document_id": "459",
"signature_id": "790"
}
],
"worksite_id": "worksite_1"
},
{
"_id": "124",
"type": "project",
"code": "p002"
},
{
"_id": "125",
"type": "project",
"code": "p003"
},
{
"_id": "456",
"type": "document",
"code": "d001",
"project_id": "123",
"worksite_id": "worksite_2"
},
{
"_id": "457",
"type": "document",
"code": "d002",
"project_id": "123",
"worksite_id": "worksite_2"
},
{
"_id": "458",
"type": "document",
"code": "d003",
"project_id": "123",
"worksite_id": "worksite_2"
},
{
"_id": "459",
"type": "document",
"code": "d001",
"project_id": "125",
"worksite_id": "worksite_1"
},
{
"_id": "789",
"type": "signature",
"user": "alice",
"pubkey": "65ab64c64ed64ef41a1bvc7d1b",
"code": "s001"
},
{
"_id": "790",
"type": "signature",
"user": "carol",
"pubkey": "tlmg90834kmn90845kjndf98734",
"code": "s002"
},
{
"_id": "791",
"type": "signature",
"user": "bob",
"pubkey": "asdf654asdf6854awer654awer654eqr654wra6354f",
"code": "s003"
},
{
"_id": "_design/projDocs",
"views": {
"docsPerWorkSite": {
"map": "function (doc) {\n if (doc.type && ['worksitelog', 'document', 'project', 'signature'].indexOf(doc.type) > -1) {\n if (doc.type == 'worksitelog') {\n emit([doc.worksite_id, 0], null);\n for (var i in doc.documents) {\n emit([doc.worksite_id, Number(i)+1, 'p'], {_id: doc.documents[i].project_id});\n emit([doc.worksite_id, Number(i)+1, 'd'], {_id: doc.documents[i].document_id});\n emit([doc.worksite_id, Number(i)+1, 's'], {_id: doc.documents[i].signature_id});\n }\n }\n }\n}"
}
},
"language": "javascript"
}
]
}
Save that data to disk as stackoverflow_53752001.json.
Use Fauxton to create a database called stackoverflow_53752001.
Here's a bash script to load the data from the file stackoverflow_53752001.json into the databasestackoverflow_53752001`. You'll need to edit the first three parameters, obviously. Fix it, then paste it into a (Unix) terminal window:
USRID="you";
USRPWD="yourpwd";
HOST="yourdb.yourpublic.work";
COUCH_DATABASE="stackoverflow_53752001";
FILE="stackoverflow_53752001.json";
#
COUCH_URL="https://${USRID}:${USRPWD}#${HOST}";
FULL_URL="${COUCH_URL}/${COUCH_DATABASE}";
curl -H 'Content-type: application/json' -X POST "${FULL_URL}/_bulk_docs" -d #${FILE};
In Fauxton, select database stackoverflow_53752001 and then, in the left-hand menu select "Design Documents" >> "projDocs" >> "Views" >> "docsPerWorkSite".
You'll see data like this:
{"total_rows":17,"offset":0,"rows":[
{"id":"1235","key":["worksite_1",0],"value":null},
{"id":"1235","key":["worksite_1",1,"d"],"value":{"_id":"459"}},
: :
: :
{"id":"1234","key":["worksite_2",4,"p"],"value":{"_id":"123"}},
{"id":"1234","key":["worksite_2",4,"s"],"value":{"_id":"790"}}
]}
If you then click on the "Options" button, in the top right, you'll get an option sheet for modifying the raw query. Pick:
"Include Docs"
"Between Keys"
"Start key" : ["worksite_1", 0]
"End key" : ["worksite_1", 9999]
Hit "Run Query", and you should see:
{"total_rows":17,"offset":0,"rows":[
{"id":"1235","key":["worksite_1",0],"value":null,"doc":{"_id":"1235","_rev":"1-de2b919591c70f643ce1005c18da1c54","type":"worksitelog","documents":[{"timestamp":"20180913101502","project_id":"125","document_id":"459","signature_id":"790"}],"worksite_id":"worksite_1"}},
{"id":"1235","key":["worksite_1",1,"d"],"value":{"_id":"459"},"doc":{"_id":"459","_rev":"1-5422628e475bab0c14e5722a1340f561","type":"document","code":"d001","project_id":"125","worksite_id":"worksite_1"}},
{"id":"1235","key":["worksite_1",1,"p"],"value":{"_id":"125"},"doc":{"_id":"125","_rev":"1-312dd8a9dd432168d8608b7cd9eb92cd","type":"project","code":"p003"}},
{"id":"1235","key":["worksite_1",1,"s"],"value":{"_id":"790"},"doc":{"_id":"790","_rev":"1-be018df4ecdf2e6add68a2758b9bd12a","type":"signature","user":"carol","pubkey":"tlmg90834kmn90845kjndf98734","code":"s002"}}
]}
If you then change the start and end keys to ["worksite_2", 0] and ["worksite_2", 9999] you will see the data for the second work site.
For this to work, each time you have written a new document and signature to the database, you'll need to:
prepare an object {
"timestamp": "20180921091502",
"project_id": "123",
"document_id": "457",
"signature_id": "791"
}
get the corresponding work site log record
append the object to the documents array
put back the altered work site log record
I assumed there are multiple signatures per document, so you'll have to write a log record for each and every one of them. If that grows too big you can change worksite_id to something like worksite_1_201812, which would give one log per work site per month with out breaking the query logic, I think.

How to Write search Query for Couchdb in views where the search object is in array?

I have a documents having the following data.
Document 1.
{
"_id": "7d7db310ff3acc857c7f301f67979de5",
"_rev": "1-3ed97634540c35292155ad40b99cafc1",
"categories": [
"Computer",
"Mobile",
"Automobile",
"Plumbing"
],
"location": [
"19.374636239520235",
"72.82339096069336"
],
"gender": "male",
"username": "ayushcshah",
"password": "sokdncx76483ynxwhakdkxfka37",
"email": "ayushcshah#gmail.com"
}
Document 2.
{
"_id": "8c499253bce69b992ebe795906fac3d3",
"_rev": "1-ce394be6ab3c79111344f026e1a3adcf",
"categories": [
"Automobile"
],
"location": [
"19.387757921807914",
"72.82626360654831"
],
"gender": "male",
"username": "smithabc",
"password": "adsadgq36eygdas2ygduabhs",
"email": "smithabc#gmail.com"
}
I need a VIEW where I would send key as any string and it would find a document where categories array contain the search string and it would send me related user's email address in value.
Example:
If I send key as "Mobile" the I would get respected username as value "ayushcshah#gmail.com"
key":"Mobile","value":"ayushcshah#gmail.com"
key":"Computer","value":"ayushcshah#gmail.com"
key":"Automobile","value":"ayushcshah#gmail.com"
key":"Automobile","value":"smithabc#gmail.com"
key":"Plumbing","value":"ayushcshah#gmail.com"
Following map function should be enough:
function (doc) {
if (!doc.categories) return;
for (var c in doc.categories) {
emit(doc.categories[c], doc.email);
}
}

Resources