blob: bca5b236f1465eec827a83bf719d084e0b77b71d [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define CFISH_USE_SHORT_NAMES
#define LUCY_USE_SHORT_NAMES
#define C_LUCY_SIMPLE
#include "Lucy/Simple.h"
#include "Clownfish/Err.h"
#include "Clownfish/Hash.h"
#include "Clownfish/HashIterator.h"
#include "Clownfish/String.h"
#include "Clownfish/Vector.h"
#include "Lucy/Analysis/EasyAnalyzer.h"
#include "Lucy/Document/Doc.h"
#include "Lucy/Document/HitDoc.h"
#include "Lucy/Index/Indexer.h"
#include "Lucy/Index/PolyReader.h"
#include "Lucy/Plan/FullTextType.h"
#include "Lucy/Plan/Schema.h"
#include "Lucy/Search/Hits.h"
#include "Lucy/Search/IndexSearcher.h"
#include "Lucy/Search/SortSpec.h"
Simple*
Simple_new(Obj *index, String *language) {
Simple *self = (Simple*)Class_Make_Obj(SIMPLE);
return Simple_init(self, index, language);
}
Simple*
Simple_init(Simple *self, Obj *index, String *language) {
SimpleIVARS *const ivars = Simple_IVARS(self);
ivars->index = INCREF(index);
ivars->language = Str_Clone(language);
return self;
}
void
Simple_Destroy_IMP(Simple *self) {
SimpleIVARS *const ivars = Simple_IVARS(self);
Simple_Finish_Indexing(self);
DECREF(ivars->index);
DECREF(ivars->language);
DECREF(ivars->schema);
DECREF(ivars->type);
DECREF(ivars->indexer);
DECREF(ivars->searcher);
DECREF(ivars->hits);
SUPER_DESTROY(self, SIMPLE);
}
static void
S_create_indexer(Simple *self) {
SimpleIVARS *const ivars = Simple_IVARS(self);
// Trigger searcher refresh.
DECREF(ivars->searcher);
DECREF(ivars->hits);
ivars->searcher = NULL;
ivars->hits = NULL;
// Get type and schema
Schema *schema = NULL;
FieldType *type = NULL;
PolyReader *reader = PolyReader_open(ivars->index, NULL, NULL);
Vector *seg_readers = PolyReader_Get_Seg_Readers(reader);
if (Vec_Get_Size(seg_readers) == 0) {
// Index is empty, create new schema and type.
schema = Schema_new();
EasyAnalyzer *analyzer = EasyAnalyzer_new(ivars->language);
type = (FieldType*)FullTextType_new((Analyzer*)analyzer);
DECREF(analyzer);
}
else {
// Get schema from reader.
schema = (Schema*)INCREF(PolyReader_Get_Schema(reader));
Vector *fields = Schema_All_Fields(schema);
String *field = (String*)CERTIFY(Vec_Fetch(fields, 0), STRING);
type = (FieldType*)INCREF(Schema_Fetch_Type(schema, field));
DECREF(fields);
}
ivars->indexer = Indexer_new(schema, ivars->index, NULL, 0);
ivars->schema = schema;
ivars->type = type;
DECREF(reader);
}
void
Simple_Add_Doc_IMP(Simple *self, Doc *doc) {
SimpleIVARS *const ivars = Simple_IVARS(self);
if (!ivars->indexer) {
S_create_indexer(self);
}
Vector *field_names = Doc_Field_Names(doc);
for (size_t i = 0, max = Vec_Get_Size(field_names); i < max; i++) {
String *field = (String*)Vec_Fetch(field_names, i);
Schema_Spec_Field(ivars->schema, field, ivars->type);
}
Indexer_Add_Doc(ivars->indexer, doc, 1.0);
DECREF(field_names);
}
uint32_t
Simple_Search_IMP(Simple *self, String *query, uint32_t offset,
uint32_t num_wanted, SortSpec *sort_spec) {
SimpleIVARS *const ivars = Simple_IVARS(self);
// Flush recent adds; lazily create searcher.
Simple_Finish_Indexing(self);
if (!ivars->searcher) {
ivars->searcher = IxSearcher_new(ivars->index);
}
DECREF(ivars->hits);
ivars->hits = IxSearcher_Hits(ivars->searcher, (Obj*)query, offset,
num_wanted, sort_spec);
return Hits_Total_Hits(ivars->hits);
}
HitDoc*
Simple_Next_IMP(Simple *self) {
SimpleIVARS *const ivars = Simple_IVARS(self);
if (!ivars->hits) { return NULL; }
// Get the hit, bail if hits are exhausted.
HitDoc *doc = Hits_Next(ivars->hits);
if (!doc) {
DECREF(ivars->hits);
ivars->hits = NULL;
}
return doc;
}
Indexer*
Simple_Get_Indexer_IMP(Simple *self) {
SimpleIVARS *const ivars = Simple_IVARS(self);
if (!ivars->indexer) {
S_create_indexer(self);
}
return ivars->indexer;
}
Hits*
Simple_Get_Hits_IMP(Simple *self) {
return Simple_IVARS(self)->hits;
}
void
Simple_Finish_Indexing_IMP(Simple *self) {
SimpleIVARS *const ivars = Simple_IVARS(self);
// Don't bother to throw an error if index not modified.
if (ivars->indexer) {
Indexer_Commit(ivars->indexer);
// Trigger searcher and indexer refresh.
DECREF(ivars->schema);
DECREF(ivars->type);
DECREF(ivars->indexer);
ivars->schema = NULL;
ivars->type = NULL;
ivars->indexer = NULL;
}
}